From 27cbf19cc0fae33f1038e0ba27e5bf3a00f68ea9 Mon Sep 17 00:00:00 2001 From: ericwu <51363667+scplsy@users.noreply.github.com> Date: Mon, 27 May 2024 14:55:02 +0800 Subject: [PATCH 1/8] =?UTF-8?q?fix:=20=E5=88=86=E5=9D=97=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E6=96=87=E4=BB=B6=E5=BA=8F=E5=8F=B7=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=E4=B8=8D=E6=AD=A3=E7=A1=AE=20#2173?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tencent/bkrepo/common/storage/core/FileBlockSupport.kt | 5 +++-- .../common/storage/core/operation/FileBlockOperation.kt | 2 +- .../com/tencent/bkrepo/generic/service/UploadService.kt | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/FileBlockSupport.kt b/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/FileBlockSupport.kt index fb8d124b91..0864c24087 100644 --- a/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/FileBlockSupport.kt +++ b/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/FileBlockSupport.kt @@ -202,7 +202,7 @@ abstract class FileBlockSupport : CleanupSupport() { } } - override fun listBlock(blockId: String, storageCredentials: StorageCredentials?): List> { + override fun listBlock(blockId: String, storageCredentials: StorageCredentials?): List> { val credentials = getCredentialsOrDefault(storageCredentials) val tempClient = getTempClient(credentials) try { @@ -213,7 +213,8 @@ abstract class FileBlockSupport : CleanupSupport() { val size = it.length() val name = it.name.replace(BLOCK_SUFFIX, SHA256_SUFFIX) val sha256 = tempClient.load(blockId, name)?.readText().orEmpty() - Pair(size, sha256) + val sequence = it.name.removeSuffix(BLOCK_SUFFIX).toInt() + Triple(size, sha256, sequence) } } catch (exception: Exception) { logger.error("Failed to list block [$blockId] on [${credentials.key}]", exception) diff --git a/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/operation/FileBlockOperation.kt b/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/operation/FileBlockOperation.kt index f4fb936e1e..940003e352 100644 --- a/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/operation/FileBlockOperation.kt +++ b/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/operation/FileBlockOperation.kt @@ -89,7 +89,7 @@ interface FileBlockOperation { * 列出分块文件 * blockId: 分块存储id */ - fun listBlock(blockId: String, storageCredentials: StorageCredentials?): List> + fun listBlock(blockId: String, storageCredentials: StorageCredentials?): List> /** * 存储分块文件 diff --git a/src/backend/generic/biz-generic/src/main/kotlin/com/tencent/bkrepo/generic/service/UploadService.kt b/src/backend/generic/biz-generic/src/main/kotlin/com/tencent/bkrepo/generic/service/UploadService.kt index 4bf8381cc4..91befeb5da 100644 --- a/src/backend/generic/biz-generic/src/main/kotlin/com/tencent/bkrepo/generic/service/UploadService.kt +++ b/src/backend/generic/biz-generic/src/main/kotlin/com/tencent/bkrepo/generic/service/UploadService.kt @@ -137,8 +137,8 @@ class UploadService( checkUploadId(uploadId, storageCredentials) val blockInfoList = storageService.listBlock(uploadId, storageCredentials) - return blockInfoList.mapIndexed { index, it -> - BlockInfo(size = it.first, sequence = index + 1, sha256 = it.second) + return blockInfoList.map { + BlockInfo(size = it.first, sha256 = it.second, sequence = it.third) } } From 52d74a16d856104437c55a369ce0791f99ec4fe9 Mon Sep 17 00:00:00 2001 From: kunlongli <16629885+cnlkl@users.noreply.github.com> Date: Mon, 27 May 2024 15:13:31 +0800 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=B0=8F?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E8=BF=81=E7=A7=BB=E7=BA=BF=E7=A8=8B=E6=B1=A0?= =?UTF-8?q?=20#2190?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/MigrateRepoStorageProperties.kt | 17 +++++++ .../job/migrate/executor/CorrectExecutor.kt | 2 +- .../job/migrate/executor/MigrateExecutor.kt | 2 +- .../executor/MigrateFailedNodeExecutor.kt | 2 +- .../job/migrate/utils/TransferDataExecutor.kt | 44 ++++++++++++++----- 5 files changed, 54 insertions(+), 13 deletions(-) diff --git a/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/config/MigrateRepoStorageProperties.kt b/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/config/MigrateRepoStorageProperties.kt index 68b5d4eb66..6f6e99a655 100644 --- a/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/config/MigrateRepoStorageProperties.kt +++ b/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/config/MigrateRepoStorageProperties.kt @@ -28,6 +28,7 @@ package com.tencent.bkrepo.job.migrate.config import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.util.unit.DataSize import java.time.Duration @ConfigurationProperties("migrate") @@ -36,6 +37,22 @@ data class MigrateRepoStorageProperties( * 允许同时迁移的制品数量 */ var nodeConcurrency: Int = Runtime.getRuntime().availableProcessors() * 2, + + /** + * 允许同时迁移的小文件数量 + */ + var smallNodeConcurrentCy: Int = Runtime.getRuntime().availableProcessors() * 2, + + /** + * 小于该大小的文件属于小文件 + */ + var smallNodeThreshold: DataSize = DataSize.ofMegabytes(1L), + + /** + * 允许使用小文件迁移线程的项目,为空时所有项目可用 + */ + var smallExecutorProjects: Set = emptySet(), + /** * 更新进度间隔,指定每迁移多少个制品更新一次任务进度 */ diff --git a/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/executor/CorrectExecutor.kt b/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/executor/CorrectExecutor.kt index eb93dbedda..ba6a81a098 100644 --- a/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/executor/CorrectExecutor.kt +++ b/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/executor/CorrectExecutor.kt @@ -82,7 +82,7 @@ class CorrectExecutor( val iterator = NodeIterator(task, mongoTemplate) iterator.forEach { node -> context.incTransferringCount() - transferDataExecutor.execute { + transferDataExecutor.execute(node) { try { correctNode(context, node) } catch (e: Exception) { diff --git a/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/executor/MigrateExecutor.kt b/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/executor/MigrateExecutor.kt index 4e23038300..d974e6ecfe 100644 --- a/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/executor/MigrateExecutor.kt +++ b/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/executor/MigrateExecutor.kt @@ -100,7 +100,7 @@ class MigrateExecutor( logger.info("submit node[${node.fullPath}] to thread pool, task[$projectId/$repoName]") val taskNumber = ++migratedCount context.incTransferringCount() - transferDataExecutor.execute { + transferDataExecutor.execute(node) { try { logger.info("migrate node[${node.fullPath}] start, task[$projectId/$repoName]") // 迁移制品 diff --git a/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/executor/MigrateFailedNodeExecutor.kt b/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/executor/MigrateFailedNodeExecutor.kt index c0be56a71f..330f15e83c 100644 --- a/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/executor/MigrateFailedNodeExecutor.kt +++ b/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/executor/MigrateFailedNodeExecutor.kt @@ -83,7 +83,7 @@ class MigrateFailedNodeExecutor( val failedNode = migrateFailedNodeDao.findOneToRetry(projectId, repoName) ?: break val node = convert(failedNode) context.incTransferringCount() - transferDataExecutor.execute { + transferDataExecutor.execute(node) { try { correctNode(context, node) logger.info("migrate failed node[${node.fullPath}] success, task[${projectId}/${repoName}]") diff --git a/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/utils/TransferDataExecutor.kt b/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/utils/TransferDataExecutor.kt index 92af074659..ba0121a9f1 100644 --- a/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/utils/TransferDataExecutor.kt +++ b/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/utils/TransferDataExecutor.kt @@ -29,6 +29,7 @@ package com.tencent.bkrepo.job.migrate.utils import com.google.common.util.concurrent.ThreadFactoryBuilder import com.tencent.bkrepo.job.migrate.config.MigrateRepoStorageProperties +import com.tencent.bkrepo.job.migrate.pojo.Node import org.springframework.stereotype.Component import java.util.concurrent.SynchronousQueue import java.util.concurrent.ThreadPoolExecutor @@ -43,16 +44,39 @@ class TransferDataExecutor( * 实际执行数据迁移的线程池 */ private val transferDataExecutor: ThreadPoolExecutor by lazy { - ThreadPoolExecutor( - properties.nodeConcurrency, - properties.nodeConcurrency, - 0L, - TimeUnit.MILLISECONDS, - SynchronousQueue(), - ThreadFactoryBuilder().setNameFormat("transfer-data-%d").build(), - ThreadPoolExecutor.CallerRunsPolicy() - ) + buildExecutor(properties.nodeConcurrency, "transfer-data") } - fun execute(r: Runnable) = transferDataExecutor.execute(r) + /** + * 执行小文件迁移的线程池 + */ + private val transferSmallDataExecutor: ThreadPoolExecutor by lazy { + buildExecutor(properties.smallNodeConcurrentCy, "transfer-data-small") + } + + /** + * 执行数据迁移 + * + * @param node 待迁移的node + * @param r 迁移动作 + */ + fun execute(node: Node, r: Runnable) { + val smallExecutorProjects = properties.smallExecutorProjects + val isSmallExecutorProject = smallExecutorProjects.isEmpty() || node.projectId in smallExecutorProjects + if (isSmallExecutorProject && node.size < properties.smallNodeThreshold.toBytes()) { + transferSmallDataExecutor.execute(r) + } else { + transferDataExecutor.execute(r) + } + } + + private fun buildExecutor(size: Int, prefix: String) = ThreadPoolExecutor( + size, + size, + 0L, + TimeUnit.MILLISECONDS, + SynchronousQueue(), + ThreadFactoryBuilder().setNameFormat("$prefix-%d").build(), + ThreadPoolExecutor.CallerRunsPolicy() + ) } From 32af7dfa75513f4b5e58be6b6f6f4c1c04a3de23 Mon Sep 17 00:00:00 2001 From: kunlongli <16629885+cnlkl@users.noreply.github.com> Date: Mon, 27 May 2024 19:58:02 +0800 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20=E4=BF=AE=E5=A4=8DsmallNodeConcurre?= =?UTF-8?q?nCy=E9=85=8D=E7=BD=AE=E5=90=8D=E7=A7=B0=E9=94=99=E8=AF=AF=20#21?= =?UTF-8?q?90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 修复smallNodeConcurrenCy配置名称错误 #2190 * feat: 修复smallNodeConcurrency配置名称错误 #2190 --- .../bkrepo/job/migrate/config/MigrateRepoStorageProperties.kt | 2 +- .../tencent/bkrepo/job/migrate/utils/TransferDataExecutor.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/config/MigrateRepoStorageProperties.kt b/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/config/MigrateRepoStorageProperties.kt index 6f6e99a655..4cfcf69b12 100644 --- a/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/config/MigrateRepoStorageProperties.kt +++ b/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/config/MigrateRepoStorageProperties.kt @@ -41,7 +41,7 @@ data class MigrateRepoStorageProperties( /** * 允许同时迁移的小文件数量 */ - var smallNodeConcurrentCy: Int = Runtime.getRuntime().availableProcessors() * 2, + var smallNodeConcurrency: Int = Runtime.getRuntime().availableProcessors() * 2, /** * 小于该大小的文件属于小文件 diff --git a/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/utils/TransferDataExecutor.kt b/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/utils/TransferDataExecutor.kt index ba0121a9f1..15205160f5 100644 --- a/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/utils/TransferDataExecutor.kt +++ b/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/utils/TransferDataExecutor.kt @@ -51,7 +51,7 @@ class TransferDataExecutor( * 执行小文件迁移的线程池 */ private val transferSmallDataExecutor: ThreadPoolExecutor by lazy { - buildExecutor(properties.smallNodeConcurrentCy, "transfer-data-small") + buildExecutor(properties.smallNodeConcurrency, "transfer-data-small") } /** From b41498a104bb40383e1ec372ad4859a846961bbf Mon Sep 17 00:00:00 2001 From: kunlongli <16629885+cnlkl@users.noreply.github.com> Date: Mon, 27 May 2024 19:58:34 +0800 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20=E6=8C=87=E5=AE=9A=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E9=A6=96=E4=B8=AA=E5=BE=85=E8=BF=81=E7=A7=BB=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E6=97=B6=E4=BD=BF=E7=94=A8PATH=5FIDX=E7=B4=A2?= =?UTF-8?q?=E5=BC=95=20#2162?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 指定查询首个待迁移节点时使用PATH_IDX索引 #2162 * feat: 修复单元测试错误 #2162 --- .../bkrepo/job/migrate/utils/NodeIterator.kt | 3 ++- .../bkrepo/job/migrate/utils/MigrateTestUtils.kt | 8 +++++++- .../bkrepo/job/migrate/utils/NodeIteratorTest.kt | 2 ++ .../kotlin/com/tencent/bkrepo/job/model/TNode.kt | 13 ++++++++++++- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/utils/NodeIterator.kt b/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/utils/NodeIterator.kt index ddd375b50c..8cff5832b0 100644 --- a/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/utils/NodeIterator.kt +++ b/src/backend/job/biz-job/src/main/kotlin/com/tencent/bkrepo/job/migrate/utils/NodeIterator.kt @@ -144,7 +144,7 @@ class NodeIterator( private fun initLastNodeId(): String { return if (task.lastMigratedNodeId == MIN_OBJECT_ID) { - val query = Query(buildCriteria()).with(Sort.by(Sort.Direction.ASC, ID)) + val query = Query(buildCriteria()).with(Sort.by(Sort.Direction.ASC, ID)).withHint(PATH_IDX) // 找到第一个node mongoTemplate.findOne(query, Node::class.java, collectionName)?.id?.let { // 获取一个比其小的id @@ -157,5 +157,6 @@ class NodeIterator( companion object { private val logger = LoggerFactory.getLogger(NodeIterator::class.java) + private const val PATH_IDX = "projectId_repoName_path_idx" } } diff --git a/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/migrate/utils/MigrateTestUtils.kt b/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/migrate/utils/MigrateTestUtils.kt index 96a472ffe6..41c3ed1eb7 100644 --- a/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/migrate/utils/MigrateTestUtils.kt +++ b/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/migrate/utils/MigrateTestUtils.kt @@ -131,7 +131,13 @@ object MigrateTestUtils { compressed = compressed, ) val sharding = HashShardingUtils.shardingSequenceFor(UT_PROJECT_ID, SHARDING_COUNT) - return insert(node, "node_$sharding") + val collectionName = "node_$sharding" + ensureNodeIndex(collectionName) + return insert(node, collectionName) + } + + fun MongoTemplate.ensureNodeIndex(collectionName: String) { + indexOps(collectionName).ensureIndex(TNode.pathIndex()) } fun MongoTemplate.removeNodes() { diff --git a/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/migrate/utils/NodeIteratorTest.kt b/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/migrate/utils/NodeIteratorTest.kt index 037823141b..ba3141b638 100644 --- a/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/migrate/utils/NodeIteratorTest.kt +++ b/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/migrate/utils/NodeIteratorTest.kt @@ -35,6 +35,7 @@ import com.tencent.bkrepo.job.UT_SHA256 import com.tencent.bkrepo.job.migrate.pojo.MigrateRepoStorageTask import com.tencent.bkrepo.job.migrate.pojo.MigrateRepoStorageTaskState import com.tencent.bkrepo.job.migrate.utils.MigrateTestUtils.buildTask +import com.tencent.bkrepo.job.migrate.utils.MigrateTestUtils.ensureNodeIndex import com.tencent.bkrepo.job.model.TNode import org.bson.types.ObjectId import org.junit.jupiter.api.Assertions.assertEquals @@ -68,6 +69,7 @@ class NodeIteratorTest @Autowired constructor( @BeforeAll fun beforeAll() { + mongoTemplate.ensureNodeIndex(collectionName) nodes = createNodes(startDate) } diff --git a/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/model/TNode.kt b/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/model/TNode.kt index 245e6d3f7b..de2243423c 100644 --- a/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/model/TNode.kt +++ b/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/model/TNode.kt @@ -27,6 +27,7 @@ package com.tencent.bkrepo.job.model +import org.springframework.data.mongodb.core.index.TextIndexDefinition.TextIndexDefinitionBuilder import java.time.LocalDateTime data class TNode( @@ -42,4 +43,14 @@ data class TNode( val deleted: LocalDateTime? = null, val archived: Boolean? = null, val compressed: Boolean? = null, -) +) { + companion object { + fun pathIndex() = TextIndexDefinitionBuilder() + .onField("projectId") + .onField("repoName") + .onField("path") + .onField("deleted") + .named("projectId_repoName_path_idx") + .build() + } +} From e48226dd7853fd251c8d2db6d13612205fef0cdc Mon Sep 17 00:00:00 2001 From: kunlongli <16629885+cnlkl@users.noreply.github.com> Date: Mon, 27 May 2024 22:00:47 +0800 Subject: [PATCH 5/8] =?UTF-8?q?bug:=20=E4=BF=AE=E5=A4=8DStorageManager?= =?UTF-8?q?=E5=88=9B=E5=BB=BANode=E8=B6=85=E6=97=B6=E5=90=8E=E8=AF=AF?= =?UTF-8?q?=E5=88=A0=E5=AD=98=E5=82=A8=20#2133?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 记录存储中的文件用于存储失败回滚 #2133 * feat: 记录存储中的文件用于存储失败回滚 #2133 * feat: 支持删除制品的同时删除staging文件 #2133 * feat: 增加存储回滚定时任务 #2133 * feat: 增加存储回滚定时任务 #2133 * feat: 移除创建node超时回滚操作 #2133 * feat: 增加存储回滚定时任务 #2133 * feat: 增加存储回滚定时任务 #2133 * feat: 增加存储回滚定时任务 #2133 * feat: 使用findAndModify创建引用,增加单元测试 #2133 * feat: 增加存储回滚任务单元测试 #2133 * feat: 修复代码检查错误 #2133 * feat: 移除FileReference创建方法 #2133 * feat: 移除FileReference创建方法 #2133 * feat: 恢复NODE_CREATE_TIMEOUT #2133 * feat: 移除存储回滚相关代码 #2133 * feat: 移除存储回滚相关代码 #2133 * feat: 移除存储回滚相关代码 #2133 * feat: 增加StorageManager单元测试 #2133 --- .../resources/i18n/messages_en.properties | 1 - .../resources/i18n/messages_zh_CN.properties | 1 - .../resources/i18n/messages_zh_TW.properties | 1 - .../common/artifact/manager/StorageManager.kt | 19 +- .../artifact/manager/StorageManagerTest.kt | 166 ++++++++++++++++++ .../bkrepo/common/artifact/util/Constant.kt | 35 ++++ .../storage-service/build.gradle.kts | 1 + .../storage/core/AbstractStorageService.kt | 4 +- .../storage/core/AbstractStorageSupport.kt | 2 - .../common/storage/core/StorageService.kt | 2 - .../storage/core/cache/CacheStorageService.kt | 13 +- .../core/simple/SimpleStorageService.kt | 2 - .../core/cache/CacheStorageServiceTest.kt | 14 +- .../job/batch/FileReferenceCleanupJobTest.kt | 16 +- .../tencent/bkrepo/job/batch/JobBaseTest.kt | 20 ++- .../job/migrate/executor/ExecutorBaseTest.kt | 2 +- .../repository/api/FileReferenceClient.kt | 9 +- .../repository/config/RepositoryProperties.kt | 1 - .../service/FileReferenceController.kt | 8 +- .../service/file/FileReferenceService.kt | 12 +- .../file/impl/FileReferenceServiceImpl.kt | 8 +- .../service/node/impl/NodeBaseService.kt | 89 +--------- .../service/FileReferenceServiceTest.kt | 2 + .../repository/service/NodeServiceTest.kt | 52 ------ 24 files changed, 276 insertions(+), 204 deletions(-) create mode 100644 src/backend/common/common-artifact/artifact-service/src/test/kotlin/com/tencent/bkrepo/common/artifact/manager/StorageManagerTest.kt create mode 100644 src/backend/common/common-artifact/artifact-service/src/test/kotlin/com/tencent/bkrepo/common/artifact/util/Constant.kt diff --git a/src/backend/common/common-artifact/artifact-api/src/main/resources/i18n/messages_en.properties b/src/backend/common/common-artifact/artifact-api/src/main/resources/i18n/messages_en.properties index 17db696628..eaae6f4896 100644 --- a/src/backend/common/common-artifact/artifact-api/src/main/resources/i18n/messages_en.properties +++ b/src/backend/common/common-artifact/artifact-api/src/main/resources/i18n/messages_en.properties @@ -43,7 +43,6 @@ artifact.node.not-found=Node [{0}] not found artifact.node.path.invalid=Invalid node path [{0}] artifact.node.existed=Node [{0}] existed artifact.node.conflict=Node [{0}] conflict -artifact.node.create.timeout=Node [{0}] create timeout artifact.node.list.too-large=Node list count too large artifact.node.link-folder-unsupported=Link folder[{0}] was unsupported artifact.stage.upgrade.error=Upgrade artifact stage error: [{0}] diff --git a/src/backend/common/common-artifact/artifact-api/src/main/resources/i18n/messages_zh_CN.properties b/src/backend/common/common-artifact/artifact-api/src/main/resources/i18n/messages_zh_CN.properties index 42288446a4..8581770312 100644 --- a/src/backend/common/common-artifact/artifact-api/src/main/resources/i18n/messages_zh_CN.properties +++ b/src/backend/common/common-artifact/artifact-api/src/main/resources/i18n/messages_zh_CN.properties @@ -40,7 +40,6 @@ artifact.node.not-found=节点[{0}]不存在 artifact.node.path.invalid=节点路径[{0}]无效 artifact.node.existed=节点[{0}]已存在 artifact.node.conflict=已存在同名文件,且不允许覆盖 -artifact.node.create.timeout=节点[{0}]创建超时 artifact.node.list.too-large=节点列表数量过大 artifact.node.link-folder-unsupported=无法链接到目录[{0}] artifact.stage.upgrade.error=制品晋级失败: {0} diff --git a/src/backend/common/common-artifact/artifact-api/src/main/resources/i18n/messages_zh_TW.properties b/src/backend/common/common-artifact/artifact-api/src/main/resources/i18n/messages_zh_TW.properties index 9053e9f34c..a5a19c3ef1 100644 --- a/src/backend/common/common-artifact/artifact-api/src/main/resources/i18n/messages_zh_TW.properties +++ b/src/backend/common/common-artifact/artifact-api/src/main/resources/i18n/messages_zh_TW.properties @@ -40,7 +40,6 @@ artifact.node.not-found=節點[{0}]不存在 artifact.node.path.invalid=節點路徑[{0}]無效 artifact.node.existed=節點[{0}]已存在 artifact.node.conflict=已存在同名文件,且不允許覆蓋 -artifact.node.create.timeout=節點[{0}]創建超時 artifact.node.list.too-large=節點列表數量過大 artifact.node.link-folder-unsupported=無法連結到目錄[{0}] artifact.stage.upgrade.error=制品晉級失敗: {0} diff --git a/src/backend/common/common-artifact/artifact-service/src/main/kotlin/com/tencent/bkrepo/common/artifact/manager/StorageManager.kt b/src/backend/common/common-artifact/artifact-service/src/main/kotlin/com/tencent/bkrepo/common/artifact/manager/StorageManager.kt index a144eb5cec..1d329a986a 100644 --- a/src/backend/common/common-artifact/artifact-service/src/main/kotlin/com/tencent/bkrepo/common/artifact/manager/StorageManager.kt +++ b/src/backend/common/common-artifact/artifact-service/src/main/kotlin/com/tencent/bkrepo/common/artifact/manager/StorageManager.kt @@ -40,6 +40,7 @@ import com.tencent.bkrepo.common.service.util.HttpContextHolder.getRequestOrNull import com.tencent.bkrepo.common.storage.core.StorageService import com.tencent.bkrepo.common.storage.credentials.StorageCredentials import com.tencent.bkrepo.common.storage.innercos.http.HttpMethod +import com.tencent.bkrepo.repository.api.FileReferenceClient import com.tencent.bkrepo.repository.api.NodeClient import com.tencent.bkrepo.repository.pojo.node.NodeDetail import com.tencent.bkrepo.repository.pojo.node.NodeInfo @@ -47,7 +48,6 @@ import com.tencent.bkrepo.repository.pojo.node.service.NodeCreateRequest import com.tencent.devops.plugin.api.PluginManager import com.tencent.devops.plugin.api.applyExtension import org.slf4j.LoggerFactory -import java.util.concurrent.atomic.AtomicBoolean /** * 存储管理类 @@ -66,7 +66,8 @@ import java.util.concurrent.atomic.AtomicBoolean class StorageManager( private val storageService: StorageService, private val nodeClient: NodeClient, - private val nodeResourceFactoryImpl: NodeResourceFactoryImpl, + private val fileReferenceClient: FileReferenceClient, + private val nodeResourceFactory: NodeResourceFactory, private val pluginManager: PluginManager, ) { @@ -79,18 +80,18 @@ class StorageManager( artifactFile: ArtifactFile, storageCredentials: StorageCredentials?, ): NodeDetail { - val cancel = AtomicBoolean(false) - val affectedCount = storageService.store(request.sha256!!, artifactFile, storageCredentials, cancel) + val affectedCount = storageService.store(request.sha256!!, artifactFile, storageCredentials) try { return nodeClient.createNode(request).data!! } catch (exception: Exception) { - // 当文件有创建,则删除文件 if (affectedCount == 1) { try { - cancel.set(true) - storageService.delete(request.sha256!!, storageCredentials) + // 当createNode调用超时,实际node和引用创建成功时不会做任何改变 + // 当文件创建成功,但是node创建失败时,则创建一个计数为0的fileReference用于清理任务清理垃圾文件 + fileReferenceClient.increment(request.sha256!!, storageCredentials?.key, 0L) } catch (exception: Exception) { - logger.error("Failed to delete new created file[${request.sha256}]", exception) + // 创建引用失败后会通过定时任务StorageReconcileJob清理垃圾文件 + logger.error("Failed to create ref for new created file[${request.sha256}]", exception) } } // 异常往上抛 @@ -124,7 +125,7 @@ class StorageManager( if (range.isEmpty() || request?.method == HttpMethod.HEAD.name) { return ArtifactInputStream(EmptyInputStream.INSTANCE, range) } - val nodeResource = nodeResourceFactoryImpl.getNodeResource(node, range, storageCredentials) + val nodeResource = nodeResourceFactory.getNodeResource(node, range, storageCredentials) return nodeResource.getArtifactInputStream() } diff --git a/src/backend/common/common-artifact/artifact-service/src/test/kotlin/com/tencent/bkrepo/common/artifact/manager/StorageManagerTest.kt b/src/backend/common/common-artifact/artifact-service/src/test/kotlin/com/tencent/bkrepo/common/artifact/manager/StorageManagerTest.kt new file mode 100644 index 0000000000..8dde16d32f --- /dev/null +++ b/src/backend/common/common-artifact/artifact-service/src/test/kotlin/com/tencent/bkrepo/common/artifact/manager/StorageManagerTest.kt @@ -0,0 +1,166 @@ +package com.tencent.bkrepo.common.artifact.manager + +import com.tencent.bkrepo.common.api.constant.StringPool +import com.tencent.bkrepo.common.api.pojo.Response +import com.tencent.bkrepo.common.artifact.api.ArtifactFile +import com.tencent.bkrepo.common.artifact.api.FileSystemArtifactFile +import com.tencent.bkrepo.common.artifact.util.Constant.UT_PROJECT_ID +import com.tencent.bkrepo.common.artifact.util.Constant.UT_REPO_NAME +import com.tencent.bkrepo.common.artifact.util.Constant.UT_SHA256 +import com.tencent.bkrepo.common.artifact.util.Constant.UT_USER +import com.tencent.bkrepo.common.storage.core.StorageService +import com.tencent.bkrepo.repository.api.FileReferenceClient +import com.tencent.bkrepo.repository.api.NodeClient +import com.tencent.bkrepo.repository.pojo.node.NodeDetail +import com.tencent.bkrepo.repository.pojo.node.NodeInfo +import com.tencent.bkrepo.repository.pojo.node.service.NodeCreateRequest +import com.tencent.devops.plugin.api.PluginManager +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.assertThrows +import org.mockito.ArgumentMatchers.anyString +import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.mock +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.SpringBootConfiguration +import org.springframework.boot.autoconfigure.EnableAutoConfiguration +import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest +import org.springframework.boot.test.mock.mockito.MockBean +import org.springframework.context.annotation.Import +import org.springframework.test.context.TestPropertySource +import org.springframework.util.ReflectionUtils +import java.io.File + +@DisplayName("存储管理器测试") +@DataMongoTest +@SpringBootConfiguration +@EnableAutoConfiguration +@Import(StorageManager::class) +@TestPropertySource( + locations = ["classpath:bootstrap-ut.properties"] +) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class StorageManagerTest @Autowired constructor( + private val storageManager: StorageManager, +) { + @MockBean + private lateinit var fileReferenceClient: FileReferenceClient + + @MockBean + private lateinit var nodeClient: NodeClient + + @MockBean + private lateinit var pluginManager: PluginManager + + @MockBean + private lateinit var storageService: StorageService + + @MockBean + private lateinit var nodeResourceFactory: NodeResourceFactory + + @BeforeEach + fun beforeEach() { + whenever(storageService.store(anyString(), any(), anyOrNull(), anyOrNull())) + .thenReturn(1) + whenever(nodeClient.createNode(any())) + .thenReturn(Response(code = 0, data = buildNodeDetail(UT_SHA256))) + whenever(fileReferenceClient.increment(anyString(), anyOrNull(), any())) + .thenReturn(Response(code = 0, data = true)) + } + + @Test + fun testStoreSuccess() { + store() + verify(nodeClient, times(1)).createNode(any()) + verify(fileReferenceClient, times(0)).increment(anyString(), anyOrNull(), any()) + } + + @Test + fun `test store failed`() { + // mock + val storageService = mock() + whenever(storageService.store(anyString(), any(), anyOrNull(), anyOrNull())).then { throw RuntimeException() } + val field = ReflectionUtils.findField(StorageManager::class.java, "storageService")!! + field.isAccessible = true + val oldStorageService = field.get(storageManager) + field.set(storageManager, storageService) + + // store failed + assertThrows { store() } + verify(nodeClient, times(0)).createNode(any()) + verify(fileReferenceClient, times(0)).increment(anyString(), anyOrNull(), any()) + + // reset mock + field.set(storageManager, oldStorageService) + } + + @Test + fun `test create node failed`() { + // mock + whenever(nodeClient.createNode(any())).then { throw RuntimeException() } + + // store failed + assertThrows { store() } + verify(nodeClient, times(1)).createNode(any()) + verify(fileReferenceClient, times(1)).increment(anyString(), anyOrNull(), any()) + } + + private fun store(): String { + val file = createTempArtifactFile() + val sha256 = file.getFileSha256() + val req = buildNodeCreateRequest("/a/b/c.txt", 10240L, sha256) + try { + storageManager.storeArtifactFile(req, file, null) + } finally { + file.delete() + } + return sha256 + } + + private fun createTempArtifactFile(size: Long = 10240L): ArtifactFile { + val tempFile = File.createTempFile("tmp", "") + val content = StringPool.randomString(size.toInt()) + content.byteInputStream().use { input -> + tempFile.outputStream().use { output -> + input.copyTo(output) + } + } + return FileSystemArtifactFile(tempFile) + } + + private fun buildNodeCreateRequest(fullPath: String, size: Long, sha256: String) = NodeCreateRequest( + projectId = UT_PROJECT_ID, + repoName = UT_REPO_NAME, + folder = false, + fullPath = fullPath, + expires = 0, + overwrite = false, + size = size, + sha256 = sha256, + md5 = "md5", + ) + + private fun buildNodeDetail(sha256: String): NodeDetail { + val nodeInfo = NodeInfo( + createdBy = UT_USER, + createdDate = "", + lastModifiedBy = UT_USER, + lastModifiedDate = "", + folder = false, + sha256 = sha256, + path = "/a/b", + name = "c.txt", + fullPath = "/a/b/c.txt", + size = 10240L, + projectId = UT_PROJECT_ID, + repoName = UT_REPO_NAME + ) + return NodeDetail(nodeInfo) + } +} diff --git a/src/backend/common/common-artifact/artifact-service/src/test/kotlin/com/tencent/bkrepo/common/artifact/util/Constant.kt b/src/backend/common/common-artifact/artifact-service/src/test/kotlin/com/tencent/bkrepo/common/artifact/util/Constant.kt new file mode 100644 index 0000000000..4bd9ca47af --- /dev/null +++ b/src/backend/common/common-artifact/artifact-service/src/test/kotlin/com/tencent/bkrepo/common/artifact/util/Constant.kt @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 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.bkrepo.common.artifact.util + +object Constant { + const val UT_PROJECT_ID = "ut-project" + const val UT_REPO_NAME = "ut-repo" + const val UT_USER = "system" + const val UT_SHA256 = "688787d8ff144c502c7f5cffaafe2cc588d86079f9de88304c26b0cb99ce91c6" +} diff --git a/src/backend/common/common-storage/storage-service/build.gradle.kts b/src/backend/common/common-storage/storage-service/build.gradle.kts index 762e9cd081..cda4daba3f 100644 --- a/src/backend/common/common-storage/storage-service/build.gradle.kts +++ b/src/backend/common/common-storage/storage-service/build.gradle.kts @@ -52,4 +52,5 @@ dependencies { testImplementation("it.ozimov:embedded-redis:${Versions.EmbeddedRedis}") { exclude("org.slf4j", "slf4j-simple") } + testImplementation("org.mockito.kotlin:mockito-kotlin") } diff --git a/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/AbstractStorageService.kt b/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/AbstractStorageService.kt index 2e43512507..c979060c35 100644 --- a/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/AbstractStorageService.kt +++ b/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/AbstractStorageService.kt @@ -39,7 +39,6 @@ import com.tencent.bkrepo.common.storage.filesystem.check.SynchronizeResult import com.tencent.bkrepo.common.storage.message.StorageErrorException import com.tencent.bkrepo.common.storage.message.StorageMessageCode import com.tencent.bkrepo.common.storage.monitor.Throughput -import java.util.concurrent.atomic.AtomicBoolean import org.slf4j.LoggerFactory import kotlin.system.measureNanoTime @@ -53,7 +52,6 @@ abstract class AbstractStorageService : CompressSupport() { digest: String, artifactFile: ArtifactFile, storageCredentials: StorageCredentials?, - cancel: AtomicBoolean?, storageClass: String?, ): Int { val path = fileLocator.locate(digest) @@ -65,7 +63,7 @@ abstract class AbstractStorageService : CompressSupport() { } else { val size = artifactFile.getSize() val nanoTime = measureNanoTime { - doStore(path, digest, artifactFile, credentials, cancel, storageClass) + doStore(path, digest, artifactFile, credentials, storageClass) } val throughput = Throughput(size, nanoTime) logger.info("Success to store artifact file [$digest], $throughput.") diff --git a/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/AbstractStorageSupport.kt b/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/AbstractStorageSupport.kt index 24e4013521..c2984e50ad 100644 --- a/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/AbstractStorageSupport.kt +++ b/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/AbstractStorageSupport.kt @@ -43,7 +43,6 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.ApplicationEventPublisher import java.nio.file.Path import java.nio.file.Paths -import java.util.concurrent.atomic.AtomicBoolean /** * 抽象存储服务辅助类 @@ -105,7 +104,6 @@ abstract class AbstractStorageSupport : StorageService { filename: String, artifactFile: ArtifactFile, credentials: StorageCredentials, - cancel: AtomicBoolean? = null, storageClass: String? = null, ) diff --git a/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/StorageService.kt b/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/StorageService.kt index 1aef49126e..75cad09373 100644 --- a/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/StorageService.kt +++ b/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/StorageService.kt @@ -42,7 +42,6 @@ import com.tencent.bkrepo.common.storage.core.operation.OverlayOperation import com.tencent.bkrepo.common.storage.credentials.StorageCredentials import com.tencent.bkrepo.common.storage.filesystem.check.SynchronizeResult import java.nio.file.Path -import java.util.concurrent.atomic.AtomicBoolean /** * 存储服务接口 @@ -61,7 +60,6 @@ interface StorageService : digest: String, artifactFile: ArtifactFile, storageCredentials: StorageCredentials?, - cancel: AtomicBoolean? = null, storageClass: String? = null, ): Int diff --git a/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/cache/CacheStorageService.kt b/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/cache/CacheStorageService.kt index fa614551d8..f4a8f40ea2 100644 --- a/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/cache/CacheStorageService.kt +++ b/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/cache/CacheStorageService.kt @@ -52,7 +52,6 @@ import java.io.File import java.io.FileNotFoundException import java.nio.file.Path import java.nio.file.Paths -import java.util.concurrent.atomic.AtomicBoolean /** * 支持缓存的存储服务 @@ -69,7 +68,6 @@ class CacheStorageService( filename: String, artifactFile: ArtifactFile, credentials: StorageCredentials, - cancel: AtomicBoolean?, storageClass: String?, ) { when { @@ -90,13 +88,12 @@ class CacheStorageService( else -> { val cacheFile = getCacheClient(credentials).move(path, filename, artifactFile.flushToFile()) cacheFileEventPublisher.publishCacheFileLoadedEvent(credentials, cacheFile) - async2Store(cancel, filename, credentials, path, cacheFile, storageClass) + async2Store(filename, credentials, path, cacheFile, storageClass) } } } private fun async2Store( - cancel: AtomicBoolean?, filename: String, credentials: StorageCredentials, path: String, @@ -105,16 +102,8 @@ class CacheStorageService( ) { threadPoolTaskExecutor.execute { try { - if (cancel?.get() == true) { - logger.info("Cancel store fle [$filename] on [${credentials.key}]") - return@execute - } fileStorage.store(path, filename, cacheFile, credentials, storageClass) } catch (ignored: Exception) { - if (cancel?.get() == true) { - logger.info("Cancel store fle [$filename] on [${credentials.key}]") - return@execute - } // 此处为异步上传,失败后异常不会被外层捕获,所以单独捕获打印error日志 logger.error("Failed to async store file [$filename] on [${credentials.key}]", ignored) // 失败时把文件放入暂存区,后台任务会进行补偿。 diff --git a/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/simple/SimpleStorageService.kt b/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/simple/SimpleStorageService.kt index b323211ba7..456f51517b 100644 --- a/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/simple/SimpleStorageService.kt +++ b/src/backend/common/common-storage/storage-service/src/main/kotlin/com/tencent/bkrepo/common/storage/core/simple/SimpleStorageService.kt @@ -37,7 +37,6 @@ import com.tencent.bkrepo.common.artifact.stream.Range import com.tencent.bkrepo.common.artifact.stream.artifactStream import com.tencent.bkrepo.common.storage.core.AbstractStorageService import com.tencent.bkrepo.common.storage.credentials.StorageCredentials -import java.util.concurrent.atomic.AtomicBoolean /** * 存储服务简单实现 @@ -49,7 +48,6 @@ class SimpleStorageService : AbstractStorageService() { filename: String, artifactFile: ArtifactFile, credentials: StorageCredentials, - cancel: AtomicBoolean?, storageClass: String?, ) { when { diff --git a/src/backend/common/common-storage/storage-service/src/test/kotlin/com/tencent/bkrepo/common/storage/core/cache/CacheStorageServiceTest.kt b/src/backend/common/common-storage/storage-service/src/test/kotlin/com/tencent/bkrepo/common/storage/core/cache/CacheStorageServiceTest.kt index 879dea966f..843328a4c9 100644 --- a/src/backend/common/common-storage/storage-service/src/test/kotlin/com/tencent/bkrepo/common/storage/core/cache/CacheStorageServiceTest.kt +++ b/src/backend/common/common-storage/storage-service/src/test/kotlin/com/tencent/bkrepo/common/storage/core/cache/CacheStorageServiceTest.kt @@ -48,20 +48,18 @@ import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertDoesNotThrow import org.junit.jupiter.api.extension.ExtendWith import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.ImportAutoConfiguration import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration import org.springframework.test.context.TestPropertySource import org.springframework.test.context.junit.jupiter.SpringExtension +import org.springframework.util.StreamUtils import java.io.File import java.nio.charset.Charset import java.util.concurrent.CyclicBarrier -import java.util.concurrent.atomic.AtomicBoolean import kotlin.concurrent.thread import kotlin.random.Random -import org.springframework.util.StreamUtils @ExtendWith(SpringExtension::class) @ImportAutoConfiguration(StorageAutoConfiguration::class, TaskExecutionAutoConfiguration::class) @@ -226,16 +224,6 @@ internal class CacheStorageServiceTest { Assertions.assertFalse(cacheClient.exist(path, sha256)) } - @Test - fun cancelStoreTest() { - val size = 1024L - val artifactFile = createTempArtifactFile(size) - val sha256 = artifactFile.getFileSha256() - val cancel = AtomicBoolean(false) - cancel.set(true) - assertDoesNotThrow { storageService.store(sha256, artifactFile, null, cancel) } - } - @Test fun deltaStoreTest() { val data1 = Random.nextBytes(Random.nextInt(1024, 1 shl 20)) diff --git a/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/batch/FileReferenceCleanupJobTest.kt b/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/batch/FileReferenceCleanupJobTest.kt index 12768cefb6..e218b72972 100644 --- a/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/batch/FileReferenceCleanupJobTest.kt +++ b/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/batch/FileReferenceCleanupJobTest.kt @@ -33,7 +33,6 @@ import com.tencent.bkrepo.common.artifact.pojo.RepositoryCategory import com.tencent.bkrepo.common.artifact.pojo.RepositoryType import com.tencent.bkrepo.common.artifact.pojo.configuration.local.LocalConfiguration import com.tencent.bkrepo.common.service.util.ResponseBuilder -import com.tencent.bkrepo.common.service.util.SpringContextUtils import com.tencent.bkrepo.common.storage.core.StorageService import com.tencent.bkrepo.common.storage.credentials.InnerCosCredentials import com.tencent.bkrepo.job.SHARDING_COUNT @@ -46,9 +45,6 @@ import com.tencent.bkrepo.repository.api.FileReferenceClient import com.tencent.bkrepo.repository.api.RepositoryClient import com.tencent.bkrepo.repository.api.StorageCredentialsClient import com.tencent.bkrepo.repository.pojo.repo.RepositoryDetail -import io.mockk.every -import io.mockk.mockk -import io.mockk.mockkObject import org.bson.Document import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions @@ -62,16 +58,13 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest import org.springframework.boot.test.mock.mockito.MockBean import org.springframework.data.mongodb.core.MongoTemplate -import org.springframework.data.mongodb.core.query.Query -import java.util.concurrent.atomic.AtomicInteger -import org.springframework.cloud.sleuth.Tracer -import org.springframework.cloud.sleuth.otel.bridge.OtelTracer import org.springframework.data.mongodb.core.find import org.springframework.data.mongodb.core.findOne -import org.springframework.data.mongodb.core.insert import org.springframework.data.mongodb.core.query.Criteria +import org.springframework.data.mongodb.core.query.Query import org.springframework.data.mongodb.core.query.isEqualTo import org.springframework.data.redis.core.RedisTemplate +import java.util.concurrent.atomic.AtomicInteger @DisplayName("文件引用清理Job测试") @DataMongoTest @@ -115,11 +108,6 @@ class FileReferenceCleanupJobTest : JobBaseTest() { @BeforeEach fun beforeEach() { - val tracer = mockk() - mockkObject(SpringContextUtils.Companion) - every { SpringContextUtils.getBean() } returns tracer - every { tracer.currentSpan() } returns null - every { SpringContextUtils.publishEvent(any()) } returns Unit Mockito.`when`(storageService.exist(anyString(), any())).thenReturn(true) val credentials = InnerCosCredentials() Mockito.`when`(storageCredentialsClient.findByKey(anyString())).thenReturn( diff --git a/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/batch/JobBaseTest.kt b/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/batch/JobBaseTest.kt index 4f17738243..db52dcd393 100644 --- a/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/batch/JobBaseTest.kt +++ b/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/batch/JobBaseTest.kt @@ -29,13 +29,21 @@ package com.tencent.bkrepo.job.batch import com.tencent.bkrepo.common.job.JobAutoConfiguration import com.tencent.bkrepo.common.service.cluster.ClusterProperties +import com.tencent.bkrepo.common.service.util.SpringContextUtils import com.tencent.bkrepo.common.storage.StorageAutoConfiguration import com.tencent.bkrepo.job.config.JobConfig +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkObject +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.TestInstance import org.springframework.boot.SpringBootConfiguration import org.springframework.boot.autoconfigure.EnableAutoConfiguration import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration import org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration +import org.springframework.cloud.sleuth.Tracer +import org.springframework.cloud.sleuth.otel.bridge.OtelTracer import org.springframework.context.annotation.ComponentScan import org.springframework.context.annotation.Import import org.springframework.test.context.TestPropertySource @@ -58,4 +66,14 @@ import org.springframework.test.context.TestPropertySource @ComponentScan(basePackages = ["com.tencent.bkrepo.job"]) @SpringBootConfiguration @EnableAutoConfiguration -open class JobBaseTest +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class JobBaseTest { + @BeforeAll + fun commonMock() { + val tracer = mockk() + mockkObject(SpringContextUtils.Companion) + every { SpringContextUtils.getBean() } returns tracer + every { tracer.currentSpan() } returns null + every { SpringContextUtils.publishEvent(any()) } returns Unit + } +} diff --git a/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/migrate/executor/ExecutorBaseTest.kt b/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/migrate/executor/ExecutorBaseTest.kt index e0afa71b45..574507e94d 100644 --- a/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/migrate/executor/ExecutorBaseTest.kt +++ b/src/backend/job/biz-job/src/test/kotlin/com/tencent/bkrepo/job/migrate/executor/ExecutorBaseTest.kt @@ -110,7 +110,7 @@ open class ExecutorBaseTest { protected lateinit var storageService: StorageService fun initMock() { - whenever(fileReferenceClient.increment(any(), anyOrNull())).thenReturn(Response(0, data = true)) + whenever(fileReferenceClient.increment(any(), anyOrNull(), any())).thenReturn(Response(0, data = true)) whenever(fileReferenceClient.decrement(any(), anyOrNull())).thenReturn(Response(0, data = true)) whenever(fileReferenceClient.count(anyString(), anyOrNull())).thenReturn(Response(0, data = 0L)) diff --git a/src/backend/repository/api-repository/src/main/kotlin/com/tencent/bkrepo/repository/api/FileReferenceClient.kt b/src/backend/repository/api-repository/src/main/kotlin/com/tencent/bkrepo/repository/api/FileReferenceClient.kt index acdae31880..71542374c6 100644 --- a/src/backend/repository/api-repository/src/main/kotlin/com/tencent/bkrepo/repository/api/FileReferenceClient.kt +++ b/src/backend/repository/api-repository/src/main/kotlin/com/tencent/bkrepo/repository/api/FileReferenceClient.kt @@ -16,8 +16,15 @@ interface FileReferenceClient { fun decrement(@RequestParam sha256: String, @RequestParam credentialsKey: String?): Response @PutMapping("/increment") - fun increment(@RequestParam sha256: String, @RequestParam credentialsKey: String?): Response + fun increment( + @RequestParam sha256: String, + @RequestParam credentialsKey: String?, + @RequestParam(required = false, defaultValue = "1") inc: Long = 1L + ): Response @GetMapping("/count") fun count(@RequestParam sha256: String, @RequestParam credentialsKey: String?): Response + + @GetMapping("/exists") + fun exists(@RequestParam sha256: String, @RequestParam credentialsKey: String?): Response } diff --git a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/config/RepositoryProperties.kt b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/config/RepositoryProperties.kt index 765029f37a..68e027d780 100644 --- a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/config/RepositoryProperties.kt +++ b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/config/RepositoryProperties.kt @@ -42,7 +42,6 @@ data class RepositoryProperties( var deletedNodeReserveDays: Long = 14, var defaultStorageCredentialsKey: String? = null, var listCountLimit: Long = 100000L, - var nodeCreateTimeout: Long = 10_000, var slowLogTimeThreshold: Long = 1_000, @NestedConfigurationProperty var job: RepoJobProperties = RepoJobProperties(), diff --git a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/controller/service/FileReferenceController.kt b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/controller/service/FileReferenceController.kt index 621ee38da0..20bc4f14c1 100644 --- a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/controller/service/FileReferenceController.kt +++ b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/controller/service/FileReferenceController.kt @@ -14,11 +14,15 @@ class FileReferenceController( return ResponseBuilder.success(fileReferenceService.decrement(sha256, credentialsKey)) } - override fun increment(sha256: String, credentialsKey: String?): Response { - return ResponseBuilder.success(fileReferenceService.increment(sha256, credentialsKey)) + override fun increment(sha256: String, credentialsKey: String?, inc: Long): Response { + return ResponseBuilder.success(fileReferenceService.increment(sha256, credentialsKey, inc)) } override fun count(sha256: String, credentialsKey: String?): Response { return ResponseBuilder.success(fileReferenceService.count(sha256, credentialsKey)) } + + override fun exists(sha256: String, credentialsKey: String?): Response { + return ResponseBuilder.success(fileReferenceService.exists(sha256, credentialsKey)) + } } diff --git a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/file/FileReferenceService.kt b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/file/FileReferenceService.kt index 637ee4834c..090cd8796f 100644 --- a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/file/FileReferenceService.kt +++ b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/file/FileReferenceService.kt @@ -61,7 +61,7 @@ interface FileReferenceService { * [credentialsKey]为`null`则使用默认的存储实例 * 增加引用成功则返回`true` */ - fun increment(sha256: String, credentialsKey: String?): Boolean + fun increment(sha256: String, credentialsKey: String?, inc: Long = 1L): Boolean /** * 减少文件[sha256]在存储实例[credentialsKey]上的文件数量 @@ -85,4 +85,14 @@ interface FileReferenceService { * @param sha256 所引用文件的sha256 */ fun get(credentialsKey: String?, sha256: String): FileReference + + /** + * 判断引用是否存在 + * + * @param sha256 所引用文件的sha256 + * @param credentialsKey 文件所在存储实例 + * + * @return 是否存在 + */ + fun exists(sha256: String, credentialsKey: String?): Boolean } diff --git a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/file/impl/FileReferenceServiceImpl.kt b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/file/impl/FileReferenceServiceImpl.kt index 2df53820ab..cc971cca60 100644 --- a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/file/impl/FileReferenceServiceImpl.kt +++ b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/file/impl/FileReferenceServiceImpl.kt @@ -78,9 +78,9 @@ class FileReferenceServiceImpl( } } - override fun increment(sha256: String, credentialsKey: String?): Boolean { + override fun increment(sha256: String, credentialsKey: String?, inc: Long): Boolean { val query = buildQuery(sha256, credentialsKey) - val update = Update().inc(TFileReference::count.name, 1) + val update = Update().inc(TFileReference::count.name, inc) try { fileReferenceDao.upsert(query, update) } catch (exception: DuplicateKeyException) { @@ -128,6 +128,10 @@ class FileReferenceServiceImpl( return convert(tFileReference) } + override fun exists(sha256: String, credentialsKey: String?): Boolean { + return fileReferenceDao.exists(buildQuery(sha256, credentialsKey)) + } + private fun convert(tFileReference: TFileReference): FileReference { return tFileReference.run { FileReference(sha256, credentialsKey, count) } } diff --git a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/node/impl/NodeBaseService.kt b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/node/impl/NodeBaseService.kt index 1599238a82..60ec9f5cdd 100644 --- a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/node/impl/NodeBaseService.kt +++ b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/node/impl/NodeBaseService.kt @@ -29,8 +29,8 @@ package com.tencent.bkrepo.repository.service.node.impl import com.tencent.bkrepo.auth.api.ServicePermissionClient import com.tencent.bkrepo.auth.pojo.enums.PermissionAction -import com.tencent.bkrepo.common.api.exception.BadRequestException import com.tencent.bkrepo.common.api.constant.PROXY_HEADER_NAME +import com.tencent.bkrepo.common.api.exception.BadRequestException import com.tencent.bkrepo.common.api.exception.ErrorCodeException import com.tencent.bkrepo.common.api.pojo.Page import com.tencent.bkrepo.common.api.util.Preconditions @@ -42,13 +42,12 @@ import com.tencent.bkrepo.common.artifact.message.ArtifactMessageCode import com.tencent.bkrepo.common.artifact.path.PathUtils import com.tencent.bkrepo.common.artifact.pojo.RepositoryType import com.tencent.bkrepo.common.artifact.router.RouterControllerProperties -import com.tencent.bkrepo.common.mongo.dao.AbstractMongoDao.Companion.ID import com.tencent.bkrepo.common.mongo.dao.util.Pages import com.tencent.bkrepo.common.query.enums.OperationType import com.tencent.bkrepo.common.query.model.Sort -import com.tencent.bkrepo.common.service.util.HeaderUtils import com.tencent.bkrepo.common.security.manager.PermissionManager import com.tencent.bkrepo.common.security.util.SecurityUtils +import com.tencent.bkrepo.common.service.util.HeaderUtils import com.tencent.bkrepo.common.service.util.SpringContextUtils.Companion.publishEvent import com.tencent.bkrepo.common.storage.core.StorageService import com.tencent.bkrepo.common.stream.constant.BinderType @@ -63,7 +62,6 @@ import com.tencent.bkrepo.repository.dao.RepositoryDao import com.tencent.bkrepo.repository.model.TNode import com.tencent.bkrepo.repository.model.TRepository import com.tencent.bkrepo.repository.pojo.metadata.MetadataModel -import com.tencent.bkrepo.repository.pojo.node.ConflictStrategy import com.tencent.bkrepo.repository.pojo.node.NodeDetail import com.tencent.bkrepo.repository.pojo.node.NodeInfo import com.tencent.bkrepo.repository.pojo.node.NodeListOption @@ -83,7 +81,6 @@ import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.Lazy import org.springframework.dao.DuplicateKeyException -import org.springframework.data.mongodb.core.query.Criteria import org.springframework.data.mongodb.core.query.Query import org.springframework.data.mongodb.core.query.Update import org.springframework.data.mongodb.core.query.and @@ -173,7 +170,6 @@ abstract class NodeBaseService( @Transactional(rollbackFor = [Throwable::class]) override fun createNode(createRequest: NodeCreateRequest): NodeDetail { with(createRequest) { - val createStart = System.currentTimeMillis() val fullPath = PathUtils.normalizeFullPath(fullPath) Preconditions.checkArgument(!PathUtils.isRoot(fullPath), this::fullPath.name) Preconditions.checkArgument(folder || !sha256.isNullOrBlank(), this::sha256.name) @@ -181,13 +177,13 @@ abstract class NodeBaseService( // 仓库是否存在 val repo = checkRepo(projectId, repoName) // 路径唯一性校验 - val deletedTime = checkConflictAndQuota(createRequest, fullPath) + checkConflictAndQuota(createRequest, fullPath) // 判断父目录是否存在,不存在先创建 - val parents = mkdirs(projectId, repoName, PathUtils.resolveParent(fullPath), operator) + mkdirs(projectId, repoName, PathUtils.resolveParent(fullPath), operator) // 创建节点 val node = buildTNode(this) doCreate(node) - afterCreate(repo, node, createStart, parents, deletedTime) + afterCreate(repo, node) logger.info("Create node[/$projectId/$repoName$fullPath], sha256[$sha256] success.") return convertToDetail(node)!! } @@ -262,21 +258,8 @@ abstract class NodeBaseService( } } - private fun afterCreate( - repo: TRepository, - node: TNode, - createStart: Long, - parents: List, - deletedTime: LocalDateTime?, - ) { + private fun afterCreate(repo: TRepository, node: TNode) { with(node) { - val createEnd = System.currentTimeMillis() - val timeout = createEnd - createStart > repositoryProperties.nodeCreateTimeout - if (timeout) { - logger.info("Create node[$fullPath] timeout") - rollbackCreate(parents, node, deletedTime) - throw ErrorCodeException(ArtifactMessageCode.NODE_CREATE_TIMEOUT, fullPath) - } if (isGenericRepo(repo)) { publishEvent(buildCreatedEvent(node)) createRouter(this) @@ -294,66 +277,6 @@ abstract class NodeBaseService( } } - /** - * 回滚创建的节点和目录 - * */ - private fun rollbackCreate(parents: List, newNode: TNode, deletedTime: LocalDateTime?) { - val toDeletedDirs = mutableListOf() - val projectId = newNode.projectId - val repoName = newNode.repoName - toDeletedDirs.addAll(parents) - if (newNode.folder) { - toDeletedDirs.add(newNode) - } else { - // 删除新创建的 - nodeDao.remove( - Query( - Criteria(ID).isEqualTo(newNode.id) - .and(TNode::projectId).isEqualTo(projectId), - ), - ) - // 软链接node或fs-server创建的node的sha256为FAKE_SHA256,不会增加引用数,回滚时无需减少 - if (newNode.sha256 != FAKE_SHA256) { - fileReferenceService.decrement(newNode) - } - quotaService.decreaseUsedVolume(projectId, repoName, newNode.size) - logger.info("Rollback node [$projectId/$repoName${newNode.fullPath}]") - } - - toDeletedDirs.sortByDescending { it.fullPath } - for (dir in toDeletedDirs) { - val option = NodeListOption(deep = true) - val query = NodeQueryHelper.nodeListQuery(dir.projectId, dir.repoName, dir.fullPath, option) - val size = nodeDao.find(query).size - if (size > 0) { - break - } - nodeDao.remove( - Query( - Criteria(ID).isEqualTo(dir.id) - .and(TNode::projectId).isEqualTo(dir.projectId), - ), - ) - logger.info("Rollback node [$projectId/$repoName${dir.fullPath}]") - } - - // 恢复创建前的情况,可能是新创建也可能是被覆盖。deletedTime不为空,则表示是覆盖创建 - with(newNode) { - deletedTime?.let { - val restoreContext = NodeRestoreSupport.RestoreContext( - projectId = projectId, - repoName = repoName, - rootFullPath = fullPath, - deletedTime = it, - conflictStrategy = ConflictStrategy.FAILED, - operator = createdBy, - ) - restoreNode(restoreContext) - logger.info("Restore node [$projectId/$repoName$fullPath]") - } - } - } - /** * 上报节点数据到数据平台 */ diff --git a/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/FileReferenceServiceTest.kt b/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/FileReferenceServiceTest.kt index 2a7598b381..8e5d6cb8d6 100644 --- a/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/FileReferenceServiceTest.kt +++ b/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/FileReferenceServiceTest.kt @@ -84,6 +84,8 @@ class FileReferenceServiceTest @Autowired constructor( fun testIncrementAndDecrement() { val sha256 = uniqueId() assertEquals(0, fileReferenceService.count(sha256, null)) + assertTrue(fileReferenceService.increment(sha256, null, 0L)) + assertEquals(0L, fileReferenceService.get(null, sha256).count) assertTrue(fileReferenceService.increment(sha256, null)) assertEquals(1, fileReferenceService.count(sha256, null)) assertTrue(fileReferenceService.increment(sha256, null)) diff --git a/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/NodeServiceTest.kt b/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/NodeServiceTest.kt index c8bdd4aa64..d59f07722b 100644 --- a/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/NodeServiceTest.kt +++ b/src/backend/repository/biz-repository/src/test/kotlin/com/tencent/bkrepo/repository/service/NodeServiceTest.kt @@ -41,7 +41,6 @@ import com.tencent.bkrepo.common.service.util.ResponseBuilder import com.tencent.bkrepo.repository.UT_PROJECT_ID import com.tencent.bkrepo.repository.UT_REPO_NAME import com.tencent.bkrepo.repository.UT_USER -import com.tencent.bkrepo.repository.config.RepositoryProperties import com.tencent.bkrepo.repository.dao.FileReferenceDao import com.tencent.bkrepo.repository.dao.NodeDao import com.tencent.bkrepo.repository.pojo.metadata.MetadataModel @@ -81,7 +80,6 @@ class NodeServiceTest @Autowired constructor( private val projectService: ProjectService, private val repositoryService: RepositoryService, private val nodeService: NodeService, - private val repositoryProperties: RepositoryProperties ) : ServiceBaseTest() { private val option = NodeListOption(includeFolder = false, deep = false) @@ -97,7 +95,6 @@ class NodeServiceTest @Autowired constructor( @BeforeEach fun beforeEach() { initMock() - repositoryProperties.nodeCreateTimeout = 5000 nodeService.deleteByPath(UT_PROJECT_ID, UT_REPO_NAME, ROOT, UT_USER) } @@ -785,55 +782,6 @@ class NodeServiceTest @Autowired constructor( assertEquals("value", nodeService.getNodeDetail(node("/b"))!!.metadata["key"]) } - @Test - @DisplayName("测试创建文件超时") - fun testCreateFileNodeTimeout() { - // 新创建的文件回滚 - repositoryProperties.nodeCreateTimeout = 0 - val fullPath = "/1/2/3.txt" - assertThrows { - val request = createRequest(fullPath, folder = false, size = 100) - nodeService.createNode(request) - } - // 回滚创建的新文件和目录 - assertEquals(false, nodeService.checkExist(node(fullPath))) - assertEquals(false, nodeService.checkExist(node("/1/2"))) - assertEquals(false, nodeService.checkExist(node("/1"))) - - // 存在旧文件,使用覆盖创建的文件回滚 - repositoryProperties.nodeCreateTimeout = 5000 - // 创建旧文件数据 - val request1 = createRequest(fullPath, folder = false, size = 10) - nodeService.createNode(request1) - assertEquals(true, nodeService.checkExist(node(fullPath))) - - assertThrows { - repositoryProperties.nodeCreateTimeout = 0 - // 覆盖创建 - val request2 = createRequest(fullPath, folder = false, size = 100, override = true) - nodeService.createNode(request2) - } - // 旧文件还在 - assertEquals(true, nodeService.checkExist(node(fullPath))) - // 恢复的旧文件 - assertEquals(10, nodeService.getNodeDetail(node(fullPath))?.size) - } - - @Test - @DisplayName("测试创建目录超时") - fun testCreateDirTimeout() { - // 新创建的目录回滚 - repositoryProperties.nodeCreateTimeout = 0 - val fullPath = "/1/2/3" - assertThrows { - val request = createRequest(fullPath, folder = true) - nodeService.createNode(request) - } - assertEquals(false, nodeService.checkExist(node(fullPath))) - assertEquals(false, nodeService.checkExist(node("/1/2"))) - assertEquals(false, nodeService.checkExist(node("/1"))) - } - private fun createRequest( fullPath: String = "/a/b/c", folder: Boolean = true, From f273e29f6ff848383b7e42e5304026e39fe23d34 Mon Sep 17 00:00:00 2001 From: owen Date: Tue, 28 May 2024 11:14:12 +0800 Subject: [PATCH 6/8] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E4=BB=93?= =?UTF-8?q?=E5=BA=93=E5=85=B3=E9=97=AD=E9=BB=98=E8=AE=A4=E6=9D=83=E9=99=90?= =?UTF-8?q?=20#2164?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat:目录权限新增配置---禁止根目录权限 #2166 * feat:目录权限新增配置---禁止根目录权限 #2166 * feat:目录权限新增配置---禁止根目录权限 #2166 * feat: 支持仓库关闭默认权限 #2164 * feat: 支持仓库关闭默认权限 #2164 * feat: 支持仓库关闭默认权限 #2164 * feat: 支持仓库关闭默认权限 #2164 * feat: 支持仓库关闭默认权限 #2164 * feat: 支持仓库关闭默认权限 #2164 * feat: 支持仓库关闭默认权限 #2164 * feat: 支持仓库关闭默认权限 #2164 * feat: 支持仓库关闭默认权限 #2164 * feat: 支持仓库关闭默认权限 #2164 * feat: 支持仓库关闭默认权限 #2164 * feat: 支持仓库关闭默认权限 #2164 * feat: 支持仓库关闭默认权限 #2164 * feat: 支持仓库关闭默认权限 #2164 * feat: 支持仓库关闭默认权限 #2164 --------- Co-authored-by: lannoy0523 <935275025@qq.com> --- .../tencent/bkrepo/auth/constant/Constants.kt | 1 + .../bkrepo/auth/config/AuthServiceConfig.kt | 9 ++- .../bkrepo/auth/controller/OpenResource.kt | 4 +- .../service/ServicePermissionController.kt | 23 ++++-- .../controller/user/RepoModeController.kt | 75 +++++++++++++++++++ .../bkrepo/auth/dao/RepoAuthConfigDao.kt | 63 ++++++++++++++++ .../bkrepo/auth/helper/PermissionHelper.kt | 53 +++++++++---- .../auth/interceptor/AuthInterceptor.kt | 2 + .../bkrepo/auth/model/TRepoAuthConfig.kt | 51 +++++++++++++ .../pojo/authconfig/RepoAuthStatusRequest.kt | 39 ++++++++++ .../auth/pojo/permission/RepoModeStatus.kt | 6 ++ .../bkrepo/auth/service/PermissionService.kt | 12 ++- .../bkrepo/auth/service/RepoModeService.kt | 44 +++++++++++ .../bkauth/DevopsPermissionServiceImpl.kt | 3 + .../bkiamv3/BkIamV3PermissionServiceImpl.kt | 3 + .../auth/service/impl/RepoModeServiceImpl.kt | 60 +++++++++++++++ .../service/local/PermissionServiceImpl.kt | 31 +++++++- .../ArtifactPreloadPlanServiceImplTest.kt | 2 +- .../user/UserRepositoryController.kt | 2 +- .../repo/impl/RepositoryServiceImpl.kt | 2 +- .../src/store/actions/permission.js | 19 +++++ .../permissionConfig/permissionConfig.vue | 41 +++++++++- src/frontend/locale/repository/en-US.json | 4 +- src/frontend/locale/repository/zh-CN.json | 4 +- 24 files changed, 517 insertions(+), 36 deletions(-) create mode 100644 src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/user/RepoModeController.kt create mode 100644 src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/dao/RepoAuthConfigDao.kt create mode 100644 src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/model/TRepoAuthConfig.kt create mode 100644 src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/authconfig/RepoAuthStatusRequest.kt create mode 100644 src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/permission/RepoModeStatus.kt create mode 100644 src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/RepoModeService.kt create mode 100644 src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/impl/RepoModeServiceImpl.kt diff --git a/src/backend/auth/api-auth/src/main/kotlin/com/tencent/bkrepo/auth/constant/Constants.kt b/src/backend/auth/api-auth/src/main/kotlin/com/tencent/bkrepo/auth/constant/Constants.kt index 4f14971b35..8068d56a40 100644 --- a/src/backend/auth/api-auth/src/main/kotlin/com/tencent/bkrepo/auth/constant/Constants.kt +++ b/src/backend/auth/api-auth/src/main/kotlin/com/tencent/bkrepo/auth/constant/Constants.kt @@ -72,6 +72,7 @@ const val AUTH_API_ACCOUNT_PREFIX = "/api/account" const val AUTH_SERVICE_ACCOUNT_PREFIX = "/service/account" const val AUTH_API_OAUTH_PREFIX = "/api/oauth" const val AUTH_SERVICE_OAUTH_PREFIX = "/service/oauth" +const val AUTH_API_AUTH_MODE_PREFIX = "/api/mode/repo" const val AUTH_CLUSTER_TOKEN_INFO_PREFIX = "/cluster/temporary/token/info" const val AUTH_CLUSTER_TOKEN_DELETE_PREFIX = "/cluster/temporary/token/delete" const val AUTH_CLUSTER_TOKEN_DECREMENT_PREFIX = "/cluster/temporary/token/decrement" diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/config/AuthServiceConfig.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/config/AuthServiceConfig.kt index a6e3b3959b..6d343dcf02 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/config/AuthServiceConfig.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/config/AuthServiceConfig.kt @@ -40,6 +40,7 @@ import com.tencent.bkrepo.auth.condition.DevopsAuthCondition import com.tencent.bkrepo.auth.condition.BkV3RbacAuthCondition import com.tencent.bkrepo.auth.condition.LocalAuthCondition import com.tencent.bkrepo.auth.dao.PersonalPathDao +import com.tencent.bkrepo.auth.dao.RepoAuthConfigDao import com.tencent.bkrepo.auth.service.AccountService import com.tencent.bkrepo.auth.service.PermissionService import com.tencent.bkrepo.auth.service.RoleService @@ -93,7 +94,8 @@ class AuthServiceConfig { accountRepository: AccountRepository, permissionDao: PermissionDao, userDao: UserDao, - personalPathDao: PersonalPathDao + personalPathDao: PersonalPathDao, + repoAuthConfigDao: RepoAuthConfigDao ): PermissionService { return PermissionServiceImpl( roleRepository, @@ -101,6 +103,7 @@ class AuthServiceConfig { permissionDao, userDao, personalPathDao, + repoAuthConfigDao, repositoryClient, projectClient ) @@ -111,6 +114,7 @@ class AuthServiceConfig { bkiamV3Service: BkIamV3Service, userDao: UserDao, personalPathDao: PersonalPathDao, + repoAuthConfigDao: RepoAuthConfigDao, roleRepository: RoleRepository, accountRepository: AccountRepository, permissionDao: PermissionDao, @@ -123,6 +127,7 @@ class AuthServiceConfig { accountRepository, permissionDao, personalPathDao, + repoAuthConfigDao, repoClient, projectClient ) @@ -136,6 +141,7 @@ class AuthServiceConfig { permissionDao: PermissionDao, userDao: UserDao, personalPathDao: PersonalPathDao, + repoAuthConfigDao: RepoAuthConfigDao, bkAuthConfig: DevopsAuthConfig, bkAuthPipelineService: DevopsPipelineService, bkAuthProjectService: DevopsProjectService, @@ -147,6 +153,7 @@ class AuthServiceConfig { permissionDao, userDao, personalPathDao, + repoAuthConfigDao, bkAuthConfig, bkAuthPipelineService, bkAuthProjectService, diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/OpenResource.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/OpenResource.kt index 1aabed1727..54ef53a1de 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/OpenResource.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/OpenResource.kt @@ -116,8 +116,8 @@ open class OpenResource(private val permissionService: PermissionService) { fun preCheckUserInProject(type: AuthPermissionType, projectId: String, repoName: String?) { val checkRequest = CheckPermissionRequest( uid = SecurityUtils.getUserId(), - resourceType = ResourceType.PROJECT.toString(), - action = PermissionAction.WRITE.toString(), + resourceType = ResourceType.PROJECT.name, + action = PermissionAction.WRITE.name, projectId = projectId, appId = SecurityUtils.getPlatformId() ) diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/service/ServicePermissionController.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/service/ServicePermissionController.kt index 635b2c7d4f..c0d736107e 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/service/ServicePermissionController.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/service/ServicePermissionController.kt @@ -49,13 +49,26 @@ class ServicePermissionController @Autowired constructor( /** - * 本接口不做权限校验,返回空列表时可能表示所有路径均有权限,也可能为无项目仓库权限,因此需要单独做仓库权限校验 + * 本接口不做权限校验,status表明是否需要做校验 + * OperationType IN 表示有权限的路径列表,需要做交集 + * OperationType NIN 表示有无权限的路径列表,需要做差集 */ override fun listPermissionPath(userId: String, projectId: String, repoName: String): Response { - val permissionPath = permissionService.listNoPermissionPath(userId, projectId, repoName) - val status = permissionPath.isNotEmpty() - val result = ListPathResult(status = status, path = mapOf(OperationType.NIN to permissionPath)) - return ResponseBuilder.success(result) + val repoAccessControl = permissionService.checkRepoAccessControl(projectId, repoName) + if (repoAccessControl) { + val permissionPath = permissionService.listPermissionPath(userId, projectId, repoName) + if (permissionPath == null) { + val result = ListPathResult(status = false, path = mapOf(OperationType.IN to emptyList())) + return ResponseBuilder.success(result) + } + val result = ListPathResult(status = true, path = mapOf(OperationType.IN to permissionPath)) + return ResponseBuilder.success(result) + } else { + val permissionPath = permissionService.listNoPermissionPath(userId, projectId, repoName) + val status = permissionPath.isNotEmpty() + val result = ListPathResult(status = status, path = mapOf(OperationType.NIN to permissionPath)) + return ResponseBuilder.success(result) + } } diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/user/RepoModeController.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/user/RepoModeController.kt new file mode 100644 index 0000000000..bfa4f1f293 --- /dev/null +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/controller/user/RepoModeController.kt @@ -0,0 +1,75 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 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.bkrepo.auth.controller.user + +import com.tencent.bkrepo.auth.controller.OpenResource +import com.tencent.bkrepo.auth.pojo.permission.RepoModeStatus +import com.tencent.bkrepo.auth.pojo.authconfig.RepoAuthStatusRequest +import com.tencent.bkrepo.auth.service.PermissionService +import com.tencent.bkrepo.auth.service.RepoModeService +import com.tencent.bkrepo.common.api.pojo.Response +import com.tencent.bkrepo.common.service.util.ResponseBuilder +import org.springframework.web.bind.annotation.RestController +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody + +@RestController +@RequestMapping("/api/mode/repo") +class RepoModeController( + private val repoModeService: RepoModeService, + permissionService: PermissionService +) : OpenResource(permissionService) { + + @GetMapping("/query") + fun getStatus( + @RequestParam projectId: String, + @RequestParam repoName: String, + ): Response { + preCheckProjectAdmin(projectId) + val result = repoModeService.getAccessControlStatus(projectId, repoName) + return ResponseBuilder.success(result) + } + + @PostMapping("/toggle") + fun toggleStatus( + @RequestBody request: RepoAuthStatusRequest + ): Response { + with(request) { + preCheckProjectAdmin(projectId) + repoModeService.createOrUpdateConfig(projectId, repoName, status) + return ResponseBuilder.success( + repoModeService.getAccessControlStatus(projectId, repoName) + ) + } + } + +} \ No newline at end of file diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/dao/RepoAuthConfigDao.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/dao/RepoAuthConfigDao.kt new file mode 100644 index 0000000000..fb936c020c --- /dev/null +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/dao/RepoAuthConfigDao.kt @@ -0,0 +1,63 @@ +/* + * 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.bkrepo.auth.dao + +import com.tencent.bkrepo.auth.model.TRepoAuthConfig +import com.tencent.bkrepo.common.mongo.dao.simple.SimpleMongoDao +import com.tencent.bkrepo.common.security.util.SecurityUtils +import org.springframework.data.mongodb.core.FindAndModifyOptions +import org.springframework.data.mongodb.core.query.Criteria +import org.springframework.data.mongodb.core.query.Query +import org.springframework.data.mongodb.core.query.Update +import org.springframework.stereotype.Repository +import java.time.LocalDateTime + +@Repository +class RepoAuthConfigDao : SimpleMongoDao() { + fun findOneByProjectRepo(projectId: String, repoName: String): TRepoAuthConfig? { + return this.findOne( + Query.query( + Criteria.where(TRepoAuthConfig::projectId.name).`is`(projectId) + .and(TRepoAuthConfig::repoName.name).`is`(repoName) + ) + ) + } + + fun upsertProjectRepo(projectId: String, repoName: String, status: Boolean): String { + val query = Query.query( + Criteria.where(TRepoAuthConfig::projectId.name).`is`(projectId) + .and(TRepoAuthConfig::repoName.name).`is`(repoName) + ) + val options = FindAndModifyOptions().returnNew(true).upsert(true) + val update = Update().set(TRepoAuthConfig::accessControl.name, status) + .set(TRepoAuthConfig::lastModifiedBy.name, SecurityUtils.getUserId()) + .set(TRepoAuthConfig::lastModifiedDate.name, LocalDateTime.now()) + return this.findAndModify(query, update, options, TRepoAuthConfig::class.java)!!.id!! + } +} \ No newline at end of file diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/helper/PermissionHelper.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/helper/PermissionHelper.kt index ced0c74925..f23127b2fd 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/helper/PermissionHelper.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/helper/PermissionHelper.kt @@ -186,10 +186,11 @@ class PermissionHelper constructor( return request.projectId != null && request.action == READ.name && isProjectUser } - fun getNoPermissionPathFromConfig( + fun getPermissionPathFromConfig( userId: String, roles: List, - config: List + config: List, + include: Boolean ): List { val excludePath = mutableListOf() val includePath = mutableListOf() @@ -221,6 +222,9 @@ class PermissionHelper constructor( } } } + if (include) { + return includePath.distinct() + } val filterPath = includePath.distinct() return excludePath.distinct().filter { !filterPath.contains(it) } } @@ -336,16 +340,14 @@ class PermissionHelper constructor( return permissionDao.updateById(id, key, value) } - fun checkNodeAction(request: CheckPermissionRequest, userRoles: List?, isProjectUser: Boolean): Boolean { + fun checkNodeActionWithOutCtrl( + request: CheckPermissionRequest, + userRoles: List?, + isProjectUser: Boolean + ): Boolean { with(request) { - var roles = userRoles if (resourceType != NODE.name || path == null) return false - if (roles == null) { - val user = userDao.findFirstByUserId(uid) ?: run { - throw ErrorCodeException(AuthMessageCode.AUTH_USER_NOT_EXIST) - } - roles = user.roles - } + val roles = getUserRoles(uid, userRoles) val result = permissionDao.listInPermission(projectId!!, repoName!!, uid, resourceType, roles) result.forEach { if (checkIncludePatternAction(it.includePattern, path!!, it.actions, action)) return true @@ -363,16 +365,35 @@ class PermissionHelper constructor( return isProjectUser } + fun checkNodeActionWithCtrl(request: CheckPermissionRequest, userRoles: List?): Boolean { + with(request) { + if (resourceType != NODE.name || path == null) return false + val roles = getUserRoles(uid, userRoles) + val result = permissionDao.listInPermission(projectId!!, repoName!!, uid, resourceType, roles) + result.forEach { + if (checkIncludePatternAction(it.includePattern, path!!, it.actions, action)) return true + } + val personalPathCheck = checkPersonalPath(uid, projectId!!, repoName!!, path!!) + if (personalPathCheck != null) return personalPathCheck + return false + } + } + + private fun getUserRoles(userId: String, userRoles: List?): List { + var roles = userRoles + if (roles == null) { + val user = userDao.findFirstByUserId(userId) ?: run { + throw ErrorCodeException(AuthMessageCode.AUTH_USER_NOT_EXIST) + } + roles = user.roles + } + return roles + } + private fun checkPersonalPath(userId: String, projectId: String, repoName: String, path: String): Boolean? { // check personal path val personalPath = personalPathDao.findOneByProjectAndRepo(userId, projectId, repoName) if (personalPath != null && path.startsWith(personalPath.fullPath)) return true - - // check personal exclude path - val personalExcludePath = personalPathDao.listByProjectAndRepoAndExcludeUser(userId, projectId, repoName) - personalExcludePath.forEach { - if (path.startsWith(it.fullPath)) return false - } return null } diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/interceptor/AuthInterceptor.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/interceptor/AuthInterceptor.kt index 4dd58bea6d..8e9bda5317 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/interceptor/AuthInterceptor.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/interceptor/AuthInterceptor.kt @@ -37,6 +37,7 @@ import com.tencent.bkrepo.auth.constant.AUTH_API_PERMISSION_PREFIX import com.tencent.bkrepo.auth.constant.AUTH_API_OAUTH_PREFIX import com.tencent.bkrepo.auth.constant.AUTH_API_USER_PREFIX import com.tencent.bkrepo.auth.constant.AUTH_API_ROLE_PREFIX +import com.tencent.bkrepo.auth.constant.AUTH_API_AUTH_MODE_PREFIX import com.tencent.bkrepo.auth.constant.AUTH_CLUSTER_PERMISSION_CHECK_PREFIX import com.tencent.bkrepo.auth.constant.AUTH_CLUSTER_TOKEN_DECREMENT_PREFIX import com.tencent.bkrepo.auth.constant.AUTH_CLUSTER_TOKEN_DELETE_PREFIX @@ -241,6 +242,7 @@ class AuthInterceptor( AUTH_API_ROLE_PREFIX, AUTH_API_PERMISSION_PREFIX, AUTH_API_OAUTH_PREFIX, + AUTH_API_AUTH_MODE_PREFIX ) diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/model/TRepoAuthConfig.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/model/TRepoAuthConfig.kt new file mode 100644 index 0000000000..54ee4d8a78 --- /dev/null +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/model/TRepoAuthConfig.kt @@ -0,0 +1,51 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 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.bkrepo.auth.model + +import org.springframework.data.mongodb.core.index.CompoundIndex +import org.springframework.data.mongodb.core.index.CompoundIndexes +import org.springframework.data.mongodb.core.mapping.Document +import java.time.LocalDateTime + +@Document("repo_auth_mode") +@CompoundIndexes( + CompoundIndex(name = "repo_idx", def = "{'projectId': 1, 'repoName': 1}", background = true, unique = true), + CompoundIndex(name = "access_ctrl_idx", def = "{'accessControl': 1}", background = true) +) +data class TRepoAuthConfig( + var id: String? = null, + var projectId: String, + var repoName: String, + var accessControl: Boolean, + var lastModifiedBy: String, + val lastModifiedDate: LocalDateTime +) diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/authconfig/RepoAuthStatusRequest.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/authconfig/RepoAuthStatusRequest.kt new file mode 100644 index 0000000000..14c6945de3 --- /dev/null +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/authconfig/RepoAuthStatusRequest.kt @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 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.bkrepo.auth.pojo.authconfig + +data class RepoAuthStatusRequest( + val status: Boolean, + val projectId: String, + val repoName: String +) + diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/permission/RepoModeStatus.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/permission/RepoModeStatus.kt new file mode 100644 index 0000000000..9d08a248ea --- /dev/null +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/pojo/permission/RepoModeStatus.kt @@ -0,0 +1,6 @@ +package com.tencent.bkrepo.auth.pojo.permission + +data class RepoModeStatus( + val id: String, + val status: Boolean +) diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/PermissionService.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/PermissionService.kt index d350caa411..c4cffa4152 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/PermissionService.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/PermissionService.kt @@ -65,10 +65,20 @@ interface PermissionService { fun listPermissionProject(userId: String): List /** - * 获取有权限路径列表 + * 获取无权限路径列表 */ fun listNoPermissionPath(userId: String, projectId: String, repoName: String): List + /** + * 获取有权限路径列表 + */ + fun listPermissionPath(userId: String, projectId: String, repoName: String): List? + + /** + * 查询是否开启仓库访问限制 + */ + fun checkRepoAccessControl(projectId: String, repoName: String): Boolean + fun createPermission(request: CreatePermissionRequest): Boolean fun listPermission(projectId: String, repoName: String?, resourceType: String): List diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/RepoModeService.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/RepoModeService.kt new file mode 100644 index 0000000000..a082786ea7 --- /dev/null +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/RepoModeService.kt @@ -0,0 +1,44 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 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.bkrepo.auth.service + + +import com.tencent.bkrepo.auth.pojo.permission.RepoModeStatus + +interface RepoModeService { + + fun createOrUpdateConfig(projectId: String, repoName: String, status: Boolean): RepoModeStatus + + fun getAccessControlStatus(projectId: String, repoName: String): RepoModeStatus + +} \ No newline at end of file diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkauth/DevopsPermissionServiceImpl.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkauth/DevopsPermissionServiceImpl.kt index 812292a5b2..3b483627fc 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkauth/DevopsPermissionServiceImpl.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkauth/DevopsPermissionServiceImpl.kt @@ -42,6 +42,7 @@ import com.tencent.bkrepo.auth.constant.LOG import com.tencent.bkrepo.auth.constant.PIPELINE import com.tencent.bkrepo.auth.constant.REPORT import com.tencent.bkrepo.auth.dao.PersonalPathDao +import com.tencent.bkrepo.auth.dao.RepoAuthConfigDao import com.tencent.bkrepo.auth.pojo.enums.PermissionAction.MANAGE import com.tencent.bkrepo.auth.pojo.enums.PermissionAction.READ import com.tencent.bkrepo.auth.pojo.enums.PermissionAction.WRITE @@ -65,6 +66,7 @@ class DevopsPermissionServiceImpl constructor( permissionDao: PermissionDao, userDao: UserDao, personalPathDao: PersonalPathDao, + repoAuthConfigDao: RepoAuthConfigDao, private val devopsAuthConfig: DevopsAuthConfig, private val devopsPipelineService: DevopsPipelineService, private val devopsProjectService: DevopsProjectService, @@ -78,6 +80,7 @@ class DevopsPermissionServiceImpl constructor( accountRepository, permissionDao, personalPathDao, + repoAuthConfigDao, repoClient, projectClient, ) { diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkiamv3/BkIamV3PermissionServiceImpl.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkiamv3/BkIamV3PermissionServiceImpl.kt index 86d0f1bcb1..3312179a57 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkiamv3/BkIamV3PermissionServiceImpl.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/bkiamv3/BkIamV3PermissionServiceImpl.kt @@ -33,6 +33,7 @@ import com.tencent.bkrepo.auth.constant.PIPELINE import com.tencent.bkrepo.auth.constant.REPORT import com.tencent.bkrepo.auth.dao.PermissionDao import com.tencent.bkrepo.auth.dao.PersonalPathDao +import com.tencent.bkrepo.auth.dao.RepoAuthConfigDao import com.tencent.bkrepo.auth.dao.UserDao import com.tencent.bkrepo.auth.dao.repository.AccountRepository import com.tencent.bkrepo.auth.dao.repository.RoleRepository @@ -56,6 +57,7 @@ open class BkIamV3PermissionServiceImpl( accountRepository: AccountRepository, permissionDao: PermissionDao, personalPathDao: PersonalPathDao, + repoAuthConfigDao: RepoAuthConfigDao, repoClient: RepositoryClient, projectClient: ProjectClient ) : PermissionServiceImpl( @@ -64,6 +66,7 @@ open class BkIamV3PermissionServiceImpl( permissionDao, userDao, personalPathDao, + repoAuthConfigDao, repoClient, projectClient ) { diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/impl/RepoModeServiceImpl.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/impl/RepoModeServiceImpl.kt new file mode 100644 index 0000000000..68c9e82b2a --- /dev/null +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/impl/RepoModeServiceImpl.kt @@ -0,0 +1,60 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2024 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.bkrepo.auth.service.impl + +import com.tencent.bkrepo.auth.dao.RepoAuthConfigDao +import com.tencent.bkrepo.auth.pojo.permission.RepoModeStatus +import com.tencent.bkrepo.auth.service.RepoModeService +import org.springframework.stereotype.Service + +@Service +class RepoModeServiceImpl( + private val repoAuthConfigDao: RepoAuthConfigDao +) : RepoModeService { + + override fun createOrUpdateConfig(projectId: String, repoName: String, status: Boolean): RepoModeStatus { + val id = repoAuthConfigDao.upsertProjectRepo(projectId, repoName, status) + return RepoModeStatus(id, status) + } + + + override fun getAccessControlStatus(projectId: String, repoName: String): RepoModeStatus { + val result = repoAuthConfigDao.findOneByProjectRepo(projectId, repoName) + if (result != null) { + return RepoModeStatus(result.id!!, result.accessControl) + } + val id = repoAuthConfigDao.upsertProjectRepo(projectId, repoName, false) + return RepoModeStatus(id, false) + } + + +} \ No newline at end of file diff --git a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/local/PermissionServiceImpl.kt b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/local/PermissionServiceImpl.kt index bd34de726f..d11fdb1976 100644 --- a/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/local/PermissionServiceImpl.kt +++ b/src/backend/auth/biz-auth/src/main/kotlin/com/tencent/bkrepo/auth/service/local/PermissionServiceImpl.kt @@ -38,6 +38,7 @@ import com.tencent.bkrepo.auth.constant.PROJECT_MANAGE_ID import com.tencent.bkrepo.auth.constant.PROJECT_VIEWER_ID import com.tencent.bkrepo.auth.dao.PermissionDao import com.tencent.bkrepo.auth.dao.PersonalPathDao +import com.tencent.bkrepo.auth.dao.RepoAuthConfigDao import com.tencent.bkrepo.auth.dao.UserDao import com.tencent.bkrepo.auth.message.AuthMessageCode import com.tencent.bkrepo.auth.model.TPermission @@ -77,6 +78,7 @@ open class PermissionServiceImpl constructor( private val permissionDao: PermissionDao, private val userDao: UserDao, private val personalPathDao: PersonalPathDao, + private val repoAuthConfigDao: RepoAuthConfigDao, val repoClient: RepositoryClient, val projectClient: ProjectClient ) : PermissionService { @@ -302,12 +304,26 @@ open class PermissionServiceImpl constructor( return emptyList() } val projectPermission = permissionDao.listByResourceAndRepo(NODE.name, projectId, repoName) - val configPath = permHelper.getNoPermissionPathFromConfig(userId, user.roles, projectPermission) + val configPath = permHelper.getPermissionPathFromConfig(userId, user.roles, projectPermission, false) val personalPath = personalPathDao.listByProjectAndRepoAndExcludeUser(userId, projectId, repoName) .map { it.fullPath } return (configPath + personalPath).distinct() } + override fun listPermissionPath(userId: String, projectId: String, repoName: String): List? { + val user = userDao.findFirstByUserId(userId) ?: return emptyList() + if (user.admin || isUserLocalProjectAdmin(userId, projectId)) { + return null + } + val permission = permissionDao.listByResourceAndRepo(NODE.name, projectId, repoName) + val configPath = permHelper.getPermissionPathFromConfig(userId, user.roles, permission, true).toMutableList() + val personalPath = personalPathDao.findOneByProjectAndRepo(userId, projectId, repoName) + if (personalPath != null) { + configPath.add(personalPath.fullPath) + } + return configPath.distinct() + } + fun getAllRepoByProjectId(projectId: String): List { return repoClient.listRepo(projectId).data?.map { it.name } ?: emptyList() } @@ -396,7 +412,13 @@ open class PermissionServiceImpl constructor( } fun checkNodeAction(request: CheckPermissionRequest, userRoles: List?, isProjectUser: Boolean): Boolean { - return permHelper.checkNodeAction(request, userRoles, isProjectUser) + with(request) { + if (checkRepoAccessControl(projectId!!, repoName!!)) { + return permHelper.checkNodeActionWithCtrl(request, userRoles) + } + return permHelper.checkNodeActionWithOutCtrl(request, userRoles, isProjectUser) + } + } fun needNodeCheck(projectId: String, repoName: String): Boolean { @@ -404,6 +426,11 @@ open class PermissionServiceImpl constructor( return projectPermission.isNotEmpty() } + override fun checkRepoAccessControl(projectId: String, repoName: String): Boolean { + val result = repoAuthConfigDao.findOneByProjectRepo(projectId, repoName) ?: return false + return result.accessControl + } + companion object { private val logger = LoggerFactory.getLogger(PermissionServiceImpl::class.java) private const val defaultPersonalPrefix = "/Personal" diff --git a/src/backend/common/common-artifact/artifact-cache/src/test/kotlin/com/tencent/bkrepo/common/artifact/cache/service/impl/ArtifactPreloadPlanServiceImplTest.kt b/src/backend/common/common-artifact/artifact-cache/src/test/kotlin/com/tencent/bkrepo/common/artifact/cache/service/impl/ArtifactPreloadPlanServiceImplTest.kt index fc57912c67..9d407befa3 100644 --- a/src/backend/common/common-artifact/artifact-cache/src/test/kotlin/com/tencent/bkrepo/common/artifact/cache/service/impl/ArtifactPreloadPlanServiceImplTest.kt +++ b/src/backend/common/common-artifact/artifact-cache/src/test/kotlin/com/tencent/bkrepo/common/artifact/cache/service/impl/ArtifactPreloadPlanServiceImplTest.kt @@ -253,7 +253,7 @@ class ArtifactPreloadPlanServiceImplTest @Autowired constructor( lastModifiedDate = "", quota = null, display = true, - used = null, + used = null ) private fun buildNodeInfo(projectId: String = UT_PROJECT_ID, repoName: String = UT_REPO_NAME): NodeInfo { diff --git a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/controller/user/UserRepositoryController.kt b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/controller/user/UserRepositoryController.kt index 6c7baf7d86..bce63881f5 100644 --- a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/controller/user/UserRepositoryController.kt +++ b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/controller/user/UserRepositoryController.kt @@ -236,7 +236,7 @@ class UserRepositoryController( description = request.description, configuration = request.configuration, operator = userId, - display = request.display, + display = request.display ) repositoryService.updateRepo(repoUpdateRequest) return ResponseBuilder.success() diff --git a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/repo/impl/RepositoryServiceImpl.kt b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/repo/impl/RepositoryServiceImpl.kt index 2b9c03f497..6931cee086 100644 --- a/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/repo/impl/RepositoryServiceImpl.kt +++ b/src/backend/repository/biz-repository/src/main/kotlin/com/tencent/bkrepo/repository/service/repo/impl/RepositoryServiceImpl.kt @@ -772,7 +772,7 @@ class RepositoryServiceImpl( lastModifiedDate = it.lastModifiedDate.format(DateTimeFormatter.ISO_DATE_TIME), quota = it.quota, used = it.used, - display = it.display, + display = it.display ) } } diff --git a/src/frontend/devops-repository/src/store/actions/permission.js b/src/frontend/devops-repository/src/store/actions/permission.js index ccb203731b..606bcd5592 100644 --- a/src/frontend/devops-repository/src/store/actions/permission.js +++ b/src/frontend/devops-repository/src/store/actions/permission.js @@ -308,5 +308,24 @@ export default { return Vue.prototype.$ajax.get(`${authPrefix}/permission/permission/available`).then(res => { commit('SET_REPO_PERMISSION_LIMIT', res) }) + }, + // 获取当前repo的根目录权限 + getRootPermission (_, { projectId, repoName }) { + return Vue.prototype.$ajax.get( + `${authPrefix}/mode/repo/query`, + { + params: { + projectId: projectId, + repoName: repoName + } + } + ) + }, + // 创建或更新当前根目录权限 + createOrUpdateRootPermission (_, { body }) { + return Vue.prototype.$ajax.post( + `${authPrefix}/mode/repo/toggle`, + body + ) } } diff --git a/src/frontend/devops-repository/src/views/repoConfig/permissionConfig/permissionConfig.vue b/src/frontend/devops-repository/src/views/repoConfig/permissionConfig/permissionConfig.vue index f10115f26d..83a26970b7 100644 --- a/src/frontend/devops-repository/src/views/repoConfig/permissionConfig/permissionConfig.vue +++ b/src/frontend/devops-repository/src/views/repoConfig/permissionConfig/permissionConfig.vue @@ -1,5 +1,11 @@