diff --git a/CHANGELOG/en/CHANGELOG-2.0.md b/CHANGELOG/en/CHANGELOG-2.0.md index a539a7887a1..c93f53b828b 100644 --- a/CHANGELOG/en/CHANGELOG-2.0.md +++ b/CHANGELOG/en/CHANGELOG-2.0.md @@ -1,14 +1,14 @@ -- [v2.0.3](#v2032024-05-28) +- [v2.0.3](#v203) - [Changelog since v2.0.2](#changelog-since-v202) -- [v2.0.2](#v2022024-03-04) +- [v2.0.2](#v202) - [Changelog since v2.0.1](#changelog-since-v201) -- [v2.0.1](#v2012024-03-01) +- [v2.0.1](#v201) - [Changelog since v2.0.0](#changelog-since-v200) -- [v2.0.0](#v2002024-01-02) +- [v2.0.0](#v200) - [Changelog since v1.14.0](#changelog-since-v1140) @@ -16,7 +16,8 @@ -# v2.0.3(2024-05-28) +# v2.0.3 +## 2024-05-28 ## Changelog since v2.0.2 #### New - [New] Modification of the login failure pop-up window specification [link](http://github.com/TencentBlueKing/bk-ci/issues/8125) @@ -25,17 +26,20 @@ - [Fix] Open source community, the open source version of the project management interface needs to be authorized [link](http://github.com/TencentBlueKing/bk-ci/issues/10382) - [Fix] The front end of the community version of the simple permission center should hide the maximum authorization scope [link](http://github.com/TencentBlueKing/bk-ci/issues/10040) -# v2.0.2(2024-03-04) +# v2.0.2 +## 2024-03-04 ## Changelog since v2.0.1 #### New - [New] Initialize bkrepo to modify httpSchema [link](http://github.com/TencentBlueKing/bk-ci/issues/10056) -# v2.0.1(2024-03-01) +# v2.0.1 +## 2024-03-01 ## Changelog since v2.0.0 #### Fixes - [Fix] Fix the problem of new project failure [link](http://github.com/TencentBlueKing/bk-ci/issues/10045) -# v2.0.0(2024-01-02) +# v2.0.0 +## 2024-01-02 ## Changelog since v1.14.0 #### New - [New] Support the authority center RBAC [link](http://github.com/TencentBlueKing/bk-ci/issues/7794) diff --git a/CHANGELOG/en/CHANGELOG-2.1.md b/CHANGELOG/en/CHANGELOG-2.1.md index 44c1aefa894..b45b5f1f789 100644 --- a/CHANGELOG/en/CHANGELOG-2.1.md +++ b/CHANGELOG/en/CHANGELOG-2.1.md @@ -1,32 +1,32 @@ -- [v2.1.3](#v2132024-05-28) +- [v2.1.3](#v213) - [Changelog since v2.1.2](#changelog-since-v212) -- [v2.1.2](#v2122024-05-20) +- [v2.1.2](#v212) - [Changelog since v2.1.1](#changelog-since-v211) -- [v2.1.1](#v2112024-04-26) +- [v2.1.1](#v211) - [Changelog since v2.1.0](#changelog-since-v210) -- [v2.1.0](#v2102024-04-22) +- [v2.1.0](#v210) - [Changelog since v2.0.0](#changelog-since-v200) -- [v2.1.0-rc.6](#v210-rc62024-04-19) +- [v2.1.0-rc.6](#v210-rc6) - [Changelog since v2.1.0-rc.5](#changelog-since-v210-rc5) -- [v2.1.0-rc.5](#v210-rc52024-04-10) +- [v2.1.0-rc.5](#v210-rc5) - [Changelog since v2.1.0-rc.4](#changelog-since-v210-rc4) -- [v2.1.0-rc.4](#v210-rc42024-03-22) +- [v2.1.0-rc.4](#v210-rc4) - [Changelog since v2.1.0-rc.3](#changelog-since-v210-rc3) -- [v2.1.0-rc.3](#v210-rc32024-03-07) +- [v2.1.0-rc.3](#v210-rc3) - [Changelog since v2.1.0-rc.2](#changelog-since-v210-rc2) -- [v2.1.0-rc.2](#v210-rc22024-02-22) +- [v2.1.0-rc.2](#v210-rc2) - [Changelog since v2.1.0-rc.1](#changelog-since-v210-rc1) -- [v2.1.0-rc.1](#v210-rc12024-01-16) +- [v2.1.0-rc.1](#v210-rc1) - [Changelog since v2.0.0](#changelog-since-v200) @@ -34,7 +34,8 @@ -# v2.1.3(2024-05-28) +# v2.1.3 +## 2024-05-28 ## Changelog since v2.1.2 #### New - [New] Modification of the login failure pop-up window specification [link](http://github.com/TencentBlueKing/bk-ci/issues/8125) @@ -42,17 +43,20 @@ #### Fixes - [Fix] Open source community, the open source version of the project management interface needs to be authorized [ link ](http://github.com/TencentBlueKing/bk-ci/issues/10382) -# v2.1.2(2024-05-20) +# v2.1.2 +## 2024-05-20 ## Changelog since v2.1.1 #### Fixes - [Fix] [Community] Listing failure & white screen issue on pipeline execution page [v2.1.0+] [Link](http://github.com/TencentBlueKing/bk-ci/issues/10357) -# v2.1.1(2024-04-26) +# v2.1.1 +## 2024-04-26 ## Changelog since v2.1.0 #### Fixes - [Fix] Failed to start the process service in version 2.1 [link](http://github.com/TencentBlueKing/bk-ci/issues/10271) -# v2.1.0(2024-04-22) +# v2.1.0 +## 2024-04-22 ## Changelog since v2.0.0 #### New - [New] Docker build machine supports extended resource scheduling [link](http://github.com/TencentBlueKing/bk-ci/issues/10162) @@ -325,7 +329,8 @@ - [Fix] DevCloud login debugging, window size cannot adapt [link](http://github.com/TencentBlueKing/bk-ci/issues/9418) - [Fix] Shared credentials do not need to rely on plugin sensitive interface permission verification [link](http://github.com/TencentBlueKing/bk-ci/issues/9398) -# v2.1.0-rc.6(2024-04-19) +# v2.1.0-rc.6 +## 2024-04-19 ## Changelog since v2.1.0-rc.5 #### New - [New] Docker build machine supports extended resource scheduling [link](http://github.com/TencentBlueKing/bk-ci/issues/10162) @@ -351,7 +356,8 @@ - [Fix] Under the stream pipeline, refreshing the page loads the full list [link](http://github.com/TencentBlueKing/bk-ci/issues/10152) - [Fix] Fix the special symbol escape problem in permission application jump [link](http://github.com/TencentBlueKing/bk-ci/issues/10188) -# v2.1.0-rc.5(2024-04-10) +# v2.1.0-rc.5 +## 2024-04-10 ## Changelog since v2.1.0-rc.4 #### New - [New] Create/edit project openapi and add mandatory check for operational products [link](http://github.com/TencentBlueKing/bk-ci/issues/10088) @@ -382,7 +388,8 @@ - [Fix] Fix the issue of code base permission loss [link](http://github.com/TencentBlueKing/bk-ci/issues/10131) - [Fix] Bug in full transfer permissions [link](http://github.com/TencentBlueKing/bk- ci/issues/10117) -# v2.1.0-rc.4(2024-03-22) +# v2.1.0-rc.4 +## 2024-03-22 ## Changelog since v2.1.0-rc.3 #### New - [New] Public build machine supports persistent build container scheduling [link](http://github.com/TencentBlueKing/bk-ci/issues/9269) @@ -406,7 +413,8 @@ - [Fix] When deleting a pipeline, the code base association is not deleted [link](http://github.com/TencentBlueKing/bk-ci/issues/10111) - [Fix] When the scheduled trigger plug-in checks the latest version of the SVN code base, there is no need to call the session interface [link](http://github.com/TencentBlueKing/bk-ci/issues/10096) -# v2.1.0-rc.3(2024-03-07) +# v2.1.0-rc.3 +## 2024-03-07 ## Changelog since v2.1.0-rc.2 #### New - [New] SVN webhook interface switch [link](http://github.com/TencentBlueKing/bk-ci/issues/9302) @@ -440,7 +448,8 @@ - [Fix] Fix openapi startup error [link](http://github.com/TencentBlueKing/bk-ci/issues/9997) - [Fix] After renaming the code base, using the old alias to associate the code base will result in an error [link](http://github.com/TencentBlueKing/bk-ci/issues/9984) -# v2.1.0-rc.2(2024-02-22) +# v2.1.0-rc.2 +## 2024-02-22 ## Changelog since v2.1.0-rc.1 #### New - [New] Migration logic optimization [link](http://github.com/TencentBlueKing/bk-ci/issues/10014) @@ -467,7 +476,8 @@ - [Fix] Matrix variables are not replaced after opening new expression [link](http://github.com/TencentBlueKing/bk-ci/issues/9914) - [Fix] The plugin pauses and the dependent redis cache fails to function properly [link](http://github.com/TencentBlueKing/bk-ci/issues/9913) -# v2.1.0-rc.1(2024-01-16) +# v2.1.0-rc.1 +## 2024-01-16 ## Changelog since v2.0.0 #### New - [New] Pipeline archive [link](http://github.com/TencentBlueKing/bk-ci/issues/9397) diff --git a/CHANGELOG/en/CHANGELOG-3.0.md b/CHANGELOG/en/CHANGELOG-3.0.md index fc514b2c4ed..077bfeb3e5f 100644 --- a/CHANGELOG/en/CHANGELOG-3.0.md +++ b/CHANGELOG/en/CHANGELOG-3.0.md @@ -1,7 +1,7 @@ -- [v3.0.0](#v3002024-09-10) +- [v3.0.0](#v300) - [Changelog since v2.1.0](#changelog-since-v210) -- [v3.0.0-rc.1](#v300-rc12024-09-10) +- [v3.0.0-rc.1](#v300-rc1) - [Changelog since v2.1.0](#changelog-since-v210) @@ -9,7 +9,8 @@ -# v3.0.0(2024-09-10) +# v3.0.0 +## 2024-09-10 ## Changelog since v2.1.0 #### New ##### Pipeline @@ -224,7 +225,8 @@ - [Fix] Synchronize difference code [link](http://github.com/TencentBlueKing/bk-ci/issues/10319) - [Fix] Fix npm dependency vulnerability [link](http://github.com/TencentBlueKing/bk-ci/issues/10604) -# v3.0.0-rc.1(2024-09-10) +# v3.0.0-rc.1 +## 2024-09-10 ## Changelog since v2.1.0 #### New ##### Pipeline diff --git a/CHANGELOG/genBundledVersionLog.py b/CHANGELOG/genBundledVersionLog.py index ecee98eb44c..3d887ff5523 100644 --- a/CHANGELOG/genBundledVersionLog.py +++ b/CHANGELOG/genBundledVersionLog.py @@ -24,6 +24,7 @@ "requestId": None } DEFAULT_LANGUAGE = "zh_CN" +time_pattern = r'\d{4}-\d{2}-\d{2}' # 获取版本类型, 0-输出所有版本, 1-输出release版本, 2-输出rc版本 def getVersionType(): @@ -32,20 +33,10 @@ def getVersionType(): versionType = sys.argv[1] return int(versionType) -def extract_version_and_time(version_title): - index = version_title.find("(") - # 版本号上没有时间戳 - if index == -1: - version_name = version_title - date = "" - else: - version_name = version_title[0:index] - date = version_title[index + 1: len(version_title) - 1] - return version_name, date - def extract_title_and_content(changelog_content): sections_data = [] current_heading = None + current_time = "" current_content = [] lines = changelog_content.split('\n') @@ -53,16 +44,16 @@ def extract_title_and_content(changelog_content): line = line.rstrip() if line.startswith('# '): if current_heading: - version_name, time = extract_version_and_time(current_heading) - sections_data.append((version_name, time , '\n'.join(current_content))) + sections_data.append((current_heading, current_time , '\n'.join(current_content))) current_heading = line[2:] current_content = [] + elif line.startswith('## ') and re.search(time_pattern, line) and current_heading: + current_time = line[3:] elif current_heading: current_content.append(line) if current_heading: - version_name, time = extract_version_and_time(current_heading) - sections_data.append((version_name, time, '\n'.join(current_content))) + sections_data.append((current_heading, current_time, '\n'.join(current_content))) return sections_data def process(data, path): diff --git a/CHANGELOG/zh_CN/CHANGELOG-2.0.md b/CHANGELOG/zh_CN/CHANGELOG-2.0.md index fa6b17b43a1..fd7890931d9 100644 --- a/CHANGELOG/zh_CN/CHANGELOG-2.0.md +++ b/CHANGELOG/zh_CN/CHANGELOG-2.0.md @@ -1,17 +1,20 @@ -- [v2.0.4](#v2042024-10-18) +- [v2.0.5](#v205) + - [Changelog since v2.0.4](#changelog-since-v204) + +- [v2.0.4](#v204) - [Changelog since v2.0.3](#changelog-since-v203) -- [v2.0.3](#v2032024-05-28) +- [v2.0.3](#v203) - [Changelog since v2.0.2](#changelog-since-v202) -- [v2.0.2](#v2022024-03-04) +- [v2.0.2](#v202) - [Changelog since v2.0.1](#changelog-since-v201) -- [v2.0.1](#v2012024-03-01) +- [v2.0.1](#v201) - [Changelog since v2.0.0](#changelog-since-v200) -- [v2.0.0](#v2002024-01-02) +- [v2.0.0](#v200) - [Changelog since v1.14.0](#changelog-since-v1140) @@ -19,12 +22,20 @@ -# v2.0.4(2024-10-18) +# v2.0.5 +## 2024-10-29 +## Changelog since v2.0.4 +#### 新增 +- [新增] 最大可授权范围更改无效修复 [链接](http://github.com/TencentBlueKing/bk-ci/issues/11153) + +# v2.0.4 +## 2024-10-18 ## Changelog since v2.0.3 #### 修复 - [修复] 归档报告插件创建token没有实现 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10693) -# v2.0.3(2024-05-28) +# v2.0.3 +## 2024-05-28 ## Changelog since v2.0.2 #### 新增 - [新增] 登录失效弹窗规范修改 [链接](http://github.com/TencentBlueKing/bk-ci/issues/8125) @@ -33,17 +44,20 @@ - [修复] 开源社区,项目管理界面 开源版权限需放开 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10382) - [修复] 社区版simple权限中心前端应该隐藏最大授权范围 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10040) -# v2.0.2(2024-03-04) +# v2.0.2 +## 2024-03-04 ## Changelog since v2.0.1 #### 新增 - [新增] 初始化bkrepo可以修改httpSchema [链接](http://github.com/TencentBlueKing/bk-ci/issues/10056) -# v2.0.1(2024-03-01) +# v2.0.1 +## 2024-03-01 ## Changelog since v2.0.0 #### 修复 - [修复] 修复新建项目失败 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10045) -# v2.0.0(2024-01-02) +# v2.0.0 +## 2024-01-02 ## Changelog since v1.14.0 #### 新增 - [新增] 蓝盾对接权限中心RBAC [链接](http://github.com/TencentBlueKing/bk-ci/issues/7794) diff --git a/CHANGELOG/zh_CN/CHANGELOG-2.1.md b/CHANGELOG/zh_CN/CHANGELOG-2.1.md index 927e51142d1..b15d57d8f17 100644 --- a/CHANGELOG/zh_CN/CHANGELOG-2.1.md +++ b/CHANGELOG/zh_CN/CHANGELOG-2.1.md @@ -1,35 +1,35 @@ -- [v2.1.4](#v2142024-10-16) +- [v2.1.4](#v214) - [Changelog since v2.1.3](#changelog-since-v213) --[v2.1.3](#v2132024-05-28) +- [v2.1.3](#v213) - [Changelog since v2.1.2](#changelog-since-v212) -- [v2.1.2](#v2122024-05-20) +- [v2.1.2](#v212) - [Changelog since v2.1.1](#changelog-since-v211) -- [v2.1.1](#v2112024-04-26) +- [v2.1.1](#v211) - [Changelog since v2.1.0](#changelog-since-v210) -- [v2.1.0](#v2102024-04-22) +- [v2.1.0](#v210) - [Changelog since v2.0.0](#changelog-since-v200) -- [v2.1.0-rc.6](#v210-rc62024-04-19) +- [v2.1.0-rc.6](#v210-rc6) - [Changelog since v2.1.0-rc.5](#changelog-since-v210-rc5) -- [v2.1.0-rc.5](#v210-rc52024-04-10) +- [v2.1.0-rc.5](#v210-rc5) - [Changelog since v2.1.0-rc.4](#changelog-since-v210-rc4) -- [v2.1.0-rc.4](#v210-rc42024-03-22) +- [v2.1.0-rc.4](#v210-rc4) - [Changelog since v2.1.0-rc.3](#changelog-since-v210-rc3) -- [v2.1.0-rc.3](#v210-rc32024-03-07) +- [v2.1.0-rc.3](#v210-rc3) - [Changelog since v2.1.0-rc.2](#changelog-since-v210-rc2) -- [v2.1.0-rc.2](#v210-rc22024-02-22) +- [v2.1.0-rc.2](#v210-rc2) - [Changelog since v2.1.0-rc.1](#changelog-since-v210-rc1) -- [v2.1.0-rc.1](#v210-rc12024-01-16) +- [v2.1.0-rc.1](#v210-rc1) - [Changelog since v2.0.0](#changelog-since-v200) @@ -37,12 +37,14 @@ -# v2.1.4(2024-10-16) +# v2.1.4 +## 2024-10-16 ## Changelog since v2.1.3 #### 修复 - [修复] 归档报告插件创建token没有实现 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10693) -# v2.1.3(2024-05-28) +# v2.1.3 +## 2024-05-28 ## Changelog since v2.1.2 #### 新增 - [新增] 登录失效弹窗规范修改 [链接](http://github.com/TencentBlueKing/bk-ci/issues/8125) @@ -50,17 +52,20 @@ #### 修复 - [修复] 开源社区,项目管理界面 开源版权限需放开 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10382) -# v2.1.2(2024-05-20) +# v2.1.2 +## 2024-05-20 ## Changelog since v2.1.1 #### 修复 - [修复] [社区]上架失败&流水线执行页面白屏问题[v2.1.0+] [链接](http://github.com/TencentBlueKing/bk-ci/issues/10357) -# v2.1.1(2024-04-26) +# v2.1.1 +## 2024-04-26 ## Changelog since v2.1.0 #### 修复 - [修复] 2.1版本process服务启动失败 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10271) -# v2.1.0(2024-04-22) +# v2.1.0 +## 2024-04-22 ## Changelog since v2.0.0 #### 新增 - [新增] Docker构建机支持拓展资源调度 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10162) @@ -332,7 +337,8 @@ - [修复] openapi 判断是否项目成员没有根据项目路由 [链接](http://github.com/TencentBlueKing/bk-ci/issues/9427) - [修复] devcloud类型登录调试,窗口大小无法自适应 [链接](http://github.com/TencentBlueKing/bk-ci/issues/9418) - [修复] 共享凭据不需要依赖插件敏感接口权限校验 [链接](http://github.com/TencentBlueKing/bk-ci/issues/9398) -# v2.1.0-rc.6(2024-04-19) +# v2.1.0-rc.6 +## 2024-04-19 ## Changelog since v2.1.0-rc.5 #### 新增 - [新增] Docker构建机支持拓展资源调度 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10162) @@ -358,7 +364,8 @@ - [修复] stream 流水线下,刷新页面加载了全量的列表 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10152) - [修复] 修复申请权限跳转特殊符号转义问题 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10188) -# v2.1.0-rc.5(2024-04-10) +# v2.1.0-rc.5 +## 2024-04-10 ## Changelog since v2.1.0-rc.4 #### 新增 - [新增] 创建/编辑项目openapi增加运营产品必填检查 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10088) @@ -389,7 +396,8 @@ - [修复] 修复代码库权限丢失问题 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10131) - [修复] 全量交接权限存在Bug [链接](http://github.com/TencentBlueKing/bk-ci/issues/10117) -# v2.1.0-rc.4(2024-03-22) +# v2.1.0-rc.4 +## 2024-03-22 ## Changelog since v2.1.0-rc.3 #### 新增 - [新增] 公共构建机支持持久化构建容器调度 [链接](http://github.com/TencentBlueKing/bk-ci/issues/9269) @@ -413,7 +421,8 @@ - [修复] 删除流水线时,代码库关联关系未删除 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10111) - [修复] 定时触发插件检查SVN代码库最新版本时, 无需调用会话接口 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10096) -# v2.1.0-rc.3(2024-03-07) +# v2.1.0-rc.3 +## 2024-03-07 ## Changelog since v2.1.0-rc.2 #### 新增 - [新增] svn webhook接口切换 [链接](http://github.com/TencentBlueKing/bk-ci/issues/9302) @@ -447,7 +456,8 @@ - [修复] 修复openapi启动报错 [链接](http://github.com/TencentBlueKing/bk-ci/issues/9997) - [修复] 重命名代码库以后, 使用旧别名关联代码库会报错 [链接](http://github.com/TencentBlueKing/bk-ci/issues/9984) -# v2.1.0-rc.2(2024-02-22) +# v2.1.0-rc.2 +## 2024-02-22 ## Changelog since v2.1.0-rc.1 #### 新增 - [新增] 迁移逻辑优化 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10014) @@ -474,7 +484,8 @@ - [修复] 开启了新表达式矩阵变量没有替换 [链接](http://github.com/TencentBlueKing/bk-ci/issues/9914) - [修复] 插件暂停依赖的redis缓存失效时功能异常 [链接](http://github.com/TencentBlueKing/bk-ci/issues/9913) -# v2.1.0-rc.1(2024-01-16) +# v2.1.0-rc.1 +## 2024-01-16 ## Changelog since v2.0.0 #### 新增 - [新增] 流水线归档 [链接](http://github.com/TencentBlueKing/bk-ci/issues/9397) diff --git a/CHANGELOG/zh_CN/CHANGELOG-3.0.md b/CHANGELOG/zh_CN/CHANGELOG-3.0.md index 3583e006f3c..652fd5d6acb 100644 --- a/CHANGELOG/zh_CN/CHANGELOG-3.0.md +++ b/CHANGELOG/zh_CN/CHANGELOG-3.0.md @@ -1,7 +1,7 @@ -- [v3.0.0](#v3002024-09-10) +- [v3.0.0](#v300) - [Changelog since v2.1.0](#changelog-since-v210) -- [v3.0.0-rc.1](#v300-rc12024-09-10) +- [v3.0.0-rc.1](#v300-rc1) - [Changelog since v2.1.0](#changelog-since-v210) @@ -9,7 +9,8 @@ -# v3.0.0(2024-09-10) +# v3.0.0 +## 2024-09-10 ## Changelog since v2.1.0 #### 新增 ##### 流水线 @@ -224,7 +225,8 @@ - [修复] 同步差异代码 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10319) - [修复] 修复npm依赖漏洞 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10604) -# v3.0.0-rc.1(2024-09-10) +# v3.0.0-rc.1 +## 2024-09-10 ## Changelog since v2.1.0 #### 新增 ##### 流水线 diff --git a/CHANGELOG/zh_CN/CHANGELOG-3.1.md b/CHANGELOG/zh_CN/CHANGELOG-3.1.md index 00deffc38ac..c84e76c5bb0 100644 --- a/CHANGELOG/zh_CN/CHANGELOG-3.1.md +++ b/CHANGELOG/zh_CN/CHANGELOG-3.1.md @@ -1,4 +1,7 @@ +- [v3.1.0-rc.2](#v310-rc2) + - [Changelog since v3.1.0-rc.1](#changelog-since-v310-rc1) + - [v3.1.0-rc.1](#v310-rc1) - [Changelog since v3.0.0](#changelog-since-v300) @@ -7,7 +10,67 @@ +# v3.1.0-rc.2 +## 2024-10-26 +## Changelog since v3.1.0-rc.1 +#### 新增 +##### 流水线 +- [新增] 推荐版本号优化 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10958) +- [新增] 支持流水线指标监控 [链接](http://github.com/TencentBlueKing/bk-ci/issues/9860) +- [新增] 流水线列表,增加标签展示 [链接](http://github.com/TencentBlueKing/bk-ci/issues/11054) +- [新增] 模版管理-列表支持展示字段和排序优化 [链接](http://github.com/TencentBlueKing/bk-ci/issues/11056) +- [新增] 源材料展示优化 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10733) +- [新增] Post action 中支持获取父任务ID [链接](http://github.com/TencentBlueKing/bk-ci/issues/10968) +- [新增] stage 审核支持 checklist 确认场景 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10920) +- [新增] AI大模型融入 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10825) +- [新增] 丰富流水线-stage准入的审核功能,支持配置角色或用户组作为审核人 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10689) +- [新增] 流水线日志支持AI修复 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10913) +- [新增] 流水线并发运行时,支持限制并发个数和排队 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10718) +- [新增] 插件管理菜单对应插件列表增加默认公共插件显示 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10472) +- [新增] 当策略为「锁定构建号」时,执行界面可以修改当前值 [链接](http://github.com/TencentBlueKing/bk-ci/issues/11089) +- [新增] 社区版流水线完成通知,支持通知组 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10976) +- [新增] Job 互斥组排队时,队列长度支持最长到 50 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10975) +- [新增] 触发事件重放操作权限控制 [链接](http://github.com/TencentBlueKing/bk-ci/issues/11052) +- [新增] 通过子流水线调用触发的执行,支持重试 [链接](http://github.com/TencentBlueKing/bk-ci/issues/11015) +- [新增] UI方式下的「所有参数满足条件时执行」和「所有参数满足条件时不执行」转为Code 优化 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10930) +- [新增] MR 事件触发器支持 WIP [链接](http://github.com/TencentBlueKing/bk-ci/issues/10683) +- [新增] 工蜂 MR 触发增加 action=edit [链接](http://github.com/TencentBlueKing/bk-ci/issues/11024) +##### 代码库 +- [新增] 增加获取工蜂和github oauth url的build接口 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10826) +##### 权限中心 +- [新增] 同步并分表存储资源组权限数据 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10964) +- [新增] 创建自定义组并赋予组组权限 [链接](http://github.com/TencentBlueKing/bk-ci/issues/11026) +##### 环境管理 +- [新增] 第三方构建机支持使用 dcoker 运行构建任务 [链接](http://github.com/TencentBlueKing/bk-ci/issues/9820) +- [新增] 第三方构建机DockerUi界面支持 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10962) +##### Openapi +- [新增] OpenApi提供转发Turbo编译加速上报资源统计数据接口 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10508) +##### 其他 +- [新增] 支持查看版本日志 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10938) +- [新增] 优化AESUtil [链接](http://github.com/TencentBlueKing/bk-ci/issues/11084) +- [新增] sql doc 文档更新 [链接](http://github.com/TencentBlueKing/bk-ci/issues/9974) +- [新增] 引擎等MQ场景接入SCS框架 [链接](http://github.com/TencentBlueKing/bk-ci/issues/7443) + +#### 优化 +##### 研发商店 +- [优化] 研发商店组件指标数据字段优化 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10219) +##### Stream +- [优化] [stream] 存留问题优化 [链接](http://github.com/TencentBlueKing/bk-ci/issues/11045) + +#### 修复 +##### 流水线 +- [修复] 某些构建场景下插入T_PIPELINE_BUILD_RECORD_TASK表的CONTAINER_ID字段值错误 [链接](http://github.com/TencentBlueKing/bk-ci/issues/11029) +- [修复] 触发器条件引入${{variables.xxx}}变量触发不了流水线 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10987) +- [修复] 触发器变量补充 [链接](http://github.com/TencentBlueKing/bk-ci/issues/11002) +##### 研发商店 +- [修复] 研发商店组件包文件上传下载优化 [链接](http://github.com/TencentBlueKing/bk-ci/issues/11115) +- [修复] 修复更新组件关联初始化项目信息时,新增调试项目记录时未成功把旧的调试项目记录清理 [链接](http://github.com/TencentBlueKing/bk-ci/issues/11011) +##### 制品库 +- [修复] 归档报告插件创建token没有实现 [链接](http://github.com/TencentBlueKing/bk-ci/issues/10693) + + # v3.1.0-rc.1 +## 2024-10-15 ## Changelog since v3.0.0 #### 新增 ##### 流水线 diff --git a/docs/overview/db/devops_ci_auth.md b/docs/overview/db/devops_ci_auth.md index ae8fd164cb8..246058ced55 100644 --- a/docs/overview/db/devops_ci_auth.md +++ b/docs/overview/db/devops_ci_auth.md @@ -32,6 +32,7 @@ | T_AUTH_RESOURCE_GROUP_APPLY | 用户组申请记录表 | | T_AUTH_RESOURCE_GROUP_CONFIG | 资源用户组配置表 | | T_AUTH_RESOURCE_GROUP_MEMBER | 资源组成员 | +| T_AUTH_RESOURCE_GROUP_PERMISSION | 资源组权限表 | | T_AUTH_RESOURCE_SYNC | 同步 IAM 资源 | | T_AUTH_RESOURCE_TYPE | 权限资源类型表 | | T_AUTH_STRATEGY | 权限策略表 | @@ -276,11 +277,12 @@ | 1 | ACCESS_TOKEN | varchar | 64 | 0 | N | N | | ACCESS_TOKEN | | 2 | CLIENT_ID | varchar | 32 | 0 | N | N | | 客户端 ID | | 3 | USER_NAME | varchar | 32 | 0 | Y | N | | 登录的用户名,客户端模式该值为空 | -| 4 | GRANT_TYPE | varchar | 32 | 0 | N | N | | 授权模式 | -| 5 | EXPIRED_TIME | bigint | 20 | 0 | N | N | | 过期时间 | -| 6 | REFRESH_TOKEN | varchar | 64 | 0 | Y | N | | REFRESH_TOKEN,客户端模式该值为空 | -| 7 | SCOPE_ID | int | 10 | 0 | N | N | | 授权范围 ID | -| 8 | CREATE_TIME | datetime | 19 | 0 | N | N | CURRENT_TIMESTAMP | 创建时间 | +| 4 | PASS_WORD | varchar | 64 | 0 | Y | N | | 用于密码模式 | +| 5 | GRANT_TYPE | varchar | 32 | 0 | N | N | | 授权模式 | +| 6 | EXPIRED_TIME | bigint | 20 | 0 | N | N | | 过期时间 | +| 7 | REFRESH_TOKEN | varchar | 64 | 0 | Y | N | | REFRESH_TOKEN,客户端模式该值为空 | +| 8 | SCOPE_ID | int | 10 | 0 | N | N | | 授权范围 ID | +| 9 | CREATE_TIME | datetime | 19 | 0 | N | N | CURRENT_TIMESTAMP | 创建时间 | **表名:** T_AUTH_OAUTH2_CLIENT_DETAILS @@ -479,6 +481,29 @@ | 11 | CREATE_TIME | datetime | 19 | 0 | N | N | CURRENT_TIMESTAMP | 创建时间 | | 12 | UPDATE_TIME | datetime | 19 | 0 | N | N | CURRENT_TIMESTAMP | 更新时间 | +**表名:** T_AUTH_RESOURCE_GROUP_PERMISSION + +**说明:** 资源组权限表 + +**数据列:** + +| 序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 | +| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | +| 1 | ID | bigint | 20 | 0 | N | Y | | 主键 ID | +| 2 | PROJECT_CODE | varchar | 64 | 0 | N | N | | 项目 ID | +| 3 | RESOURCE_TYPE | varchar | 32 | 0 | N | N | | 用户组关联的资源类型 | +| 4 | RESOURCE_CODE | varchar | 255 | 0 | N | N | | 用户组关联的资源 ID | +| 5 | IAM_RESOURCE_CODE | varchar | 32 | 0 | N | N | | 用户组关联的 IAM 资源 ID | +| 6 | GROUP_CODE | varchar | 32 | 0 | N | N | | 用户组标识 | +| 7 | IAM_GROUP_ID | int | 10 | 0 | N | N | | 关联的 IAM 组 ID | +| 8 | ACTION | varchar | 64 | 0 | N | N | | 操作 ID | +| 9 | ACTION_RELATED_RESOURCE_TYPE | varchar | 32 | 0 | N | N | | 动作关联的资源类型 | +| 10 | RELATED_RESOURCE_TYPE | varchar | 32 | 0 | N | N | | 组权限关联的资源类型 | +| 11 | RELATED_RESOURCE_CODE | varchar | 255 | 0 | N | N | | 组权限关联的资源 ID | +| 12 | RELATED_IAM_RESOURCE_CODE | varchar | 255 | 0 | N | N | | 组权限关联的资源 ID | +| 13 | CREATE_TIME | datetime | 19 | 0 | N | N | CURRENT_TIMESTAMP | 创建时间 | +| 14 | UPDATE_TIME | datetime | 19 | 0 | N | N | CURRENT_TIMESTAMP | 更新时间 | + **表名:** T_AUTH_RESOURCE_SYNC **说明:** 同步 IAM 资源 diff --git a/docs/overview/db/devops_ci_plugin.md b/docs/overview/db/devops_ci_plugin.md index ac496d2a91b..6500a0ec287 100644 --- a/docs/overview/db/devops_ci_plugin.md +++ b/docs/overview/db/devops_ci_plugin.md @@ -7,9 +7,29 @@ **文档描述:** devops_ci_plugin 的数据库文档 | 表名 | 说明 | | :---: | :---: | +| T_AI_SCORE | 脚本执行报错 AI 分析-评分 | | T_PLUGIN_GITHUB_CHECK | | | T_PLUGIN_GIT_CHECK | | +**表名:** T_AI_SCORE + +**说明:** 脚本执行报错 AI 分析-评分 + +**数据列:** + +| 序号 | 名称 | 数据类型 | 长度 | 小数位 | 允许空值 | 主键 | 默认值 | 说明 | +| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | +| 1 | ID | bigint | 20 | 0 | N | Y | | 主键 ID | +| 2 | LABEL | varchar | 256 | 0 | N | N | | 任务 ID | +| 3 | ARCHIVE | bit | 1 | 0 | N | N | 0 | 是否已归档 | +| 4 | CREATE_TIME | datetime | 19 | 0 | N | N | CURRENT_TIMESTAMP | 创建时间 | +| 5 | UPDATE_TIME | datetime | 19 | 0 | N | N | CURRENT_TIMESTAMP | 更新时间 | +| 6 | GOOD_USERS | text | 65535 | 0 | Y | N | | 赞的人 | +| 7 | BAD_USERS | text | 65535 | 0 | Y | N | | 踩的人 | +| 8 | AI_MSG | text | 65535 | 0 | Y | N | | 大模型生成的内容 | +| 9 | SYSTEM_MSG | text | 65535 | 0 | Y | N | | Promptforsystem | +| 10 | USER_MSG | text | 65535 | 0 | Y | N | | Promptforuser | + **表名:** T_PLUGIN_GITHUB_CHECK **说明:** diff --git a/docs/overview/db/devops_ci_process.md b/docs/overview/db/devops_ci_process.md index 26fcaa288af..8f714dcff53 100644 --- a/docs/overview/db/devops_ci_process.md +++ b/docs/overview/db/devops_ci_process.md @@ -427,6 +427,7 @@ | 11 | CONDITIONS | mediumtext | 16777215 | 0 | Y | N | | 状况 | | 12 | CHECK_IN | mediumtext | 16777215 | 0 | Y | N | | 准入检查配置 | | 13 | CHECK_OUT | mediumtext | 16777215 | 0 | Y | N | | 准出检查配置 | +| 14 | STAGE_ID_FOR_USER | varchar | 64 | 0 | Y | N | | 当前 stageId 阶段 ID(用户可编辑) | **表名:** T_PIPELINE_BUILD_SUMMARY @@ -834,7 +835,7 @@ | 24 | SUCCESS_WECHAT_GROUP_MARKDOWN_FLAG | bit | 1 | 0 | N | N | b'0' | 成功的企业微信群通知转为 Markdown 格式开关 | | 25 | FAIL_WECHAT_GROUP_MARKDOWN_FLAG | bit | 1 | 0 | N | N | b'0' | 失败的企业微信群通知转为 Markdown 格式开关 | | 26 | MAX_PIPELINE_RES_NUM | int | 10 | 0 | Y | N | 500 | 保存流水线编排的最大个数 | -| 27 | MAX_CON_RUNNING_QUEUE_SIZE | int | 10 | 0 | Y | N | 50 | 并发构建数量限制 | +| 27 | MAX_CON_RUNNING_QUEUE_SIZE | int | 10 | 0 | Y | N | | 并发构建数量限制,为 null 时表示取系统默认值 | | 28 | BUILD_NUM_RULE | varchar | 512 | 0 | Y | N | | 构建号生成规则 | | 29 | CONCURRENCY_GROUP | varchar | 255 | 0 | Y | N | | 并发时,设定的 group | | 30 | CONCURRENCY_CANCEL_IN_PROGRESS | bit | 1 | 0 | Y | N | b'0' | 并发时,是否相同 group 取消正在执行的流水线 | @@ -885,6 +886,7 @@ | 31 | FAIL_CONTENT | longtext | 2147483647 | 0 | Y | N | | | | 32 | SUCCESS_WECHAT_GROUP_MARKDOWN_FLAG | bit | 1 | 0 | N | N | b'0' | | | 33 | FAIL_WECHAT_GROUP_MARKDOWN_FLAG | bit | 1 | 0 | Y | N | b'0' | | +| 34 | MAX_CON_RUNNING_QUEUE_SIZE | int | 10 | 0 | Y | N | | 并发构建数量限制,值为-1 时表示取系统默认值。 | **表名:** T_PIPELINE_STAGE_TAG diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/ServiceGroupResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/ServiceGroupResource.kt deleted file mode 100644 index 440ecb2523b..00000000000 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/ServiceGroupResource.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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.devops.auth.api - -import com.tencent.devops.auth.pojo.dto.GroupDTO -import com.tencent.devops.common.api.auth.AUTH_HEADER_USER_ID -import com.tencent.devops.common.api.pojo.Result -import io.swagger.v3.oas.annotations.tags.Tag -import io.swagger.v3.oas.annotations.Operation -import io.swagger.v3.oas.annotations.Parameter -import javax.ws.rs.Consumes -import javax.ws.rs.HeaderParam -import javax.ws.rs.POST -import javax.ws.rs.Path -import javax.ws.rs.PathParam -import javax.ws.rs.Produces -import javax.ws.rs.core.MediaType - -@Tag(name = "AUTH_GROUP", description = "权限-用户组") -@Path("/service/auth/group") -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -interface ServiceGroupResource { - - @POST - @Path("/projectCodes/{projectCode}/") - @Operation(summary = "项目下添加指定组") - fun createGroup( - @Parameter(description = "用户名", required = true) - @HeaderParam(AUTH_HEADER_USER_ID) - userId: String, - @Parameter(description = "项目标识", required = true) - @PathParam("projectCode") - projectCode: String, - @Parameter(description = "用户组信息", required = true) - groupInfo: GroupDTO - ): Result - - @POST - @Path("/projectCodes/{projectCode}/batchCreate") - @Operation(summary = "项目下添加指定组") - fun batchCreateGroup( - @Parameter(description = "用户名", required = true) - @HeaderParam(AUTH_HEADER_USER_ID) - userId: String, - @Parameter(description = "项目标识", required = true) - @PathParam("projectCode") - projectCode: String, - @Parameter(description = "用户组信息", required = true) - groupInfos: List - ): Result -} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/ServiceUserGroupResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/ServiceUserGroupResource.kt deleted file mode 100644 index a297c2c1c29..00000000000 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/ServiceUserGroupResource.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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.devops.auth.api - -import com.tencent.devops.common.api.auth.AUTH_HEADER_USER_ID -import com.tencent.devops.common.api.pojo.Result -import io.swagger.v3.oas.annotations.tags.Tag -import io.swagger.v3.oas.annotations.Operation -import io.swagger.v3.oas.annotations.Parameter -import javax.ws.rs.Consumes -import javax.ws.rs.POST -import javax.ws.rs.Path -import javax.ws.rs.PathParam -import javax.ws.rs.Produces -import javax.ws.rs.core.MediaType - -@Tag(name = "AUTH_USER_GROUP", description = "权限-用户-用户组") -@Path("/service/auth/userGroup") -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -interface ServiceUserGroupResource { - - @POST - @Path("/users/{userId}/groupIds/{groupId}") - @Operation(summary = "添加用户到指定组") - fun addUser2Group( - @Parameter(description = "用户名", required = true) - @PathParam(AUTH_HEADER_USER_ID) - userId: String, - @Parameter(description = "用户组Id", required = true) - @PathParam("groupId") - groupId: Int - ): Result -} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/migrate/OpAuthMigrateResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/migrate/OpAuthMigrateResource.kt index 868c8d533cc..98d9fb98965 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/migrate/OpAuthMigrateResource.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/migrate/OpAuthMigrateResource.kt @@ -165,4 +165,12 @@ interface OpAuthMigrateResource { @Parameter(description = "迁移项目", required = true) projectCodes: List ): Result + + @POST + @Path("/enablePipelineListPermissionControl") + @Operation(summary = "开启流水线列表权限控制") + fun enablePipelineListPermissionControl( + @Parameter(description = "项目", required = true) + projectCodes: List + ): Result } diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/callback/OpCallBackResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/op/OpCallBackResource.kt similarity index 98% rename from src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/callback/OpCallBackResource.kt rename to src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/op/OpCallBackResource.kt index 363eecb272f..26167b0f38b 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/callback/OpCallBackResource.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/op/OpCallBackResource.kt @@ -25,7 +25,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.api.callback +package com.tencent.devops.auth.api.op import com.tencent.devops.auth.pojo.IamCallBackInfo import com.tencent.devops.auth.pojo.IamCallBackInterfaceDTO diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/open/OpenPermissionAuthResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/open/OpenPermissionAuthResource.kt new file mode 100644 index 00000000000..9ccfe984dae --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/open/OpenPermissionAuthResource.kt @@ -0,0 +1,374 @@ +/* + * 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.devops.auth.api.open + +import com.tencent.devops.common.api.auth.AUTH_HEADER_DEVOPS_BK_TOKEN +import com.tencent.devops.common.api.auth.AUTH_HEADER_DEVOPS_USER_ID +import com.tencent.devops.common.api.auth.AUTH_HEADER_GIT_TYPE +import com.tencent.devops.common.api.pojo.Result +import com.tencent.devops.common.auth.api.AuthPermission +import com.tencent.devops.common.auth.api.pojo.AuthResourceInstance +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter +import io.swagger.v3.oas.annotations.tags.Tag +import javax.ws.rs.Consumes +import javax.ws.rs.DELETE +import javax.ws.rs.GET +import javax.ws.rs.HeaderParam +import javax.ws.rs.POST +import javax.ws.rs.PUT +import javax.ws.rs.Path +import javax.ws.rs.PathParam +import javax.ws.rs.Produces +import javax.ws.rs.QueryParam +import javax.ws.rs.core.MediaType + +@Tag(name = "AUTH_OPEN_PERMISSION", description = "权限--权限校验以及资源操作相关接口") +@Path("/open/service/auth/permission") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@SuppressWarnings("LongParameterList") +interface OpenPermissionAuthResource { + + @GET + @Path("/projects/{projectCode}/action/validate") + @Operation(summary = "校验用户是否有具体操作的权限") + fun validateUserActionPermission( + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + @Parameter(description = "待校验用户ID", required = true) + userId: String, + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @HeaderParam(AUTH_HEADER_GIT_TYPE) + @Parameter(description = "系统类型") + type: String? = null, + @QueryParam("action") + @Parameter(description = "资源类型", required = true) + action: String + ): Result + + @GET + @Path("/projects/{projectCode}/resource/validate") + @Operation(summary = "校验用户是否有具体资源的操作权限") + fun validateUserResourcePermission( + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + @Parameter(description = "待校验用户ID", required = true) + userId: String, + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @HeaderParam(AUTH_HEADER_GIT_TYPE) + @Parameter(description = "系统类型") + type: String? = null, + @QueryParam("action") + @Parameter(description = "资源类型", required = true) + action: String, + @QueryParam("projectCode") + @Parameter(description = "项目编码", required = true) + projectCode: String, + // 此处resourceCode实际为resourceType + @QueryParam("resourceCode") + @Parameter(description = "资源类型", required = false) + resourceCode: String? + ): Result + + @GET + @Path("/projects/{projectCode}/relation/validate") + @Operation(summary = "校验用户是否有具体资源实例的操作权限") + fun validateUserResourcePermissionByRelation( + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + @Parameter(description = "待校验用户ID", required = true) + userId: String, + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @HeaderParam(AUTH_HEADER_GIT_TYPE) + @Parameter(description = "系统类型") + type: String? = null, + @QueryParam("action") + @Parameter(description = "action类型", required = true) + action: String, + @PathParam("projectCode") + @Parameter(description = "项目Code", required = true) + projectCode: String, + @QueryParam("resourceCode") + @Parameter(description = "资源code", required = true) + resourceCode: String, + @QueryParam("resourceType") + @Parameter(description = "资源类型", required = true) + resourceType: String, + @QueryParam("relationResourceType") + @Parameter(description = "关联资源,一般为Project", required = false) + relationResourceType: String? = null + ): Result + + @POST + @Path("/projects/{projectCode}/instance/validate") + @Operation(summary = "校验用户是否有具体资源实例的操作权限") + fun validateUserResourcePermissionByInstance( + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + @Parameter(description = "待校验用户ID", required = true) + userId: String, + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @HeaderParam(AUTH_HEADER_GIT_TYPE) + @Parameter(description = "系统类型") + type: String? = null, + @QueryParam("action") + @Parameter(description = "action类型", required = true) + action: String, + @PathParam("projectCode") + @Parameter(description = "项目Code", required = true) + projectCode: String, + resource: AuthResourceInstance + ): Result + + @POST + @Path("/projects/{projectCode}/relation/validate/batch") + @Operation(summary = "批量校验用户是否有具体资源实例的操作权限") + fun batchValidateUserResourcePermissionByRelation( + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + @Parameter(description = "待校验用户ID", required = true) + userId: String, + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @HeaderParam(AUTH_HEADER_GIT_TYPE) + @Parameter(description = "系统类型") + type: String? = null, + @PathParam("projectCode") + @Parameter(description = "项目Code", required = true) + projectCode: String, + @QueryParam("resourceCode") + @Parameter(description = "资源code", required = true) + resourceCode: String, + @QueryParam("resourceType") + @Parameter(description = "资源类型", required = true) + resourceType: String, + @QueryParam("relationResourceType") + @Parameter(description = "关联资源,一般为Project", required = false) + relationResourceType: String? = null, + @Parameter(description = "action类型列表", required = true) + action: List + ): Result + + @GET + @Path("/projects/{projectCode}/action/instanceAndParent") + @Operation(summary = "获取用户所拥有指定权限下的指定类型资源和类型父资源code列表") + fun getUserResourceAndParentByPermission( + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + @Parameter(description = "待校验用户ID", required = true) + userId: String, + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @HeaderParam(AUTH_HEADER_GIT_TYPE) + @Parameter(description = "系统类型") + type: String? = null, + @QueryParam("action") + @Parameter(description = "action类型") + action: String, + @PathParam("projectCode") + @Parameter(description = "项目Code", required = true) + projectCode: String, + @QueryParam("resourceType") + @Parameter(description = "资源类型") + resourceType: String + ): Result>> + + @GET + @Path("/projects/{projectCode}/actions/instance/map") + @Operation(summary = "获取用户某项目下多操作的资源实例列表") + fun getUserResourcesByPermissions( + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + @Parameter(description = "待校验用户ID", required = true) + userId: String, + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @HeaderParam(AUTH_HEADER_GIT_TYPE) + @Parameter(description = "系统类型") + type: String? = null, + @QueryParam("action") + @Parameter(description = "action类型") + action: List, + @PathParam("projectCode") + @Parameter(description = "项目Code", required = true) + projectCode: String, + @QueryParam("resourceType") + @Parameter(description = "资源类型") + resourceType: String + ): Result>> + + @GET + @Path("/projects/{projectCode}/action/instance") + @Operation(summary = "获取用户某项目下指定操作的资源实例列表") + fun getUserResourceByPermission( + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + @Parameter(description = "待校验用户ID", required = true) + userId: String, + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @HeaderParam(AUTH_HEADER_GIT_TYPE) + @Parameter(description = "系统类型") + type: String? = null, + @QueryParam("action") + @Parameter(description = "action类型") + action: String, + @PathParam("projectCode") + @Parameter(description = "项目Code", required = true) + projectCode: String, + @QueryParam("resourceType") + @Parameter(description = "资源类型") + resourceType: String + ): Result> + + @POST + @Path("/projects/{projectCode}/actions/instance/filter") + @Operation(summary = "过滤用户某项目下多操作的资源实例列表") + fun filterUserResourcesByPermissions( + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + @Parameter(description = "待校验用户ID", required = true) + userId: String, + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @HeaderParam(AUTH_HEADER_GIT_TYPE) + @Parameter(description = "系统类型") + type: String? = null, + @QueryParam("action") + @Parameter(description = "action类型") + actions: List, + @PathParam("projectCode") + @Parameter(description = "项目Code", required = true) + projectCode: String, + @QueryParam("resourceType") + @Parameter(description = "资源类型") + resourceType: String, + resources: List + ): Result>> + + @Path("/projects/{projectCode}/create/relation") + @POST + @Operation(summary = "创建权限中心资源") + fun resourceCreateRelation( + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + @Parameter(description = "待校验用户ID", required = true) + userId: String, + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @HeaderParam(AUTH_HEADER_GIT_TYPE) + @Parameter(description = "系统类型") + type: String? = null, + @PathParam("projectCode") + @Parameter(description = "项目Id") + projectCode: String, + @QueryParam("resourceType") + @Parameter(description = "资源类型") + resourceType: String, + @QueryParam("resourceCode") + @Parameter(description = "资源Code") + resourceCode: String, + @QueryParam("resourceName") + @Parameter(description = "资源名称") + resourceName: String + ): Result + + @Path("/projects/{projectCode}/modify/relation") + @PUT + @Operation(summary = "修改权限中心资源") + fun resourceModifyRelation( + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @HeaderParam(AUTH_HEADER_GIT_TYPE) + @Parameter(description = "系统类型") + type: String? = null, + @PathParam("projectCode") + @Parameter(description = "项目Id") + projectCode: String, + @QueryParam("resourceType") + @Parameter(description = "资源类型") + resourceType: String, + @QueryParam("resourceCode") + @Parameter(description = "资源Code") + resourceCode: String, + @QueryParam("resourceName") + @Parameter(description = "资源名称") + resourceName: String + ): Result + + @Path("/projects/{projectCode}/delete/relation") + @DELETE + @Operation(summary = "删除权限中心资源") + fun resourceDeleteRelation( + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @HeaderParam(AUTH_HEADER_GIT_TYPE) + @Parameter(description = "系统类型") + type: String? = null, + @PathParam("projectCode") + @Parameter(description = "项目Id") + projectCode: String, + @QueryParam("resourceType") + @Parameter(description = "资源类型") + resourceType: String, + @QueryParam("resourceCode") + @Parameter(description = "资源Code") + resourceCode: String + ): Result + + @Path("/projects/{projectCode}/cancel/relation") + @PUT + @Operation(summary = "取消权限中心资源") + fun resourceCancelRelation( + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + @Parameter(description = "操作用户ID", required = true) + userId: String, + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @HeaderParam(AUTH_HEADER_GIT_TYPE) + @Parameter(description = "系统类型") + type: String? = null, + @PathParam("projectCode") + @Parameter(description = "项目Id") + projectCode: String, + @QueryParam("resourceType") + @Parameter(description = "资源类型") + resourceType: String, + @QueryParam("resourceCode") + @Parameter(description = "资源Code") + resourceCode: String + ): Result +} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/open/OpenProjectAuthResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/open/OpenProjectAuthResource.kt new file mode 100644 index 00000000000..f0e9bc38070 --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/open/OpenProjectAuthResource.kt @@ -0,0 +1,250 @@ +/* + * 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.devops.auth.api.open + +import com.tencent.devops.auth.pojo.vo.ProjectPermissionInfoVO +import com.tencent.devops.common.api.auth.AUTH_HEADER_DEVOPS_BK_TOKEN +import com.tencent.devops.common.api.auth.AUTH_HEADER_GIT_TYPE +import com.tencent.devops.common.api.auth.AUTH_HEADER_USER_ID +import com.tencent.devops.common.api.pojo.Result +import com.tencent.devops.common.auth.api.pojo.BKAuthProjectRolesResources +import com.tencent.devops.common.auth.api.pojo.BkAuthGroup +import com.tencent.devops.common.auth.api.pojo.BkAuthGroupAndUserList +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter +import io.swagger.v3.oas.annotations.tags.Tag +import javax.ws.rs.Consumes +import javax.ws.rs.GET +import javax.ws.rs.HeaderParam +import javax.ws.rs.POST +import javax.ws.rs.Path +import javax.ws.rs.PathParam +import javax.ws.rs.Produces +import javax.ws.rs.QueryParam +import javax.ws.rs.core.MediaType + +@Tag(name = "AUTH_SERVICE_PROJECT", description = "权限--项目相关接口") +@Path("/open/service/auth/projects") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +interface OpenProjectAuthResource { + @GET + @Path("/{projectCode}/users/byGroup") + @Operation(summary = "获取项目成员 (需要对接的权限中心支持该功能才可以)") + fun getProjectUsers( + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @HeaderParam(AUTH_HEADER_GIT_TYPE) + @Parameter(description = "系统类型") + type: String? = null, + @PathParam("projectCode") + @Parameter(description = "项目Code", required = true) + projectCode: String, + @QueryParam("group") + @Parameter(description = "用户组类型", required = false) + group: BkAuthGroup? = null + ): Result> + + @GET + @Path("/{projectCode}/users") + @Operation(summary = "拉取项目所有成员,并按项目角色组分组成员信息返回") + fun getProjectGroupAndUserList( + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @PathParam("projectCode") + @Parameter(description = "项目Code", required = true) + projectCode: String + ): Result> + + @GET + @Path("/users/{userId}") + @Operation(summary = "获取用户有管理权限的项目Code") + fun getUserProjects( + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @PathParam("userId") + @Parameter(description = "用户userId", required = true) + userId: String + ): Result> + + @GET + @Path("/users/{userId}/{action}") + @Operation(summary = "获取用户有某种项目资源类型权限的项目Code") + fun getUserProjectsByPermission( + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @PathParam("userId") + @Parameter(description = "用户userId", required = true) + userId: String, + @PathParam("action") + @Parameter(description = "项目资源类型action", required = true) + action: String, + @QueryParam("resourceType") + @Parameter(description = "资源类型", required = true) + resourceType: String? = null + ): Result> + + @GET + @Path("/{projectCode}/users/{userId}/isProjectUsers") + @Operation(summary = "判断是否某个项目中某个组角色的成员") + fun isProjectUser( + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @HeaderParam(AUTH_HEADER_GIT_TYPE) + @Parameter(description = "系统类型") + type: String? = null, + @PathParam("userId") + @Parameter(description = "用户Id", required = true) + userId: String, + @PathParam("projectCode") + @Parameter(description = "项目Code", required = true) + projectCode: String, + @QueryParam("group") + @Parameter(description = "用户组类型", required = false) + group: BkAuthGroup? = null + ): Result + + @GET + @Path("/{projectCode}/users/{userId}/checkUserInProjectLevelGroup") + @Operation(summary = "是否该用户在项目级别的组中") + fun checkUserInProjectLevelGroup( + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @HeaderParam(AUTH_HEADER_GIT_TYPE) + @PathParam("userId") + @Parameter(description = "用户Id", required = true) + userId: String, + @PathParam("projectCode") + @Parameter(description = "项目Code", required = true) + projectCode: String + ): Result + + @GET + @Path("/{projectCode}/users/{userId}/checkProjectManager") + @Operation(summary = "判断是否是项目管理员") + fun checkProjectManager( + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @HeaderParam(AUTH_HEADER_GIT_TYPE) + @Parameter(description = "系统类型") + type: String? = null, + @PathParam("userId") + @Parameter(description = "用户Id", required = true) + userId: String, + @PathParam("projectCode") + @Parameter(description = "项目Code", required = true) + projectCode: String + ): Result + + @GET + @Path("/projectIds/{projectId}/checkManager") + @Operation(summary = "判断是否是项目管理员或CI管理员") + fun checkManager( + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @Parameter(description = "用户名", required = true) + @HeaderParam(AUTH_HEADER_USER_ID) + userId: String, + @PathParam("projectId") + @Parameter(description = "项目Id", required = true) + projectId: String + ): Result + + @POST + @Path("/{projectCode}/createUser") + @Operation(summary = "添加单个用户到指定项目指定分组") + fun createProjectUser( + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @QueryParam("userId") + @Parameter(description = "用户Id", required = true) + userId: String, + @PathParam("projectCode") + @Parameter(description = "项目Code", required = true) + projectCode: String, + @QueryParam("roleCode") + @Parameter(description = "用户组Code", required = true) + roleCode: String + ): Result + + @POST + @Path("/{projectCode}/batchCreateProjectUser/{roleCode}") + @Operation(summary = "批量添加用户到指定项目指定分组") + fun batchCreateProjectUser( + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @Parameter(description = "用户名", required = true) + @HeaderParam(AUTH_HEADER_USER_ID) + userId: String, + @Parameter(description = "项目Code", required = true) + @PathParam("projectCode") + projectCode: String, + @Parameter(description = "用户组Code", required = true) + @PathParam("roleCode") + roleCode: String, + @Parameter(description = "添加用户集合", required = true) + members: List + ): Result + + @GET + @Path("/{projectCode}/roles") + @Operation(summary = "获取项目角色") + fun getProjectRoles( + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @PathParam("projectCode") + @Parameter(description = "项目Code", required = true) + projectCode: String, + @QueryParam("projectId") + @Parameter(description = "项目Id", required = true) + projectId: String + ): Result> + + @GET + @Path("/{projectCode}/getProjectPermissionInfo") + @Operation(summary = "获取项目权限信息") + fun getProjectPermissionInfo( + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @PathParam("projectCode") + @Parameter(description = "项目Code", required = true) + projectCode: String + ): Result +} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/open/OpenResourceMemberResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/open/OpenResourceMemberResource.kt new file mode 100644 index 00000000000..196d3a321a4 --- /dev/null +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/open/OpenResourceMemberResource.kt @@ -0,0 +1,99 @@ +package com.tencent.devops.auth.api.open + +import com.tencent.devops.common.api.auth.AUTH_HEADER_DEVOPS_BK_TOKEN +import com.tencent.devops.common.api.pojo.Result +import com.tencent.devops.common.auth.api.AuthResourceType +import com.tencent.devops.common.auth.api.pojo.BkAuthGroup +import com.tencent.devops.common.auth.api.pojo.BkAuthGroupAndUserList +import com.tencent.devops.project.pojo.ProjectCreateUserInfo +import com.tencent.devops.project.pojo.ProjectDeleteUserInfo +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter +import io.swagger.v3.oas.annotations.tags.Tag +import javax.ws.rs.Consumes +import javax.ws.rs.DELETE +import javax.ws.rs.GET +import javax.ws.rs.HeaderParam +import javax.ws.rs.POST +import javax.ws.rs.Path +import javax.ws.rs.PathParam +import javax.ws.rs.Produces +import javax.ws.rs.QueryParam +import javax.ws.rs.core.MediaType + +@Tag(name = "AUTH_SERVICE_RESOURCE", description = "权限--资源相关接口") +@Path("/open/service/auth/resource/member") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +interface OpenResourceMemberResource { + /** + * @param resourceType 是个枚举类型详见 AuthResourceType + * @see AuthResourceType + */ + @GET + @Path("/{projectCode}/getResourceGroupUsers") + @Operation(summary = "获取特定资源下用户组成员") + fun getResourceGroupMembers( + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @PathParam("projectCode") + @Parameter(description = "项目Code", required = true) + projectCode: String, + @QueryParam("resourceType") + @Parameter(description = "资源类型", required = false) + resourceType: String, + @QueryParam("resourceCode") + @Parameter(description = "资源code", required = false) + resourceCode: String, + @QueryParam("group") + @Parameter(description = "资源用户组类型", required = false) + group: BkAuthGroup? = null + ): Result> + + @GET + @Path("/{projectCode}/getResourceUsers") + @Operation(summary = "拉取资源下所有成员,并按项目角色组分组成员信息返回") + fun getResourceGroupAndMembers( + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @PathParam("projectCode") + @Parameter(description = "项目Code", required = true) + projectCode: String, + @QueryParam("resourceType") + @Parameter(description = "资源类型", required = false) + resourceType: String, + @QueryParam("resourceCode") + @Parameter(description = "资源code", required = false) + resourceCode: String + ): Result> + + @POST + @Path("/{projectCode}/batchAddResourceGroupMembers/") + @Operation(summary = "用户组添加成员") + fun batchAddResourceGroupMembers( + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @PathParam("projectCode") + @Parameter(description = "项目Code", required = true) + projectCode: String, + @Parameter(description = "用户组添加成员请求体", required = true) + projectCreateUserInfo: ProjectCreateUserInfo + ): Result + + @DELETE + @Path("/{projectCode}/batchDeleteResourceGroupMembers/") + @Operation(summary = "用户组删除成员") + fun batchDeleteResourceGroupMembers( + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @PathParam("projectCode") + @Parameter(description = "项目Code", required = true) + projectCode: String, + @Parameter(description = "用户组删除成员请求体", required = true) + projectDeleteUserInfo: ProjectDeleteUserInfo + ): Result +} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceManagerResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceManagerResource.kt deleted file mode 100644 index c0353028065..00000000000 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceManagerResource.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.devops.auth.api.service - -import com.tencent.devops.common.api.auth.AUTH_HEADER_DEVOPS_BK_TOKEN -import com.tencent.devops.common.api.auth.AUTH_HEADER_DEVOPS_USER_ID -import com.tencent.devops.common.api.pojo.Result -import io.swagger.v3.oas.annotations.Parameter -import io.swagger.v3.oas.annotations.tags.Tag -import javax.ws.rs.Consumes -import javax.ws.rs.GET -import javax.ws.rs.HeaderParam -import javax.ws.rs.Path -import javax.ws.rs.PathParam -import javax.ws.rs.Produces -import javax.ws.rs.QueryParam -import javax.ws.rs.core.MediaType - -@Tag(name = "SERVICE_MANAGER", description = "权限校验--超级管理员") -@Path("/open/service/auth/manager") -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -interface ServiceManagerResource { - - @GET - @Path("/projects/{projectCode}") - fun validateManagerPermission( - @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) - @Parameter(description = "待校验用户ID", required = true) - userId: String, - @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) - @Parameter(description = "认证token", required = true) - token: String, - @PathParam("projectCode") - @Parameter(description = "项目编码", required = true) - projectCode: String, - @QueryParam("action") - @Parameter(description = "资源类型", required = true) - action: String, - @QueryParam("resourceCode") - @Parameter(description = "资源编码", required = false) - resourceCode: String - ): Result -} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServicePermissionAuthResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServicePermissionAuthResource.kt index 6b7fbc569cd..bcb816ef562 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServicePermissionAuthResource.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServicePermissionAuthResource.kt @@ -27,7 +27,6 @@ package com.tencent.devops.auth.api.service -import com.tencent.devops.auth.pojo.dto.GrantInstanceDTO import com.tencent.devops.common.api.auth.AUTH_HEADER_DEVOPS_BK_TOKEN import com.tencent.devops.common.api.auth.AUTH_HEADER_DEVOPS_USER_ID import com.tencent.devops.common.api.auth.AUTH_HEADER_GIT_TYPE @@ -50,7 +49,7 @@ import javax.ws.rs.QueryParam import javax.ws.rs.core.MediaType @Tag(name = "AUTH_SERVICE_PERMISSION", description = "权限--权限校验以及资源操作相关接口") -@Path("/open/service/auth/permission") +@Path("/service/auth/permission") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @SuppressWarnings("LongParameterList") @@ -372,20 +371,4 @@ interface ServicePermissionAuthResource { @Parameter(description = "资源Code") resourceCode: String ): Result - - @Path("/projects/{projectCode}/grant") - @POST - @Operation(summary = "授权实例级别权限") - fun grantInstancePermission( - @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) - @Parameter(description = "操作用户ID", required = true) - userId: String, - @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) - @Parameter(description = "认证token", required = true) - token: String, - @PathParam("projectCode") - @Parameter(description = "项目Id") - projectCode: String, - grantInstance: GrantInstanceDTO - ): Result } diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceProjectAuthResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceProjectAuthResource.kt index 2b12f32d915..31fc7fdb84c 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceProjectAuthResource.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceProjectAuthResource.kt @@ -49,7 +49,7 @@ import javax.ws.rs.QueryParam import javax.ws.rs.core.MediaType @Tag(name = "AUTH_SERVICE_PROJECT", description = "权限--项目相关接口") -@Path("/open/service/auth/projects") +@Path("/service/auth/projects") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) interface ServiceProjectAuthResource { diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceResourceGroupResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceResourceGroupResource.kt index 22867da08a6..c5fd9ed32b6 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceResourceGroupResource.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceResourceGroupResource.kt @@ -2,8 +2,11 @@ package com.tencent.devops.auth.api.service import com.tencent.devops.auth.pojo.dto.GroupAddDTO import com.tencent.devops.auth.pojo.request.CustomGroupCreateReq +import com.tencent.devops.auth.pojo.vo.GroupDetailsInfoVo import com.tencent.devops.auth.pojo.vo.GroupPermissionDetailVo import com.tencent.devops.common.api.annotation.BkInterfaceI18n +import com.tencent.devops.common.api.auth.AUTH_HEADER_USER_ID +import com.tencent.devops.common.api.model.SQLPage import com.tencent.devops.common.api.pojo.Result import com.tencent.devops.common.auth.api.pojo.BkAuthGroup import io.swagger.v3.oas.annotations.Operation @@ -12,6 +15,7 @@ import io.swagger.v3.oas.annotations.tags.Tag import javax.ws.rs.Consumes import javax.ws.rs.DELETE import javax.ws.rs.GET +import javax.ws.rs.HeaderParam import javax.ws.rs.POST import javax.ws.rs.Path import javax.ws.rs.PathParam @@ -37,6 +41,48 @@ interface ServiceResourceGroupResource { groupId: Int ): Result>> + @GET + @Path("/{projectCode}/{resourceType}/getMemberGroupsDetails") + @Operation(summary = "获取项目成员有权限的用户组详情") + fun getMemberGroupsDetails( + @Parameter(description = "用户名", required = true) + @HeaderParam(AUTH_HEADER_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectCode") + projectCode: String, + @Parameter(description = "资源类型") + @PathParam("resourceType") + resourceType: String, + @QueryParam("memberId") + @Parameter(description = "组织ID/成员ID") + memberId: String, + @QueryParam("groupName") + @Parameter(description = "用户组名称") + groupName: String?, + @QueryParam("minExpiredAt") + @Parameter(description = "最小过期时间") + minExpiredAt: Long?, + @QueryParam("maxExpiredAt") + @Parameter(description = "最大过期时间") + maxExpiredAt: Long?, + @QueryParam("relatedResourceType") + @Parameter(description = "资源类型") + relatedResourceType: String?, + @QueryParam("relatedResourceCode") + @Parameter(description = "资源ID") + relatedResourceCode: String?, + @QueryParam("action") + @Parameter(description = "操作") + action: String?, + @Parameter(description = "起始位置,从0开始") + @QueryParam("start") + start: Int?, + @Parameter(description = "每页多少条") + @QueryParam("limit") + limit: Int? + ): Result> + @POST @Path("/{projectCode}/createGroupByGroupCode/") @Operation(summary = "根据groupCode添加用户组") diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceResourceMemberResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceResourceMemberResource.kt index 67c65238e08..f7e3b61387a 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceResourceMemberResource.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceResourceMemberResource.kt @@ -1,6 +1,8 @@ package com.tencent.devops.auth.api.service +import com.tencent.devops.auth.pojo.request.GroupMemberSingleRenewalReq import com.tencent.devops.common.api.auth.AUTH_HEADER_DEVOPS_BK_TOKEN +import com.tencent.devops.common.api.auth.AUTH_HEADER_USER_ID import com.tencent.devops.common.api.pojo.Result import com.tencent.devops.common.auth.api.AuthResourceType import com.tencent.devops.common.auth.api.pojo.BkAuthGroup @@ -15,6 +17,7 @@ import javax.ws.rs.DELETE import javax.ws.rs.GET import javax.ws.rs.HeaderParam import javax.ws.rs.POST +import javax.ws.rs.PUT import javax.ws.rs.Path import javax.ws.rs.PathParam import javax.ws.rs.Produces @@ -22,7 +25,7 @@ import javax.ws.rs.QueryParam import javax.ws.rs.core.MediaType @Tag(name = "AUTH_SERVICE_RESOURCE", description = "权限--资源相关接口") -@Path("/open/service/auth/resource/member") +@Path("/service/auth/resource/member") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) interface ServiceResourceMemberResource { @@ -96,4 +99,21 @@ interface ServiceResourceMemberResource { @Parameter(description = "用户组删除成员请求体", required = true) projectDeleteUserInfo: ProjectDeleteUserInfo ): Result + + @PUT + @Path("/{projectCode}/renewal") + @Operation(summary = "续期单个组成员权限--无需进行审批") + fun renewalGroupMember( + @HeaderParam(AUTH_HEADER_DEVOPS_BK_TOKEN) + @Parameter(description = "认证token", required = true) + token: String, + @Parameter(description = "用户名", required = true) + @HeaderParam(AUTH_HEADER_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectCode") + projectCode: String, + @Parameter(description = "续期成员请求实体") + renewalConditionReq: GroupMemberSingleRenewalReq + ): Result } diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthUrlResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthUrlResource.kt deleted file mode 100644 index 611478b9534..00000000000 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserAuthUrlResource.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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.devops.auth.api.user - -import com.tencent.devops.auth.pojo.PermissionUrlDTO -import com.tencent.devops.common.api.pojo.Result -import io.swagger.v3.oas.annotations.tags.Tag -import io.swagger.v3.oas.annotations.Operation -import io.swagger.v3.oas.annotations.Parameter -import javax.ws.rs.Consumes -import javax.ws.rs.GET -import javax.ws.rs.Path -import javax.ws.rs.Produces -import javax.ws.rs.POST -import javax.ws.rs.QueryParam -import javax.ws.rs.core.MediaType - -@Tag(name = "AUTH_RESOURCE", description = "用户态-权限") -@Path("/user/auth") -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -interface UserAuthUrlResource { - - @POST - @Path("/permissionUrl") - @Operation(summary = "权限申请重定向Url") - fun permissionUrl( - @Parameter(description = "待申请实例信息") - permissionUrlDTO: List - ): Result - - @GET - @Path("/group/permission/url") - fun getRolePermissionUrl( - @Parameter(description = "待分配权限用户组所属项目") - @QueryParam("projectId") - projectId: String, - @Parameter(description = "用户组Id") - @QueryParam("roleId") - roleId: String? - ): Result -} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserProjectMemberResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserProjectMemberResource.kt index e9e8d73b9c7..5a106df0295 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserProjectMemberResource.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserProjectMemberResource.kt @@ -28,25 +28,17 @@ package com.tencent.devops.auth.api.user -import com.tencent.bk.sdk.iam.constants.ManagerScopesEnum -import com.tencent.bk.sdk.iam.dto.manager.ManagerRoleGroupInfo -import com.tencent.bk.sdk.iam.dto.manager.vo.ManagerGroupMemberVo -import com.tencent.devops.auth.pojo.dto.RoleMemberDTO -import com.tencent.devops.auth.pojo.vo.ProjectMembersVO import com.tencent.devops.common.api.auth.AUTH_HEADER_USER_ID import com.tencent.devops.common.api.pojo.Result -import io.swagger.v3.oas.annotations.tags.Tag import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.Parameter +import io.swagger.v3.oas.annotations.tags.Tag import javax.ws.rs.Consumes -import javax.ws.rs.DELETE import javax.ws.rs.GET import javax.ws.rs.HeaderParam -import javax.ws.rs.POST import javax.ws.rs.Path import javax.ws.rs.PathParam import javax.ws.rs.Produces -import javax.ws.rs.QueryParam import javax.ws.rs.core.MediaType @Tag(name = "USER_PROJECT_MEMBER", description = "用户组—用户") @@ -54,97 +46,6 @@ import javax.ws.rs.core.MediaType @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) interface UserProjectMemberResource { - @POST - @Path("/projectIds/{projectId}/roleIds/{roleId}") - @Operation(summary = "项目下添加指定组组员") - fun createRoleMember( - @Parameter(description = "用户名", required = true) - @HeaderParam(AUTH_HEADER_USER_ID) - userId: String, - @Parameter(description = "项目标识", required = true) - @PathParam("projectId") - projectId: Int, - @Parameter(description = "角色Id", required = true) - @PathParam("roleId") - roleId: Int, - @Parameter(description = "是否为管理员分组", required = true) - @QueryParam("managerGroup") - managerGroup: Boolean, - @Parameter(description = "添加用户集合", required = true) - members: List - ): Result - - @GET - @Path("/projectIds/{projectId}/roleIds/{roleId}") - @Operation(summary = "查询项目下指定用户组用户") - fun getRoleMember( - @Parameter(description = "项目标识", required = true) - @PathParam("projectId") - projectId: Int, - @Parameter(description = "角色Id", required = true) - @PathParam("roleId") - roleId: Int, - @Parameter(description = "页数", required = true) - @QueryParam("path") - page: Int?, - @Parameter(description = "页面大小", required = true) - @QueryParam("pageSize") - pageSize: Int? - ): Result - - @GET - @Path("projectIds/{projectId}/members/all") - @Operation(summary = "获取项目下所有用户") - fun getProjectAllMember( - @Parameter(description = "项目标识", required = true) - @PathParam("projectId") - projectId: Int, - @Parameter(description = "页数", required = true) - @QueryParam("path") - page: Int?, - @Parameter(description = "页面大小", required = true) - @QueryParam("pageSize") - pageSize: Int? - ): Result - - @DELETE - @Path("/projectIds/{projectId}/roleIds/{roleId}") - @Operation(summary = "删除项目下指定用户组用户") - fun deleteRoleMember( - @Parameter(description = "用户名", required = true) - @HeaderParam(AUTH_HEADER_USER_ID) - userId: String, - @Parameter(description = "项目标识", required = true) - @PathParam("projectId") - projectId: Int, - @Parameter(description = "角色Id", required = true) - @PathParam("roleId") - roleId: Int, - @Parameter(description = "是否为管理员分组", required = true) - @QueryParam("managerGroup") - managerGroup: Boolean, - @Parameter(description = "待删除用户或组织Id", required = true) - @QueryParam("id") - members: String, - @Parameter(description = "组员类型 user:单用户, dept:组织", required = true) - @QueryParam("type") - type: ManagerScopesEnum - ): Result - - @GET - @Path("projectIds/{projectId}/user/groups") - @Operation(summary = "获取指定用户指定项目下的用户组") - fun getUserAllGroup( - @Parameter(description = "用户名", required = true) - @HeaderParam(AUTH_HEADER_USER_ID) - userId: String, - @Parameter(description = "项目标识", required = true) - @PathParam("projectId") - projectId: Int, - @Parameter(description = "待搜用户", required = true) - searchUserId: String - ): Result?> - @GET @Path("/projectIds/{projectId}/checkManager") @Operation(summary = "判断是否是项目管理员或CI管理员") diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserProjectRoleResource.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserProjectRoleResource.kt deleted file mode 100644 index a4b27df373d..00000000000 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/user/UserProjectRoleResource.kt +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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.devops.auth.api.user - -import com.tencent.devops.auth.pojo.DefaultGroup -import com.tencent.devops.auth.pojo.dto.ProjectRoleDTO -import com.tencent.devops.auth.pojo.vo.GroupInfoVo -import com.tencent.devops.common.api.auth.AUTH_HEADER_USER_ID -import com.tencent.devops.common.api.pojo.Result -import io.swagger.v3.oas.annotations.tags.Tag -import io.swagger.v3.oas.annotations.Operation -import io.swagger.v3.oas.annotations.Parameter -import javax.ws.rs.Consumes -import javax.ws.rs.DELETE -import javax.ws.rs.GET -import javax.ws.rs.HeaderParam -import javax.ws.rs.POST -import javax.ws.rs.PUT -import javax.ws.rs.Path -import javax.ws.rs.PathParam -import javax.ws.rs.Produces -import javax.ws.rs.QueryParam -import javax.ws.rs.core.MediaType - -@Tag(name = "USER_PROJECT_ROLE", description = "项目-用户组") -@Path("/user/project/roles") -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -interface UserProjectRoleResource { - @POST - @Path("/projectIds/{projectId}/") - @Operation(summary = "项目下添加指定组") - fun createProjectRole( - @Parameter(description = "用户名", required = true) - @HeaderParam(AUTH_HEADER_USER_ID) - userId: String, - @Parameter(description = "项目标识", required = true) - @PathParam("projectId") - projectId: Int, - @Parameter(description = "项目标识", required = true) - @QueryParam("projectCode") - projectCode: String, - @Parameter(description = "用户组信息", required = true) - groupInfo: ProjectRoleDTO - ): Result - - @PUT - @Path("/projectIds/{projectId}/roleIds/{roleId}") - @Operation(summary = "用户组重命名") - fun updateProjectRole( - @Parameter(description = "用户名", required = true) - @HeaderParam(AUTH_HEADER_USER_ID) - userId: String, - @Parameter(description = "项目标识", required = true) - @PathParam("projectId") - projectId: Int, - @Parameter(description = "角色Id", required = true) - @PathParam("roleId") - roleId: Int, - @Parameter(description = "用户组信息", required = true) - groupInfo: ProjectRoleDTO - ): Result - - @GET - @Path("/projectIds/{projectId}") - @Operation(summary = "获取用户组") - fun getProjectRoles( - @Parameter(description = "用户名", required = true) - @HeaderParam(AUTH_HEADER_USER_ID) - userId: String, - @Parameter(description = "项目标识", required = true) - @PathParam("projectId") - projectId: Int - ): Result> - - @DELETE - @Path("/projectIds/{projectId}/roles/{roleId}") - @Operation(summary = "删除用户组") - fun deleteProjectRole( - @Parameter(description = "用户名", required = true) - @HeaderParam(AUTH_HEADER_USER_ID) - userId: String, - @Parameter(description = "项目标识", required = true) - @PathParam("projectId") - projectId: Int, - @Parameter(description = "角色Id", required = true) - @PathParam("roleId") - roleId: Int - ): Result - - @GET - @Path("/projects/{projectId}/manager/hasPermission") - @Operation(summary = "是否有项目管理操作的权限") - fun hashPermission( - @Parameter(description = "用户名", required = true) - @HeaderParam(AUTH_HEADER_USER_ID) - userId: String, - @Parameter(description = "项目标识", required = true) - @PathParam("projectId") - projectId: Int - ): Result - - @GET - @Path("/default/role") - fun getDefaultRole( - @Parameter(description = "用户名", required = true) - @HeaderParam(AUTH_HEADER_USER_ID) - userId: String - ): Result> -} diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/GroupDetailsInfoVo.kt b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/GroupDetailsInfoVo.kt index 26bd35b3aff..6193dda5c6e 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/GroupDetailsInfoVo.kt +++ b/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/pojo/vo/GroupDetailsInfoVo.kt @@ -20,7 +20,7 @@ data class GroupDetailsInfoVo( val groupDesc: String? = null, @get:Schema(title = "有效期,天") val expiredAtDisplay: String, - @get:Schema(title = "过期时间戳,秒") + @get:Schema(title = "过期时间戳,毫秒") val expiredAt: Long, @get:Schema(title = "加入时间") val joinedTime: Long, diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/common/Constants.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/common/Constants.kt index dd56d40fc8e..e19021fbf9c 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/common/Constants.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/common/Constants.kt @@ -29,7 +29,7 @@ package com.tencent.devops.auth.common object Constants { const val SUPER_MANAGER = -1 - const val DEPT_LABEL = "id,name,parent,enabled,has_children" + const val DEPT_LABEL = "id,name,parent,enabled" const val USER_LABEL = "id,username,display_name,enabled,departments,extras" const val USER_NAME_AND_DISPLAY_NAME_LABEL = "id,username,display_name" const val LEVEL = "level" diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthGroupDao.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthGroupDao.kt deleted file mode 100644 index acad2517975..00000000000 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthGroupDao.kt +++ /dev/null @@ -1,194 +0,0 @@ -/* - * 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.devops.auth.dao - -import com.tencent.devops.auth.entity.GroupCreateInfo -import com.tencent.devops.model.auth.tables.TAuthGroupInfo -import com.tencent.devops.model.auth.tables.records.TAuthGroupInfoRecord -import org.jooq.DSLContext -import org.jooq.Result -import org.springframework.stereotype.Repository -import java.time.LocalDateTime - -@Repository -class AuthGroupDao { - - fun createGroup(dslContext: DSLContext, groupCreateInfo: GroupCreateInfo): Int { - with(TAuthGroupInfo.T_AUTH_GROUP_INFO) { - return dslContext.insertInto( - this, - GROUP_NAME, - GROUP_CODE, - GROUP_TYPE, - RELATION_ID, - DISPLAY_NAME, - PROJECT_CODE, - CREATE_USER, - CREATE_TIME, - UPDATE_USER, - UPDATE_TIME - ).values( - groupCreateInfo.groupName, - groupCreateInfo.groupCode, - groupCreateInfo.groupType, - groupCreateInfo.relationId, - groupCreateInfo.displayName, - groupCreateInfo.projectCode, - groupCreateInfo.user, - LocalDateTime.now(), - null, - null - ).returning(ID).fetchOne()!!.id - } - } - - fun getGroup(dslContext: DSLContext, projectCode: String, groupCode: String): TAuthGroupInfoRecord? { - with(TAuthGroupInfo.T_AUTH_GROUP_INFO) { - return dslContext.selectFrom(this) - .where(PROJECT_CODE.eq(projectCode).and(GROUP_CODE.eq(groupCode).and(IS_DELETE.eq(false)))).fetchAny() - } - } - - fun getGroupByProject(dslContext: DSLContext, projectCode: String): Result { - with(TAuthGroupInfo.T_AUTH_GROUP_INFO) { - return dslContext.selectFrom(this) - .where(PROJECT_CODE.eq(projectCode).and(IS_DELETE.eq(false))).fetch() - } - } - - fun getGroupByCodes( - dslContext: DSLContext, - projectCode: String, - groupCodes: List - ): Result { - with(TAuthGroupInfo.T_AUTH_GROUP_INFO) { - return dslContext.selectFrom(this) - .where(PROJECT_CODE.eq(projectCode).and(GROUP_CODE.`in`(groupCodes).and(IS_DELETE.eq(false)))).fetch() - } - } - - fun getGroupById(dslContext: DSLContext, groupId: Int): TAuthGroupInfoRecord? { - with(TAuthGroupInfo.T_AUTH_GROUP_INFO) { - return dslContext.selectFrom(this) - .where(ID.eq(groupId)).fetchOne() - } - } - - fun getGroupByRelationId(dslContext: DSLContext, relationId: Int): TAuthGroupInfoRecord? { - with(TAuthGroupInfo.T_AUTH_GROUP_INFO) { - return dslContext.selectFrom(this).where(RELATION_ID.eq(relationId.toString())).fetchAny() - } - } - - fun getGroupByRelationIds(dslContext: DSLContext, relationIds: List): Result { - with(TAuthGroupInfo.T_AUTH_GROUP_INFO) { - return dslContext.selectFrom(this).where(RELATION_ID.`in`(relationIds).and(IS_DELETE.eq(false))).fetch() - } - } - - fun getGroupByName(dslContext: DSLContext, projectCode: String, groupName: String): TAuthGroupInfoRecord? { - with(TAuthGroupInfo.T_AUTH_GROUP_INFO) { - return dslContext.selectFrom(this).where(PROJECT_CODE.eq(projectCode) - .and(GROUP_NAME.eq(groupName))).fetchAny() - } - } - - fun batchCreateGroups(dslContext: DSLContext, groups: List) { - if (groups.isEmpty()) { - return - } - dslContext.batch(groups.map { - with(TAuthGroupInfo.T_AUTH_GROUP_INFO) { - dslContext.insertInto( - this, - GROUP_NAME, - GROUP_CODE, - GROUP_TYPE, - RELATION_ID, - DISPLAY_NAME, - PROJECT_CODE, - CREATE_USER, - CREATE_TIME, - UPDATE_USER, - UPDATE_TIME - ).values( - it.groupName, - it.groupCode, - it.groupType, - it.relationId, - it.displayName, - it.projectCode, - it.user, - LocalDateTime.now(), - null, - null - ) - } - }).execute() - } - - fun update( - dslContext: DSLContext, - id: Int, - groupName: String, - displayName: String, - userId: String - ): Int { - with(TAuthGroupInfo.T_AUTH_GROUP_INFO) { - return dslContext.update(this).set(GROUP_NAME, groupName) - .set(DISPLAY_NAME, displayName) - .set(UPDATE_USER, userId) - .set(UPDATE_TIME, LocalDateTime.now()) - .where(ID.eq(id)).execute() - } - } - - fun updateRelationId(dslContext: DSLContext, roleId: Int, relationId: String): Int { - with(TAuthGroupInfo.T_AUTH_GROUP_INFO) { - return dslContext.update(this).set(RELATION_ID, relationId).where(ID.eq(roleId)).execute() - } - } - - fun getRelationId(dslContext: DSLContext, roleId: Int): TAuthGroupInfoRecord? { - with(TAuthGroupInfo.T_AUTH_GROUP_INFO) { - return dslContext.selectFrom(this).where(ID.eq(roleId)).fetchAny() - } - } - - fun softDelete(dslContext: DSLContext, roleId: Int) { - with(TAuthGroupInfo.T_AUTH_GROUP_INFO) { - dslContext.update(this).set(IS_DELETE, true).where(ID.eq(roleId)).execute() - } - } - - fun deleteRole(dslContext: DSLContext, roleId: Int) { - with(TAuthGroupInfo.T_AUTH_GROUP_INFO) { - dslContext.delete(this).where(ID.eq(roleId)).execute() - } - } -} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthGroupPermissionDao.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthGroupPermissionDao.kt deleted file mode 100644 index b102acae785..00000000000 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthGroupPermissionDao.kt +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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.devops.auth.dao - -import com.tencent.devops.common.api.util.UUIDUtil -import com.tencent.devops.model.auth.tables.TAuthGroupPerssion -import com.tencent.devops.model.auth.tables.records.TAuthGroupPerssionRecord -import org.jooq.DSLContext -import org.jooq.Result -import org.springframework.stereotype.Repository -import java.time.LocalDateTime -import java.util.UUID - -@Repository -class AuthGroupPermissionDao { - - fun create(dslContext: DSLContext, groupCode: String, userId: String, authAction: String): Int { - with(TAuthGroupPerssion.T_AUTH_GROUP_PERSSION) { - return dslContext.insertInto( - this, - ID, - GROUP_CODE, - AUTH_ACTION, - CREATE_USER, - CREATE_TIME, - UPDATE_USER, - UPDATE_TIME - ).values( - UUIDUtil.generate(), - groupCode, - authAction, - userId, - LocalDateTime.now(), - null, - null - ).execute() - } - } - - fun batchCreateAction(dslContext: DSLContext, groupCode: String, userId: String, authActions: List) { - if (authActions.isEmpty()) { - return - } - dslContext.batch(authActions.map { - with(TAuthGroupPerssion.T_AUTH_GROUP_PERSSION) { - dslContext.insertInto( - this, - ID, - GROUP_CODE, - AUTH_ACTION, - CREATE_USER, - CREATE_TIME, - UPDATE_USER, - UPDATE_TIME - ).values( - UUID.randomUUID().toString(), - groupCode, - it, - userId, - LocalDateTime.now(), - null, - null - ) - } - }).execute() - } - - fun getByGroupCode(dslContext: DSLContext, groupCode: String): Result? { - with(TAuthGroupPerssion.T_AUTH_GROUP_PERSSION) { - return dslContext.selectFrom(this).where(GROUP_CODE.eq(groupCode)).fetch() - } - } -} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthGroupUserDao.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthGroupUserDao.kt deleted file mode 100644 index 83ac4ee0cda..00000000000 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthGroupUserDao.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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.devops.auth.dao - -import com.tencent.devops.common.api.util.UUIDUtil -import com.tencent.devops.model.auth.tables.TAuthGroupUser -import com.tencent.devops.model.auth.tables.records.TAuthGroupUserRecord -import org.jooq.DSLContext -import org.springframework.stereotype.Repository -import java.time.LocalDateTime - -@Repository -class AuthGroupUserDao { - - fun create(dslContext: DSLContext, userId: String, groupId: String): Int { - with(TAuthGroupUser.T_AUTH_GROUP_USER) { - return dslContext.insertInto( - this, - ID, - USER_ID, - GROUP_ID, - CREATE_USER, - CREATE_TIME - ).values( - UUIDUtil.generate(), - userId, - groupId, - userId, - LocalDateTime.now() - ).execute() - } - } - - fun get(dslContext: DSLContext, userId: String, groupId: String): TAuthGroupUserRecord? { - with(TAuthGroupUser.T_AUTH_GROUP_USER) { - return dslContext.selectFrom(this) - .where(USER_ID.eq(userId).and(GROUP_ID.eq(groupId))).fetchOne() - } - } -} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthOauth2ClientDetailsDao.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthOauth2ClientDetailsDao.kt index c1aa8191f28..abb70fb6c37 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthOauth2ClientDetailsDao.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthOauth2ClientDetailsDao.kt @@ -42,7 +42,7 @@ class AuthOauth2ClientDetailsDao { clientDetailsDTO.clientName, clientDetailsDTO.scope, clientDetailsDTO.icon, - clientDetailsDTO.authorizedGrantTypes.map { it.grantType }.joinToString { "," }, + clientDetailsDTO.authorizedGrantTypes.joinToString(",") { it.grantType }, clientDetailsDTO.webServerRedirectUri, clientDetailsDTO.accessTokenValidity, clientDetailsDTO.refreshTokenValidity, diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupApplyDao.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupApplyDao.kt index 24aa6bea53a..5cd98deab4a 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupApplyDao.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/dao/AuthResourceGroupApplyDao.kt @@ -61,4 +61,15 @@ class AuthResourceGroupApplyDao { } } } + + fun delete( + dslContext: DSLContext, + id: Long + ) { + with(TAuthResourceGroupApply.T_AUTH_RESOURCE_GROUP_APPLY) { + dslContext.deleteFrom(this) + .where(ID.eq(id)) + .execute() + } + } } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/config/RbacMQConfiguration.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/config/RbacMQConfiguration.kt index 65834d74a18..39b1542714e 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/config/RbacMQConfiguration.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/config/RbacMQConfiguration.kt @@ -38,6 +38,7 @@ import com.tencent.devops.auth.provider.rbac.pojo.event.AuthResourceGroupCreateE import com.tencent.devops.auth.provider.rbac.pojo.event.AuthResourceGroupModifyEvent import com.tencent.devops.auth.provider.rbac.service.PermissionGradeManagerService import com.tencent.devops.auth.provider.rbac.service.PermissionSubsetManagerService +import com.tencent.devops.auth.service.iam.PermissionResourceGroupPermissionService import com.tencent.devops.auth.service.iam.PermissionResourceGroupSyncService import com.tencent.devops.common.client.Client import com.tencent.devops.common.event.annotation.EventConsumer @@ -61,9 +62,11 @@ class RbacMQConfiguration { @Bean fun syncGroupAndMemberListener( - permissionResourceGroupSyncService: PermissionResourceGroupSyncService + resourceGroupSyncService: PermissionResourceGroupSyncService, + resourceGroupPermissionService: PermissionResourceGroupPermissionService ) = SyncGroupAndMemberListener( - permissionResourceGroupSyncService = permissionResourceGroupSyncService + resourceGroupSyncService = resourceGroupSyncService, + resourceGroupPermissionService = resourceGroupPermissionService ) @EventConsumer diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/listener/SyncGroupAndMemberListener.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/listener/SyncGroupAndMemberListener.kt index 710346d1324..b16d0c5673a 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/listener/SyncGroupAndMemberListener.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/listener/SyncGroupAndMemberListener.kt @@ -28,6 +28,7 @@ package com.tencent.devops.auth.provider.rbac.listener +import com.tencent.devops.auth.service.iam.PermissionResourceGroupPermissionService import com.tencent.devops.auth.service.iam.PermissionResourceGroupSyncService import com.tencent.devops.common.event.listener.EventListener import com.tencent.devops.project.pojo.mq.ProjectEnableStatusBroadCastEvent @@ -35,13 +36,17 @@ import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired class SyncGroupAndMemberListener @Autowired constructor( - private val permissionResourceGroupSyncService: PermissionResourceGroupSyncService + private val resourceGroupSyncService: PermissionResourceGroupSyncService, + private val resourceGroupPermissionService: PermissionResourceGroupPermissionService ) : EventListener { override fun execute(event: ProjectEnableStatusBroadCastEvent) { - logger.info("sync group and member when enabled project $event") + logger.info("sync group,group member and group permissions when enabled project $event") if (event.enabled) { - permissionResourceGroupSyncService.syncGroupAndMember(projectCode = event.projectId) + // 项目启用时,同步用户组/用户组成员/用户组权限 + resourceGroupSyncService.syncProjectGroup(projectCode = event.projectId) + resourceGroupSyncService.syncGroupAndMember(projectCode = event.projectId) + resourceGroupPermissionService.syncProjectPermissions(projectCode = event.projectId) } } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionApplyService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionApplyService.kt index bc836f761a1..e5ff247f490 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionApplyService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionApplyService.kt @@ -27,7 +27,6 @@ import com.tencent.devops.auth.pojo.vo.AuthRedirectGroupInfoVo import com.tencent.devops.auth.pojo.vo.ManagerRoleGroupVO import com.tencent.devops.auth.pojo.vo.ResourceTypeInfoVo import com.tencent.devops.auth.service.DeptService -import com.tencent.devops.auth.service.GroupUserService import com.tencent.devops.auth.service.iam.PermissionApplyService import com.tencent.devops.auth.service.iam.PermissionService import com.tencent.devops.common.api.exception.ErrorCodeException @@ -640,7 +639,7 @@ class RbacPermissionApplyService @Autowired constructor( } companion object { - private val logger = LoggerFactory.getLogger(GroupUserService::class.java) + private val logger = LoggerFactory.getLogger(RbacPermissionApplyService::class.java) private val executor = Executors.newFixedThreadPool(10) } } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupSyncService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupSyncService.kt index 40ddb9fb584..4f14cdb55f5 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupSyncService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceGroupSyncService.kt @@ -197,6 +197,7 @@ class RbacPermissionResourceGroupSyncService @Autowired constructor( isMemberJoinedToGroup } catch (ignore: Exception) { logger.warn("verify group valid member failed,${it.memberId}|${it.iamGroupId}", ignore) + authResourceGroupApplyDao.delete(dslContext, it.id) false } } @@ -314,7 +315,7 @@ class RbacPermissionResourceGroupSyncService @Autowired constructor( } @Suppress("NestedBlockDepth") - private fun syncProjectGroup(projectCode: String) { + override fun syncProjectGroup(projectCode: String) { val startEpoch = System.currentTimeMillis() logger.info("start to sync project group :$projectCode") try { diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceMemberService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceMemberService.kt index d5525d3fb69..8fd0e90c4b6 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceMemberService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/RbacPermissionResourceMemberService.kt @@ -683,7 +683,7 @@ class RbacPermissionResourceMemberService( dslContext = dslContext, projectCode = projectCode, iamGroupId = groupId, - expiredTime = DateTimeUtil.convertTimestampToLocalDateTime(expiredAt), + expiredTime = DateTimeUtil.convertTimestampToLocalDateTime(finalExpiredAt), memberId = targetMember.id ) } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/RbacPermissionMigrateService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/RbacPermissionMigrateService.kt index cf292ec217f..6c6f54198f8 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/RbacPermissionMigrateService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/migrate/RbacPermissionMigrateService.kt @@ -698,4 +698,15 @@ class RbacPermissionMigrateService constructor( } return true } + + override fun enablePipelineListPermissionControl(projectCodes: List): Boolean { + projectCodes.forEach { + val projectInfo = client.get(ServiceProjectResource::class).get(it).data!! + val properties = projectInfo.properties ?: ProjectProperties() + properties.pipelineListPermissionControl = true + logger.info("update project($it) properties|$properties") + client.get(ServiceProjectResource::class).updateProjectProperties(it, properties) + } + return true + } } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/config/MockAuthConfiguration.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/config/MockAuthConfiguration.kt index 279ba8e8728..84587ca37b7 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/config/MockAuthConfiguration.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/config/MockAuthConfiguration.kt @@ -5,11 +5,9 @@ import com.tencent.devops.auth.provider.sample.service.SampleAuthAuthorizationSc import com.tencent.devops.auth.provider.sample.service.SampleAuthMonitorSpaceService import com.tencent.devops.auth.provider.sample.service.SampleAuthPermissionProjectService import com.tencent.devops.auth.provider.sample.service.SampleAuthPermissionService -import com.tencent.devops.auth.provider.sample.service.SampleGrantPermissionServiceImpl import com.tencent.devops.auth.provider.sample.service.SampleOrganizationService import com.tencent.devops.auth.provider.sample.service.SamplePermissionApplyService import com.tencent.devops.auth.provider.sample.service.SamplePermissionExtService -import com.tencent.devops.auth.provider.sample.service.SamplePermissionGradeService import com.tencent.devops.auth.provider.sample.service.SamplePermissionItsmCallbackService import com.tencent.devops.auth.provider.sample.service.SamplePermissionMigrateService import com.tencent.devops.auth.provider.sample.service.SamplePermissionResourceGroupAndMemberFacadeService @@ -19,9 +17,6 @@ import com.tencent.devops.auth.provider.sample.service.SamplePermissionResourceG import com.tencent.devops.auth.provider.sample.service.SamplePermissionResourceMemberService import com.tencent.devops.auth.provider.sample.service.SamplePermissionResourceService import com.tencent.devops.auth.provider.sample.service.SamplePermissionResourceValidateService -import com.tencent.devops.auth.provider.sample.service.SamplePermissionRoleMemberService -import com.tencent.devops.auth.provider.sample.service.SamplePermissionRoleService -import com.tencent.devops.auth.provider.sample.service.SamplePermissionUrlServiceImpl import com.tencent.devops.auth.provider.sample.service.SampleSuperManagerServiceImpl import com.tencent.devops.auth.service.AuthAuthorizationScopesService import com.tencent.devops.auth.service.AuthMonitorSpaceService @@ -32,8 +27,6 @@ import com.tencent.devops.auth.service.PermissionAuthorizationService import com.tencent.devops.auth.service.SuperManagerService import com.tencent.devops.auth.service.iam.PermissionApplyService import com.tencent.devops.auth.service.iam.PermissionExtService -import com.tencent.devops.auth.service.iam.PermissionGradeService -import com.tencent.devops.auth.service.iam.PermissionGrantService import com.tencent.devops.auth.service.iam.PermissionItsmCallbackService import com.tencent.devops.auth.service.iam.PermissionMigrateService import com.tencent.devops.auth.service.iam.PermissionProjectService @@ -44,10 +37,7 @@ import com.tencent.devops.auth.service.iam.PermissionResourceGroupSyncService import com.tencent.devops.auth.service.iam.PermissionResourceMemberService import com.tencent.devops.auth.service.iam.PermissionResourceService import com.tencent.devops.auth.service.iam.PermissionResourceValidateService -import com.tencent.devops.auth.service.iam.PermissionRoleMemberService -import com.tencent.devops.auth.service.iam.PermissionRoleService import com.tencent.devops.auth.service.iam.PermissionService -import com.tencent.devops.auth.service.iam.PermissionUrlService import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.context.annotation.Bean @@ -67,10 +57,6 @@ class MockAuthConfiguration { @ConditionalOnMissingBean(PermissionExtService::class) fun permissionExtService() = SamplePermissionExtService() - @Bean - @ConditionalOnMissingBean(PermissionUrlService::class) - fun permissionUrlService() = SamplePermissionUrlServiceImpl() - @Bean @ConditionalOnMissingBean(PermissionProjectService::class) fun sampleAuthPermissionProjectService() = SampleAuthPermissionProjectService() @@ -79,26 +65,10 @@ class MockAuthConfiguration { @ConditionalOnMissingBean(PermissionService::class) fun sampleAuthPermissionService() = SampleAuthPermissionService() - @Bean - @ConditionalOnMissingBean(PermissionGrantService::class) - fun sampleGrantPermissionServiceImpl() = SampleGrantPermissionServiceImpl() - @Bean @ConditionalOnMissingBean(SuperManagerService::class) fun sampleSuperManagerServiceImpl() = SampleSuperManagerServiceImpl() - @Bean - @ConditionalOnMissingBean(PermissionGradeService::class) - fun samplePermissionGradeService() = SamplePermissionGradeService() - - @Bean - @ConditionalOnMissingBean(PermissionRoleMemberService::class) - fun samplePermissionRoleMemberService() = SamplePermissionRoleMemberService() - - @Bean - @ConditionalOnMissingBean(PermissionRoleService::class) - fun samplePermissionRoleService() = SamplePermissionRoleService() - @Bean @ConditionalOnMissingBean(OrganizationService::class) fun sampleOrganizationService() = SampleOrganizationService() diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SampleGrantPermissionServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SampleGrantPermissionServiceImpl.kt deleted file mode 100644 index 7b51e11baef..00000000000 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SampleGrantPermissionServiceImpl.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.tencent.devops.auth.provider.sample.service - -import com.tencent.devops.auth.pojo.dto.GrantInstanceDTO -import com.tencent.devops.auth.service.iam.PermissionGrantService - -class SampleGrantPermissionServiceImpl : PermissionGrantService { - override fun grantInstancePermission(projectId: String, grantInfo: GrantInstanceDTO): Boolean { - return false - } -} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionMigrateService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionMigrateService.kt index aedbef85813..3c79190e0b0 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionMigrateService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionMigrateService.kt @@ -103,4 +103,6 @@ class SamplePermissionMigrateService( } override fun fixResourceGroups(projectCodes: List): Boolean = true + + override fun enablePipelineListPermissionControl(projectCodes: List) = true } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceGroupSyncService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceGroupSyncService.kt index eceb19c8a3c..774476405d7 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceGroupSyncService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionResourceGroupSyncService.kt @@ -39,6 +39,8 @@ class SamplePermissionResourceGroupSyncService : PermissionResourceGroupSyncServ override fun syncGroupAndMember(projectCode: String) = Unit + override fun syncProjectGroup(projectCode: String) = Unit + override fun getStatusOfSync(projectCode: String): AuthMigrateStatus = AuthMigrateStatus.SUCCEED override fun batchSyncProjectGroup(projectCodes: List) = Unit diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionRoleMemberService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionRoleMemberService.kt deleted file mode 100644 index e3ca29e427f..00000000000 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionRoleMemberService.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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.devops.auth.provider.sample.service - -import com.tencent.bk.sdk.iam.constants.ManagerScopesEnum -import com.tencent.bk.sdk.iam.dto.manager.ManagerRoleGroupInfo -import com.tencent.bk.sdk.iam.dto.manager.vo.ManagerGroupMemberVo -import com.tencent.devops.auth.pojo.dto.RoleMemberDTO -import com.tencent.devops.auth.pojo.vo.ProjectMembersVO -import com.tencent.devops.auth.service.iam.PermissionRoleMemberService - -class SamplePermissionRoleMemberService : PermissionRoleMemberService { - override fun createRoleMember( - userId: String, - projectId: Int, - roleId: Int, - members: List, - managerGroup: Boolean, - checkAGradeManager: Boolean? - ) { - TODO("Not yet implemented") - } - - override fun deleteRoleMember( - userId: String, - projectId: Int, - roleId: Int, - id: String, - type: ManagerScopesEnum, - managerGroup: Boolean - ) { - TODO("Not yet implemented") - } - - override fun getRoleMember(projectId: Int, roleId: Int, page: Int?, pageSize: Int?): ManagerGroupMemberVo { - TODO("Not yet implemented") - } - - override fun getProjectAllMember(projectId: Int, page: Int?, pageSize: Int?): ProjectMembersVO? { - TODO("Not yet implemented") - } - - override fun getUserGroups(projectId: Int, userId: String): List? { - TODO("Not yet implemented") - } -} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionRoleService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionRoleService.kt deleted file mode 100644 index cc3f1b215c5..00000000000 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionRoleService.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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.devops.auth.provider.sample.service - -import com.tencent.devops.auth.pojo.DefaultGroup -import com.tencent.devops.auth.pojo.dto.ProjectRoleDTO -import com.tencent.devops.auth.pojo.vo.GroupInfoVo -import com.tencent.devops.auth.service.iam.PermissionRoleService - -class SamplePermissionRoleService : PermissionRoleService { - override fun createPermissionRole( - userId: String, - projectId: Int, - projectCode: String, - groupInfo: ProjectRoleDTO - ): Int { - TODO("Not yet implemented") - } - - override fun renamePermissionRole(userId: String, projectId: Int, roleId: Int, groupInfo: ProjectRoleDTO) { - TODO("Not yet implemented") - } - - override fun getPermissionRole(projectId: Int): List { - TODO("Not yet implemented") - } - - override fun deletePermissionRole(userId: String, projectId: Int, roleId: Int) { - TODO("Not yet implemented") - } - - override fun getDefaultRole(): List { - TODO("Not yet implemented") - } -} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ServiceGroupResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ServiceGroupResourceImpl.kt deleted file mode 100644 index 5f1d238df12..00000000000 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ServiceGroupResourceImpl.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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.devops.auth.resources - -import com.tencent.devops.auth.api.ServiceGroupResource -import com.tencent.devops.auth.pojo.dto.GroupDTO -import com.tencent.devops.auth.service.AuthGroupService -import com.tencent.devops.common.api.pojo.Result -import com.tencent.devops.common.web.RestResource -import org.springframework.beans.factory.annotation.Autowired - -@RestResource -class ServiceGroupResourceImpl @Autowired constructor( - val authGroupService: AuthGroupService -) : ServiceGroupResource { - - override fun createGroup( - userId: String, - projectCode: String, - groupInfo: GroupDTO - ): Result { - authGroupService.createGroup(userId, projectCode, groupInfo) - return Result(true) - } - - override fun batchCreateGroup(userId: String, projectCode: String, groupInfos: List): Result { - return authGroupService.batchCreate(userId, projectCode, groupInfos) - } -} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ServiceUserGroupResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ServiceUserGroupResourceImpl.kt deleted file mode 100644 index 381baddf45c..00000000000 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ServiceUserGroupResourceImpl.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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.devops.auth.resources - -import com.tencent.devops.auth.api.ServiceUserGroupResource -import com.tencent.devops.auth.service.GroupUserService -import com.tencent.devops.common.api.pojo.Result -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.web.bind.annotation.RestController - -@RestController -class ServiceUserGroupResourceImpl @Autowired constructor( - val groupUserService: GroupUserService -) : ServiceUserGroupResource { - - override fun addUser2Group(userId: String, groupId: Int): Result { - return groupUserService.addUser2Group(userId, groupId) - } -} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserProjectMemberResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserProjectMemberResourceImpl.kt deleted file mode 100644 index c14de3fe690..00000000000 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserProjectMemberResourceImpl.kt +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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.devops.auth.resources - -import com.tencent.bk.sdk.iam.constants.ManagerScopesEnum -import com.tencent.bk.sdk.iam.dto.manager.ManagerRoleGroupInfo -import com.tencent.bk.sdk.iam.dto.manager.vo.ManagerGroupMemberVo -import com.tencent.devops.auth.api.user.UserProjectMemberResource -import com.tencent.devops.auth.pojo.dto.RoleMemberDTO -import com.tencent.devops.auth.pojo.vo.ProjectMembersVO -import com.tencent.devops.auth.service.iam.PermissionProjectService -import com.tencent.devops.auth.service.iam.PermissionRoleMemberService -import com.tencent.devops.common.api.pojo.Result -import com.tencent.devops.common.auth.api.pojo.BkAuthGroup -import com.tencent.devops.common.web.RestResource -import org.springframework.beans.factory.annotation.Autowired - -@RestResource -class UserProjectMemberResourceImpl @Autowired constructor( - val permissionRoleMemberService: PermissionRoleMemberService, - val permissionProjectService: PermissionProjectService -) : UserProjectMemberResource { - override fun createRoleMember( - userId: String, - projectId: Int, - roleId: Int, - managerGroup: Boolean, - members: List - ): Result { - permissionRoleMemberService.createRoleMember( - userId = userId, - projectId = projectId, - roleId = roleId, - members = members, - managerGroup = managerGroup, - checkAGradeManager = true - ) - return Result(true) - } - - override fun getRoleMember( - projectId: Int, - roleId: Int, - page: Int?, - pageSize: Int? - ): Result { - return Result( - permissionRoleMemberService.getRoleMember( - projectId = projectId, - roleId = roleId, - page = page, - pageSize = pageSize - ) - ) - } - - override fun getProjectAllMember(projectId: Int, page: Int?, pageSize: Int?): Result { - return Result(permissionRoleMemberService.getProjectAllMember(projectId, page, pageSize)) - } - - override fun deleteRoleMember( - userId: String, - projectId: Int, - roleId: Int, - managerGroup: Boolean, - members: String, - type: ManagerScopesEnum - ): Result { - Result( - permissionRoleMemberService.deleteRoleMember( - userId = userId, - projectId = projectId, - roleId = roleId, - id = members, - type = type, - managerGroup = managerGroup - ) - ) - return Result(true) - } - - override fun getUserAllGroup( - userId: String, - projectId: Int, - searchUserId: String - ): Result?> { - return Result(permissionRoleMemberService.getUserGroups(projectId, searchUserId)) - } - - override fun checkManager(userId: String, projectId: String): Result { - val result = permissionProjectService.checkProjectManager(userId, projectId) || - permissionProjectService.isProjectUser(userId, projectId, BkAuthGroup.CI_MANAGER) - return Result(result) - } -} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserProjectRoleResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserProjectRoleResourceImpl.kt deleted file mode 100644 index 04716d8c200..00000000000 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserProjectRoleResourceImpl.kt +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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.devops.auth.resources - -import com.tencent.devops.auth.api.user.UserProjectRoleResource -import com.tencent.devops.auth.pojo.DefaultGroup -import com.tencent.devops.auth.pojo.dto.ProjectRoleDTO -import com.tencent.devops.auth.pojo.vo.GroupInfoVo -import com.tencent.devops.auth.service.iam.PermissionGradeService -import com.tencent.devops.auth.service.iam.PermissionRoleService -import com.tencent.devops.common.api.exception.PermissionForbiddenException -import com.tencent.devops.common.api.pojo.Result -import com.tencent.devops.common.web.RestResource -import org.springframework.beans.factory.annotation.Autowired - -@RestResource -class UserProjectRoleResourceImpl @Autowired constructor( - val permissionRoleService: PermissionRoleService, - val permissionGradeService: PermissionGradeService -) : UserProjectRoleResource { - override fun createProjectRole( - userId: String, - projectId: Int, - projectCode: String, - groupInfo: ProjectRoleDTO - ): Result { - return Result( - permissionRoleService.createPermissionRole( - userId = userId, - projectId = projectId, - projectCode = projectCode, - groupInfo = groupInfo - ).toString() - ) - } - - override fun updateProjectRole( - userId: String, - projectId: Int, - roleId: Int, - groupInfo: ProjectRoleDTO - ): Result { - permissionRoleService.renamePermissionRole( - userId = userId, - projectId = projectId, - roleId = roleId, - groupInfo = groupInfo - ) - return Result(true) - } - - override fun getProjectRoles(userId: String, projectId: Int): Result> { - return Result(permissionRoleService.getPermissionRole(projectId)) - } - - override fun deleteProjectRole(userId: String, projectId: Int, roleId: Int): Result { - permissionRoleService.deletePermissionRole(userId, projectId, roleId) - return Result(true) - } - - override fun hashPermission(userId: String, projectId: Int): Result { - try { - permissionGradeService.checkGradeManagerUser(userId, projectId) - } catch (e: PermissionForbiddenException) { - return Result(false) - } - return Result(true) - } - - override fun getDefaultRole(userId: String): Result> { - return Result(permissionRoleService.getDefaultRole()) - } -} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ExternalAuthItsmCallbackResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/external/ExternalAuthItsmCallbackResourceImpl.kt similarity index 98% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ExternalAuthItsmCallbackResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/external/ExternalAuthItsmCallbackResourceImpl.kt index 8690520c263..7a65f7f317a 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ExternalAuthItsmCallbackResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/external/ExternalAuthItsmCallbackResourceImpl.kt @@ -26,7 +26,7 @@ * */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.external import com.tencent.devops.auth.api.callback.ExternalAuthItsmCallbackResource import com.tencent.devops.auth.pojo.ItsmCallBackInfo diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ExternalThirdLoginResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/external/ExternalThirdLoginResourceImpl.kt similarity index 97% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ExternalThirdLoginResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/external/ExternalThirdLoginResourceImpl.kt index 528f10584dc..d23dbca915a 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ExternalThirdLoginResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/external/ExternalThirdLoginResourceImpl.kt @@ -25,7 +25,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.external import com.tencent.devops.auth.api.login.ExternalThirdLoginResource import com.tencent.devops.auth.service.ThirdLoginService diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/Oauth2DesktopEndpointResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/oauth2/Oauth2DesktopEndpointResourceImpl.kt similarity index 96% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/Oauth2DesktopEndpointResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/oauth2/Oauth2DesktopEndpointResourceImpl.kt index aae663c0ef3..08a1ce1151f 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/Oauth2DesktopEndpointResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/oauth2/Oauth2DesktopEndpointResourceImpl.kt @@ -1,4 +1,4 @@ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.oauth2 import com.tencent.devops.auth.api.oauth2.Oauth2DesktopEndpointResource import com.tencent.devops.auth.pojo.dto.Oauth2AuthorizationCodeDTO diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/Oauth2ServiceEndpointResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/oauth2/Oauth2ServiceEndpointResourceImpl.kt similarity index 98% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/Oauth2ServiceEndpointResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/oauth2/Oauth2ServiceEndpointResourceImpl.kt index a4620163561..77d6006aa59 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/Oauth2ServiceEndpointResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/oauth2/Oauth2ServiceEndpointResourceImpl.kt @@ -1,4 +1,4 @@ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.oauth2 import com.tencent.devops.auth.api.oauth2.Oauth2ServiceEndpointResource import com.tencent.devops.auth.pojo.Oauth2AccessTokenRequest diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpAuthMigrateResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpAuthMigrateResourceImpl.kt similarity index 95% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpAuthMigrateResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpAuthMigrateResourceImpl.kt index b2b53bb43bd..a43804cbe06 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpAuthMigrateResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpAuthMigrateResourceImpl.kt @@ -26,7 +26,7 @@ * */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.op import com.tencent.devops.auth.api.migrate.OpAuthMigrateResource import com.tencent.devops.auth.pojo.dto.MigrateResourceDTO @@ -113,4 +113,8 @@ class OpAuthMigrateResourceImpl @Autowired constructor( override fun fixResourceGroups(projectCodes: List): Result { return Result(permissionMigrateService.fixResourceGroups(projectCodes)) } + + override fun enablePipelineListPermissionControl(projectCodes: List): Result { + return Result(permissionMigrateService.enablePipelineListPermissionControl(projectCodes)) + } } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpAuthResourceGroupPermSyncResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpAuthResourceGroupPermSyncResourceImpl.kt similarity index 98% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpAuthResourceGroupPermSyncResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpAuthResourceGroupPermSyncResourceImpl.kt index 104eb3c4cc8..fdd544d0b68 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpAuthResourceGroupPermSyncResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpAuthResourceGroupPermSyncResourceImpl.kt @@ -25,7 +25,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.op import com.tencent.devops.auth.api.sync.OpAuthResourceGroupPermSyncResource import com.tencent.devops.auth.service.iam.PermissionResourceGroupPermissionService diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpAuthResourceGroupSyncResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpAuthResourceGroupSyncResourceImpl.kt similarity index 98% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpAuthResourceGroupSyncResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpAuthResourceGroupSyncResourceImpl.kt index 40cccbbb9ba..b5bae65a264 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpAuthResourceGroupSyncResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpAuthResourceGroupSyncResourceImpl.kt @@ -25,7 +25,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.op import com.tencent.devops.auth.api.sync.OpAuthResourceGroupSyncResource import com.tencent.devops.auth.service.iam.PermissionResourceGroupSyncService diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpCallBackResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpCallBackResourceImpl.kt similarity index 96% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpCallBackResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpCallBackResourceImpl.kt index cb51a10eb15..40503d8992c 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpCallBackResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpCallBackResourceImpl.kt @@ -25,9 +25,9 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.op -import com.tencent.devops.auth.api.callback.OpCallBackResource +import com.tencent.devops.auth.api.op.OpCallBackResource import com.tencent.devops.auth.pojo.IamCallBackInfo import com.tencent.devops.auth.pojo.IamCallBackInterfaceDTO import com.tencent.devops.auth.service.CallBackService diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpManagerOrganizationResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpManagerOrganizationResourceImpl.kt similarity index 98% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpManagerOrganizationResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpManagerOrganizationResourceImpl.kt index 48e3c3b6d3a..8e6ca5763c3 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpManagerOrganizationResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpManagerOrganizationResourceImpl.kt @@ -25,7 +25,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.op import com.tencent.devops.auth.api.manager.OpManagerOrganizationResource import com.tencent.devops.auth.pojo.ManageOrganizationEntity diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpManagerStrategyResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpManagerStrategyResourceImpl.kt similarity index 98% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpManagerStrategyResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpManagerStrategyResourceImpl.kt index c4e1d48557b..8de3d17ece0 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpManagerStrategyResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpManagerStrategyResourceImpl.kt @@ -25,7 +25,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.op import com.tencent.devops.auth.api.manager.OpManagerStrategyResource import com.tencent.devops.auth.pojo.StrategyEntity diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpManagerUserResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpManagerUserResourceImpl.kt similarity index 99% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpManagerUserResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpManagerUserResourceImpl.kt index d09a76baae4..f513f1ec44f 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpManagerUserResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpManagerUserResourceImpl.kt @@ -25,7 +25,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.op import com.tencent.devops.auth.api.manager.OpManagerUserResource import com.tencent.devops.auth.pojo.ManagerUserEntity diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpOauth2ResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpOauth2ResourceImpl.kt similarity index 96% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpOauth2ResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpOauth2ResourceImpl.kt index cc5ae56a256..a99a00651bc 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpOauth2ResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpOauth2ResourceImpl.kt @@ -1,4 +1,4 @@ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.op import com.tencent.devops.auth.api.oauth2.OpOauth2Resource import com.tencent.devops.auth.pojo.dto.ClientDetailsDTO diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpPermissionFacadeResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpPermissionFacadeResourceImpl.kt similarity index 96% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpPermissionFacadeResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpPermissionFacadeResourceImpl.kt index fa1e3ae544d..d98383997c0 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpPermissionFacadeResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/op/OpPermissionFacadeResourceImpl.kt @@ -1,4 +1,4 @@ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.op import com.tencent.devops.auth.api.op.OpPermissionFacadeResource import com.tencent.devops.auth.pojo.request.CustomGroupCreateReq diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpenAuthResourceCallBackResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/open/OpenAuthResourceCallBackResourceImpl.kt similarity index 98% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpenAuthResourceCallBackResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/open/OpenAuthResourceCallBackResourceImpl.kt index 1ea7995c582..4e3a66dfe3c 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/OpenAuthResourceCallBackResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/open/OpenAuthResourceCallBackResourceImpl.kt @@ -26,7 +26,7 @@ * */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.open import com.tencent.bk.sdk.iam.dto.callback.request.CallbackRequestDTO import com.tencent.bk.sdk.iam.dto.callback.response.CallbackBaseResponseDTO diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/open/OpenPermissionAuthResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/open/OpenPermissionAuthResourceImpl.kt new file mode 100644 index 00000000000..615ff1cf000 --- /dev/null +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/open/OpenPermissionAuthResourceImpl.kt @@ -0,0 +1,293 @@ +/* + * 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.devops.auth.resources.open + +import com.tencent.devops.auth.api.open.OpenPermissionAuthResource +import com.tencent.devops.auth.service.iam.PermissionExtService +import com.tencent.devops.auth.service.iam.PermissionService +import com.tencent.devops.common.api.pojo.Result +import com.tencent.devops.common.auth.api.AuthPermission +import com.tencent.devops.common.auth.api.pojo.AuthResourceInstance +import com.tencent.devops.common.web.RestResource +import com.tencent.devops.common.web.annotation.BkApiPermission +import com.tencent.devops.common.web.constant.BkApiHandleType +import org.springframework.beans.factory.annotation.Autowired + +@RestResource +class OpenPermissionAuthResourceImpl @Autowired constructor( + val permissionService: PermissionService, + val permissionExtService: PermissionExtService +) : OpenPermissionAuthResource { + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun validateUserActionPermission( + userId: String, + token: String, + type: String?, + action: String + ): Result { + return Result(permissionService.validateUserActionPermission(userId, action)) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun validateUserResourcePermission( + userId: String, + token: String, + type: String?, + action: String, + projectCode: String, + resourceCode: String? + ): Result { + return Result(permissionService.validateUserResourcePermission(userId, action, projectCode, resourceCode)) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun validateUserResourcePermissionByRelation( + userId: String, + token: String, + type: String?, + action: String, + projectCode: String, + resourceCode: String, + resourceType: String, + relationResourceType: String? + ): Result { + return Result( + permissionService.validateUserResourcePermissionByRelation( + userId = userId, + action = action, + projectCode = projectCode, + resourceCode = resourceCode, + resourceType = resourceType, + relationResourceType = relationResourceType + ) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun validateUserResourcePermissionByInstance( + userId: String, + token: String, + type: String?, + action: String, + projectCode: String, + resource: AuthResourceInstance + ): Result { + return Result( + permissionService.validateUserResourcePermissionByInstance( + userId = userId, + action = action, + projectCode = projectCode, + resource = resource + ) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun batchValidateUserResourcePermissionByRelation( + userId: String, + token: String, + type: String?, + projectCode: String, + resourceCode: String, + resourceType: String, + relationResourceType: String?, + action: List + ): Result { + var actionCheckPermission = true + action.forEach { + val checkActionPermission = permissionService.validateUserResourcePermissionByRelation( + userId = userId, + action = it, + projectCode = projectCode, + resourceCode = resourceCode, + resourceType = resourceType, + relationResourceType = relationResourceType + ) + if (!checkActionPermission) { + actionCheckPermission = false + return@forEach + } + } + return Result(actionCheckPermission) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun getUserResourceByPermission( + userId: String, + token: String, + type: String?, + action: String, + projectCode: String, + resourceType: String + ): Result> { + return Result( + permissionService.getUserResourceByAction( + userId = userId, + action = action, + projectCode = projectCode, + resourceType = resourceType + ) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun getUserResourcesByPermissions( + userId: String, + token: String, + type: String?, + actions: List, + projectCode: String, + resourceType: String + ): Result>> { + return Result( + permissionService.getUserResourcesByActions( + userId = userId, + actions = actions, + projectCode = projectCode, + resourceType = resourceType + ) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun filterUserResourcesByPermissions( + userId: String, + token: String, + type: String?, + actions: List, + projectCode: String, + resourceType: String, + resources: List + ): Result>> { + return Result( + permissionService.filterUserResourcesByActions( + userId = userId, + actions = actions, + projectCode = projectCode, + resourceType = resourceType, + resources = resources + ) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun getUserResourceAndParentByPermission( + userId: String, + token: String, + type: String?, + action: String, + projectCode: String, + resourceType: String + ): Result>> { + return Result( + permissionService.getUserResourceAndParentByPermission( + userId = userId, + action = action, + projectCode = projectCode, + resourceType = resourceType + ) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun resourceCreateRelation( + userId: String, + token: String, + type: String?, + projectCode: String, + resourceType: String, + resourceCode: String, + resourceName: String + ): Result { + return Result( + permissionExtService.resourceCreateRelation( + userId = userId, + projectCode = projectCode, + resourceType = resourceType, + resourceCode = resourceCode, + resourceName = resourceName + ) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun resourceModifyRelation( + token: String, + type: String?, + projectCode: String, + resourceType: String, + resourceCode: String, + resourceName: String + ): Result { + return Result( + permissionExtService.resourceModifyRelation( + projectCode = projectCode, + resourceType = resourceType, + resourceCode = resourceCode, + resourceName = resourceName + ) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun resourceDeleteRelation( + token: String, + type: String?, + projectCode: String, + resourceType: String, + resourceCode: String + ): Result { + return Result( + permissionExtService.resourceDeleteRelation( + projectCode = projectCode, + resourceType = resourceType, + resourceCode = resourceCode + ) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun resourceCancelRelation( + userId: String, + token: String, + type: String?, + projectCode: String, + resourceType: String, + resourceCode: String + ): Result { + return Result( + permissionExtService.resourceCancelRelation( + userId = userId, + projectCode = projectCode, + resourceType = resourceType, + resourceCode = resourceCode + ) + ) + } +} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/open/OpenProjectAuthResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/open/OpenProjectAuthResourceImpl.kt new file mode 100644 index 00000000000..8b85c8c6797 --- /dev/null +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/open/OpenProjectAuthResourceImpl.kt @@ -0,0 +1,204 @@ +/* + * 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.devops.auth.resources.open + +import com.tencent.devops.auth.api.open.OpenProjectAuthResource +import com.tencent.devops.auth.pojo.vo.ProjectPermissionInfoVO +import com.tencent.devops.auth.service.iam.PermissionProjectService +import com.tencent.devops.common.api.pojo.Result +import com.tencent.devops.common.auth.api.pojo.BKAuthProjectRolesResources +import com.tencent.devops.common.auth.api.pojo.BkAuthGroup +import com.tencent.devops.common.auth.api.pojo.BkAuthGroupAndUserList +import com.tencent.devops.common.web.RestResource +import com.tencent.devops.common.web.annotation.BkApiPermission +import com.tencent.devops.common.web.constant.BkApiHandleType +import org.springframework.beans.factory.annotation.Autowired + +@RestResource +class OpenProjectAuthResourceImpl @Autowired constructor( + val permissionProjectService: PermissionProjectService +) : OpenProjectAuthResource { + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun getProjectUsers( + token: String, + type: String?, + projectCode: String, + group: BkAuthGroup? + ): Result> { + return Result( + permissionProjectService.getProjectUsers( + projectCode = projectCode, + group = group + ) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun getProjectGroupAndUserList( + token: String, + projectCode: String + ): Result> { + return Result( + permissionProjectService.getProjectGroupAndUserList(projectCode = projectCode) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun getUserProjects(token: String, userId: String): Result> { + return Result(permissionProjectService.getUserProjects(userId)) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun getUserProjectsByPermission( + token: String, + userId: String, + action: String, + resourceType: String? + ): Result> { + return Result( + permissionProjectService.getUserProjectsByPermission( + userId = userId, + action = action, + resourceType = resourceType + ) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun isProjectUser( + token: String, + type: String?, + userId: String, + projectCode: String, + group: BkAuthGroup? + ): Result { + return Result( + permissionProjectService.isProjectUser( + userId = userId, + projectCode = projectCode, + group = group + ) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun checkUserInProjectLevelGroup( + token: String, + userId: String, + projectCode: String + ): Result { + return Result( + permissionProjectService.checkUserInProjectLevelGroup( + userId = userId, + projectCode = projectCode + ) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun checkManager(token: String, userId: String, projectId: String): Result { + val result = permissionProjectService.checkProjectManager(userId, projectId) || + permissionProjectService.isProjectUser(userId, projectId, BkAuthGroup.CI_MANAGER) + return Result(result) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun checkProjectManager( + token: String, + type: String?, + userId: String, + projectCode: String + ): Result { + return Result( + permissionProjectService.checkProjectManager( + userId = userId, + projectCode = projectCode + ) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun createProjectUser( + token: String, + userId: String, + projectCode: String, + role: String + ): Result { + return Result( + permissionProjectService.createProjectUser( + userId = userId, + projectCode = projectCode, + roleCode = role + ) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun batchCreateProjectUser( + token: String, + userId: String, + projectCode: String, + roleCode: String, + members: List + ): Result { + return Result( + permissionProjectService.batchCreateProjectUser( + userId = userId, + projectCode = projectCode, + roleCode = roleCode, + members = members + ) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun getProjectRoles( + token: String, + projectCode: String, + projectId: String + ): Result> { + return Result( + permissionProjectService.getProjectRoles( + projectCode = projectCode, + projectId = projectId + ) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun getProjectPermissionInfo( + token: String, + projectCode: String + ): Result { + return Result( + permissionProjectService.getProjectPermissionInfo( + projectCode = projectCode + ) + ) + } +} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/open/OpenResourceMemberResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/open/OpenResourceMemberResourceImpl.kt new file mode 100644 index 00000000000..36d0953e388 --- /dev/null +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/open/OpenResourceMemberResourceImpl.kt @@ -0,0 +1,112 @@ +package com.tencent.devops.auth.resources.open + +import com.tencent.devops.auth.api.open.OpenResourceMemberResource +import com.tencent.devops.auth.service.iam.PermissionResourceMemberService +import com.tencent.devops.common.api.pojo.Result +import com.tencent.devops.common.auth.api.pojo.BkAuthGroup +import com.tencent.devops.common.auth.api.pojo.BkAuthGroupAndUserList +import com.tencent.devops.common.web.RestResource +import com.tencent.devops.common.web.annotation.BkApiPermission +import com.tencent.devops.common.web.constant.BkApiHandleType +import com.tencent.devops.project.pojo.ProjectCreateUserInfo +import com.tencent.devops.project.pojo.ProjectDeleteUserInfo +import java.util.concurrent.TimeUnit + +@RestResource +class OpenResourceMemberResourceImpl( + private val permissionResourceMemberService: PermissionResourceMemberService +) : OpenResourceMemberResource { + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun getResourceGroupMembers( + token: String, + projectCode: String, + resourceType: String, + resourceCode: String, + group: BkAuthGroup? + ): Result> { + return Result( + permissionResourceMemberService.getResourceGroupMembers( + projectCode = projectCode, + resourceType = resourceType, + resourceCode = resourceCode, + group = group + ) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun getResourceGroupAndMembers( + token: String, + projectCode: String, + resourceType: String, + resourceCode: String + ): Result> { + return Result( + permissionResourceMemberService.getResourceGroupAndMembers( + projectCode = projectCode, + resourceType = resourceType, + resourceCode = resourceCode + ) + ) + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun batchAddResourceGroupMembers( + token: String, + projectCode: String, + projectCreateUserInfo: ProjectCreateUserInfo + ): Result { + with(projectCreateUserInfo) { + val expiredTime = System.currentTimeMillis() / 1000 + TimeUnit.DAYS.toSeconds(365L) + return Result( + permissionResourceMemberService.batchAddResourceGroupMembers( + projectCode = projectCode, + iamGroupId = getIamGroupId( + groupId = groupId, + projectCode = projectCode, + roleName = roleName, + roleId = roleId + ), + expiredTime = expiredTime, + members = userIds, + departments = deptIds + ) + ) + } + } + + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun batchDeleteResourceGroupMembers( + token: String, + projectCode: String, + projectDeleteUserInfo: ProjectDeleteUserInfo + ): Result { + with(projectDeleteUserInfo) { + return Result( + permissionResourceMemberService.batchDeleteResourceGroupMembers( + projectCode = projectCode, + iamGroupId = getIamGroupId( + groupId = groupId, + projectCode = projectCode, + roleName = roleName, + roleId = roleId + ), + members = userIds, + departments = deptIds + ) + ) + } + } + + private fun getIamGroupId( + groupId: Int?, + projectCode: String, + roleName: String?, + roleId: Int? + ): Int { + return groupId ?: permissionResourceMemberService.roleCodeToIamGroupId( + projectCode = projectCode, + roleCode = roleName ?: BkAuthGroup.getByRoleId(roleId!!).value + ) + } +} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ServiceAuthResourceCallBackResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceAuthResourceCallBackResourceImpl.kt similarity index 98% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ServiceAuthResourceCallBackResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceAuthResourceCallBackResourceImpl.kt index 48ef616ce6e..55bed829a21 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ServiceAuthResourceCallBackResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceAuthResourceCallBackResourceImpl.kt @@ -26,7 +26,7 @@ * */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.service import com.tencent.bk.sdk.iam.dto.callback.request.CallbackRequestDTO import com.tencent.bk.sdk.iam.dto.callback.response.CallbackBaseResponseDTO diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ServiceManagerApprovalResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceManagerApprovalResourceImpl.kt similarity index 98% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ServiceManagerApprovalResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceManagerApprovalResourceImpl.kt index 7e629317eb5..b1a6b52309f 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ServiceManagerApprovalResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceManagerApprovalResourceImpl.kt @@ -25,7 +25,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.service import com.tencent.devops.auth.api.manager.ServiceManagerApprovalResource import com.tencent.devops.auth.pojo.enum.ApprovalType diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceManagerResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceManagerResourceImpl.kt deleted file mode 100644 index 9ecb56fb985..00000000000 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceManagerResourceImpl.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.devops.auth.resources.service - -import com.tencent.devops.auth.api.service.ServiceManagerResource -import com.tencent.devops.auth.service.SuperManagerService -import com.tencent.devops.common.api.pojo.Result -import com.tencent.devops.common.web.RestResource -import com.tencent.devops.common.web.annotation.BkApiPermission -import com.tencent.devops.common.web.constant.BkApiHandleType -import org.springframework.beans.factory.annotation.Autowired - -@RestResource -class ServiceManagerResourceImpl @Autowired constructor( - val superManagerService: SuperManagerService -) : ServiceManagerResource { - @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) - override fun validateManagerPermission( - userId: String, - token: String, - projectCode: String, - action: String, - resourceCode: String - ): Result { - return Result(superManagerService.projectManagerCheck( - userId = userId, - projectCode = projectCode, - action = action, - resourceType = resourceCode - )) - } -} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ServiceManagerUserResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceManagerUserResourceImpl.kt similarity index 97% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ServiceManagerUserResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceManagerUserResourceImpl.kt index fb88a0d1791..27b7ef299e2 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ServiceManagerUserResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceManagerUserResourceImpl.kt @@ -25,7 +25,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.service import com.tencent.devops.auth.api.manager.ServiceManagerUserResource import com.tencent.devops.auth.pojo.UserPermissionInfo diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ServiceMonitorSpaceResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceMonitorSpaceResourceImpl.kt similarity index 98% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ServiceMonitorSpaceResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceMonitorSpaceResourceImpl.kt index 194e40946ab..763b09e70b2 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/ServiceMonitorSpaceResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceMonitorSpaceResourceImpl.kt @@ -25,7 +25,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.service import com.tencent.devops.auth.api.service.ServiceMonitorSpaceResource import com.tencent.devops.auth.service.AuthMonitorSpaceService diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServicePermissionAuthResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServicePermissionAuthResourceImpl.kt index 0772fa5cfb7..24eb78b3c4e 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServicePermissionAuthResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServicePermissionAuthResourceImpl.kt @@ -28,9 +28,7 @@ package com.tencent.devops.auth.resources.service import com.tencent.devops.auth.api.service.ServicePermissionAuthResource -import com.tencent.devops.auth.pojo.dto.GrantInstanceDTO import com.tencent.devops.auth.service.iam.PermissionExtService -import com.tencent.devops.auth.service.iam.PermissionGrantService import com.tencent.devops.auth.service.iam.PermissionService import com.tencent.devops.common.api.pojo.Result import com.tencent.devops.common.auth.api.AuthPermission @@ -43,8 +41,7 @@ import org.springframework.beans.factory.annotation.Autowired @RestResource class ServicePermissionAuthResourceImpl @Autowired constructor( val permissionService: PermissionService, - val permissionExtService: PermissionExtService, - val permissionGrantService: PermissionGrantService + val permissionExtService: PermissionExtService ) : ServicePermissionAuthResource { @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) @@ -293,19 +290,4 @@ class ServicePermissionAuthResourceImpl @Autowired constructor( ) ) } - - @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) - override fun grantInstancePermission( - userId: String, - token: String, - projectCode: String, - grantInstance: GrantInstanceDTO - ): Result { - return Result( - permissionGrantService.grantInstancePermission( - projectId = projectCode, - grantInfo = grantInstance - ) - ) - } } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceResourceGroupResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceResourceGroupResourceImpl.kt index f336c2275ff..cea4a8350a8 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceResourceGroupResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceResourceGroupResourceImpl.kt @@ -3,9 +3,12 @@ package com.tencent.devops.auth.resources.service import com.tencent.devops.auth.api.service.ServiceResourceGroupResource import com.tencent.devops.auth.pojo.dto.GroupAddDTO import com.tencent.devops.auth.pojo.request.CustomGroupCreateReq +import com.tencent.devops.auth.pojo.vo.GroupDetailsInfoVo import com.tencent.devops.auth.pojo.vo.GroupPermissionDetailVo +import com.tencent.devops.auth.service.iam.PermissionResourceGroupAndMemberFacadeService import com.tencent.devops.auth.service.iam.PermissionResourceGroupPermissionService import com.tencent.devops.auth.service.iam.PermissionResourceGroupService +import com.tencent.devops.common.api.model.SQLPage import com.tencent.devops.common.api.pojo.Result import com.tencent.devops.common.auth.api.pojo.BkAuthGroup import com.tencent.devops.common.web.RestResource @@ -13,7 +16,8 @@ import com.tencent.devops.common.web.RestResource @RestResource class ServiceResourceGroupResourceImpl( val permissionResourceGroupService: PermissionResourceGroupService, - val resourceGroupPermissionService: PermissionResourceGroupPermissionService + val resourceGroupPermissionService: PermissionResourceGroupPermissionService, + val resourceGroupAndMemberFacadeService: PermissionResourceGroupAndMemberFacadeService ) : ServiceResourceGroupResource { override fun getGroupPermissionDetail( projectCode: String, @@ -26,6 +30,37 @@ class ServiceResourceGroupResourceImpl( ) } + override fun getMemberGroupsDetails( + userId: String, + projectCode: String, + resourceType: String, + memberId: String, + groupName: String?, + minExpiredAt: Long?, + maxExpiredAt: Long?, + relatedResourceType: String?, + relatedResourceCode: String?, + action: String?, + start: Int?, + limit: Int? + ): Result> { + return Result( + resourceGroupAndMemberFacadeService.getMemberGroupsDetails( + projectId = projectCode, + resourceType = resourceType, + memberId = memberId, + groupName = groupName, + minExpiredAt = minExpiredAt, + maxExpiredAt = maxExpiredAt, + relatedResourceType = relatedResourceType, + relatedResourceCode = relatedResourceCode, + action = action, + start = start, + limit = limit + ) + ) + } + override fun createGroupByGroupCode( projectCode: String, resourceType: String, diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceResourceMemberResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceResourceMemberResourceImpl.kt index c9946730b02..02c0636974f 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceResourceMemberResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceResourceMemberResourceImpl.kt @@ -1,6 +1,7 @@ package com.tencent.devops.auth.resources.service import com.tencent.devops.auth.api.service.ServiceResourceMemberResource +import com.tencent.devops.auth.pojo.request.GroupMemberSingleRenewalReq import com.tencent.devops.auth.service.iam.PermissionResourceMemberService import com.tencent.devops.common.api.pojo.Result import com.tencent.devops.common.auth.api.pojo.BkAuthGroup @@ -98,6 +99,22 @@ class ServiceResourceMemberResourceImpl constructor( } } + @BkApiPermission([BkApiHandleType.API_OPEN_TOKEN_CHECK]) + override fun renewalGroupMember( + token: String, + userId: String, + projectCode: String, + renewalConditionReq: GroupMemberSingleRenewalReq + ): Result { + return Result( + permissionResourceMemberService.renewalGroupMember( + userId = userId, + projectCode = projectCode, + renewalConditionReq = renewalConditionReq + ) + ) + } + private fun getIamGroupId( groupId: Int?, projectCode: String, diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthApplyResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthApplyResourceImpl.kt similarity index 98% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthApplyResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthApplyResourceImpl.kt index 69fa55a4677..10c63c3c4c3 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthApplyResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthApplyResourceImpl.kt @@ -1,4 +1,4 @@ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.user import com.tencent.devops.auth.api.user.UserAuthApplyResource import com.tencent.devops.auth.pojo.ApplyJoinGroupInfo diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthAuthorizationResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthAuthorizationResourceImpl.kt similarity index 98% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthAuthorizationResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthAuthorizationResourceImpl.kt index a184fd8cf63..e4c5ed68aa4 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthAuthorizationResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthAuthorizationResourceImpl.kt @@ -1,4 +1,4 @@ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.user import com.tencent.devops.auth.api.user.UserAuthAuthorizationResource import com.tencent.devops.auth.pojo.vo.ResourceTypeInfoVo diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthItsmCallbackResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthItsmCallbackResourceImpl.kt similarity index 98% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthItsmCallbackResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthItsmCallbackResourceImpl.kt index c55888d8d7e..1104942b842 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthItsmCallbackResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthItsmCallbackResourceImpl.kt @@ -26,7 +26,7 @@ * */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.user import com.tencent.devops.auth.api.user.UserAuthItsmCallbackResource import com.tencent.devops.auth.dao.AuthItsmCallbackDao diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthPermissionResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthPermissionResourceImpl.kt similarity index 96% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthPermissionResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthPermissionResourceImpl.kt index 16f15f2ac5c..a9aa3d540af 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthPermissionResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthPermissionResourceImpl.kt @@ -1,4 +1,4 @@ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.user import com.tencent.devops.auth.api.user.UserAuthPermissionResource import com.tencent.devops.auth.pojo.dto.PermissionBatchValidateDTO diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceGroupResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceGroupResourceImpl.kt similarity index 99% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceGroupResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceGroupResourceImpl.kt index a49c0327503..4c99458fb86 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceGroupResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceGroupResourceImpl.kt @@ -26,7 +26,7 @@ * */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.user import com.tencent.bk.sdk.iam.constants.ManagerScopesEnum import com.tencent.devops.auth.api.user.UserAuthResourceGroupResource diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceGroupSyncResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceGroupSyncResourceImpl.kt similarity index 98% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceGroupSyncResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceGroupSyncResourceImpl.kt index 4a98618ce51..46240f0f357 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceGroupSyncResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceGroupSyncResourceImpl.kt @@ -25,7 +25,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.user import com.tencent.devops.auth.api.user.UserAuthResourceGroupSyncResource import com.tencent.devops.auth.pojo.enum.AuthMigrateStatus @@ -40,7 +40,6 @@ class UserAuthResourceGroupSyncResourceImpl @Autowired constructor( private val permissionResourceGroupSyncService: PermissionResourceGroupSyncService, private val permissionResourceGroupPermissionService: PermissionResourceGroupPermissionService ) : UserAuthResourceGroupSyncResource { - override fun syncGroupAndMember(userId: String, projectId: String): Result { permissionResourceGroupSyncService.syncGroupAndMember(projectId) return Result(true) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceMemberResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceMemberResourceImpl.kt similarity index 99% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceMemberResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceMemberResourceImpl.kt index 4672c1c64ea..bd76fea6919 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceMemberResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceMemberResourceImpl.kt @@ -1,4 +1,4 @@ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.user import com.tencent.devops.auth.api.user.UserAuthResourceMemberResource import com.tencent.devops.auth.pojo.ResourceMemberInfo diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceResourceImpl.kt similarity index 99% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceResourceImpl.kt index 5ee46656fe5..7df892506cc 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthResourceResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserAuthResourceResourceImpl.kt @@ -26,7 +26,7 @@ * */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.user import com.tencent.devops.auth.api.user.UserAuthResourceResource import com.tencent.devops.auth.pojo.AuthResourceInfo diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserManagerUserResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserManagerUserResourceImpl.kt similarity index 97% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserManagerUserResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserManagerUserResourceImpl.kt index 53b41873b99..f8d999bc42b 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserManagerUserResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserManagerUserResourceImpl.kt @@ -25,7 +25,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.user import com.tencent.devops.auth.api.manager.UserManagerUserResource import com.tencent.devops.auth.service.ManagerUserService diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserMonitorSpaceResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserMonitorSpaceResourceImpl.kt similarity index 97% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserMonitorSpaceResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserMonitorSpaceResourceImpl.kt index 62803cfb6eb..d7e01441fcf 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserMonitorSpaceResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserMonitorSpaceResourceImpl.kt @@ -25,7 +25,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.user import com.tencent.devops.auth.api.user.UserMonitorSpaceResource import com.tencent.devops.auth.service.AuthMonitorSpaceService diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthUrlResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserProjectMemberResourceImpl.kt similarity index 70% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthUrlResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserProjectMemberResourceImpl.kt index 15fddef4fc7..8b52a40e862 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserAuthUrlResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserProjectMemberResourceImpl.kt @@ -25,24 +25,22 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.user -import com.tencent.devops.auth.api.user.UserAuthUrlResource -import com.tencent.devops.auth.pojo.PermissionUrlDTO -import com.tencent.devops.auth.service.iam.PermissionUrlService +import com.tencent.devops.auth.api.user.UserProjectMemberResource +import com.tencent.devops.auth.service.iam.PermissionProjectService import com.tencent.devops.common.api.pojo.Result +import com.tencent.devops.common.auth.api.pojo.BkAuthGroup import com.tencent.devops.common.web.RestResource import org.springframework.beans.factory.annotation.Autowired @RestResource -class UserAuthUrlResourceImpl @Autowired constructor( - val urlService: PermissionUrlService -) : UserAuthUrlResource { - override fun permissionUrl(permissionUrlDTO: List): Result { - return urlService.getPermissionUrl(permissionUrlDTO) - } - - override fun getRolePermissionUrl(projectId: String, roleId: String?): Result { - return Result(urlService.getRolePermissionUrl(projectId, roleId)) +class UserProjectMemberResourceImpl @Autowired constructor( + val permissionProjectService: PermissionProjectService +) : UserProjectMemberResource { + override fun checkManager(userId: String, projectId: String): Result { + val result = permissionProjectService.checkProjectManager(userId, projectId) || + permissionProjectService.isProjectUser(userId, projectId, BkAuthGroup.CI_MANAGER) + return Result(result) } } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserThirdLoginResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserThirdLoginResourceImpl.kt similarity index 97% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserThirdLoginResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserThirdLoginResourceImpl.kt index 02dfe4a015a..58c58d0a229 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserThirdLoginResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserThirdLoginResourceImpl.kt @@ -25,7 +25,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.user import com.tencent.devops.auth.api.login.UserThirdLoginResource import com.tencent.devops.auth.service.ThirdLoginService diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserTokenResourceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserTokenResourceImpl.kt similarity index 97% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserTokenResourceImpl.kt rename to src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserTokenResourceImpl.kt index f5bea0b2c96..6bc3e14768a 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/UserTokenResourceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/user/UserTokenResourceImpl.kt @@ -25,7 +25,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.resources +package com.tencent.devops.auth.resources.user import com.tencent.devops.auth.api.user.UserTokenResource import com.tencent.devops.auth.pojo.TokenInfo diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/AuthDeptServiceImpl.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/AuthDeptServiceImpl.kt index bedb4278b84..2c95c78278d 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/AuthDeptServiceImpl.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/AuthDeptServiceImpl.kt @@ -180,12 +180,14 @@ class AuthDeptServiceImpl @Autowired constructor( ) } } + ManagerScopesEnum.DEPARTMENT -> { val deptInfos = getDeptInfo(deptSearch) deptInfos.results.forEach { it.toUserAndDeptInfoVo() } } + ManagerScopesEnum.ALL -> { val userInfos = getUserInfo(userSearch) userInfos.results.forEach { @@ -271,6 +273,7 @@ class AuthDeptServiceImpl @Autowired constructor( ) } + @Suppress("NestedBlockDepth") override fun listMemberInfos( memberIds: List, memberType: ManagerScopesEnum @@ -280,12 +283,15 @@ class AuthDeptServiceImpl @Autowired constructor( if (membersNotInCache.isNotEmpty()) { val fetchedMembers = fetchMemberInfos(membersNotInCache, memberType) - fetchedMembers.forEach { memberInfoCache.put(it.name, it) } - - if (memberType == ManagerScopesEnum.USER) { - val departedMembers = membersNotInCache.subtract(fetchedMembers.map { it.name }.toSet()) - if (departedMembers.isNotEmpty()) { - departedMembersCache.addAll(departedMembers) + fetchedMembers.forEach { + if (it.type == ManagerScopesEnum.USER) { + memberInfoCache.put(it.name, it) + val departedMembers = membersNotInCache.subtract(fetchedMembers.map { it.name }.toSet()) + if (departedMembers.isNotEmpty()) { + departedMembersCache.addAll(departedMembers) + } + } else { + memberInfoCache.put(it.id.toString(), it) } } } @@ -327,6 +333,7 @@ class AuthDeptServiceImpl @Autowired constructor( ) getUserInfo(userSearch).results.map { it.toUserAndDeptInfoVo() } } + ManagerScopesEnum.DEPARTMENT -> { val deptSearch = SearchUserAndDeptEntity( bk_app_code = appCode!!, @@ -338,6 +345,7 @@ class AuthDeptServiceImpl @Autowired constructor( ) getDeptInfo(deptSearch).results.map { it.toUserAndDeptInfoVo() } } + else -> emptyList() } return memberInfos diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/AuthGroupService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/AuthGroupService.kt deleted file mode 100644 index 7e778946edb..00000000000 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/AuthGroupService.kt +++ /dev/null @@ -1,185 +0,0 @@ -/* - * 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.devops.auth.service - -import com.tencent.devops.auth.constant.AuthMessageCode -import com.tencent.devops.auth.dao.AuthGroupDao -import com.tencent.devops.auth.entity.GroupCreateInfo -import com.tencent.devops.auth.pojo.dto.GroupDTO -import com.tencent.devops.auth.pojo.dto.ProjectRoleDTO -import com.tencent.devops.common.api.exception.OperationException -import com.tencent.devops.common.api.exception.ParamBlankException -import com.tencent.devops.common.api.pojo.Result -import com.tencent.devops.common.auth.api.pojo.DefaultGroupType -import com.tencent.devops.common.web.utils.I18nUtil -import com.tencent.devops.model.auth.tables.records.TAuthGroupInfoRecord -import org.jooq.DSLContext -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.stereotype.Service - -@Service -class AuthGroupService @Autowired constructor( - val dslContext: DSLContext, - val groupDao: AuthGroupDao -) { - fun createGroup( - userId: String, - projectCode: String, - groupInfo: GroupDTO - ): Int { - logger.info("createGroup : userId = $userId| projectCode = $projectCode | groupInfo = $groupInfo") - val groupRecord = groupDao.getGroup( - dslContext = dslContext, - projectCode = projectCode, - groupCode = groupInfo.groupCode - ) - if (groupRecord != null) { - // 项目下分组已存在,不能重复创建 - logger.warn( - "group is exsit, don't create repeatedly : userId = $userId | " + - "projectCode = $projectCode | groupInfo = $groupInfo " - ) - throw OperationException( - I18nUtil.getCodeLanMessage( - messageCode = AuthMessageCode.GROUP_EXIST, - language = I18nUtil.getLanguage(userId) - ) - ) - } - val groupCreateInfo = GroupCreateInfo( - groupCode = groupInfo.groupCode, - groupType = groupInfo.groupType, - groupName = groupInfo.groupName, - projectCode = projectCode, - relationId = groupInfo.relationId, - displayName = groupInfo.displayName, - user = userId - ) - return groupDao.createGroup(dslContext, groupCreateInfo) - } - - fun batchCreate( - userId: String, - projectCode: String, - groupInfos: List - ): Result { - val groupCodes = groupInfos.map { it.groupCode } - val groupRecord = groupDao.getGroupByCodes( - dslContext = dslContext, - projectCode = projectCode, - groupCodes = groupCodes - ) - if (groupRecord.isNotEmpty) { - // 项目下分组已存在,不能重复创建 - logger.warn( - "group is exsit, don't create repeatedly : userId = $userId | " + - "projectCode = $projectCode | groupInfo = $groupCodes " - ) - throw OperationException( - I18nUtil.getCodeLanMessage(AuthMessageCode.GROUP_EXIST, language = I18nUtil.getLanguage(userId)) - ) - } - val groupCreateInfos = mutableListOf() - groupInfos.forEach { - val groupCreateInfo = GroupCreateInfo( - groupCode = it.groupCode, - groupType = it.groupType, - groupName = it.groupName, - projectCode = projectCode, - relationId = it.relationId, - displayName = it.displayName, - user = userId - ) - groupCreateInfos.add(groupCreateInfo) - } - groupDao.batchCreateGroups(dslContext, groupCreateInfos) - return Result(true) - } - - fun updateGroupName(userId: String, groupId: Int, groupInfo: ProjectRoleDTO): Int { - val groupEntity = groupDao.getGroupById(dslContext, groupId) - ?: throw ParamBlankException("group not exist : groupId = $groupId") - - if (DefaultGroupType.contains(groupEntity.groupCode)) { - throw ParamBlankException(AuthMessageCode.DEFAULT_GROUP_UPDATE_NAME_ERROR) - } - - return groupDao.update( - dslContext, - groupEntity.id, - groupInfo.name, - groupInfo.displayName ?: groupInfo.name, - userId - ) - } - - fun getGroupCode(groupId: Int): TAuthGroupInfoRecord? { - return groupDao.getGroupById(dslContext, groupId) - } - - fun getGroupByName(projectCode: String, groupName: String): TAuthGroupInfoRecord? { - return groupDao.getGroupByName(dslContext, projectCode, groupName) - } - - fun getGroupByCode(projectCode: String, groupCode: String): TAuthGroupInfoRecord? { - return groupDao.getGroup(dslContext, projectCode, groupCode) - } - - fun getGroupByProject(projectCode: String): List? { - return groupDao.getGroupByProject(dslContext, projectCode) - } - - fun bindRelationId(id: Int, relationId: String): Int { - return groupDao.updateRelationId(dslContext, id, relationId) - } - - fun getRelationId(roleId: Int): String? { - val groupInfo = groupDao.getRelationId(dslContext, roleId) ?: return null - return groupInfo.relationId!! - } - - fun deleteGroup(id: Int, softDelete: Boolean? = true) { - if (softDelete!!) { - groupDao.softDelete(dslContext, id) - } else { - groupDao.deleteRole(dslContext, id) - } - } - - fun getGroupByRelationIds(relationIds: List): List { - return groupDao.getGroupByRelationIds( - dslContext = dslContext, - relationIds = relationIds - ) - } - - companion object { - private val logger = LoggerFactory.getLogger(AuthGroupService::class.java) - } -} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/GroupUserService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/GroupUserService.kt deleted file mode 100644 index 81a5a3fb12a..00000000000 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/GroupUserService.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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.devops.auth.service - -import com.tencent.devops.auth.constant.AuthMessageCode -import com.tencent.devops.auth.dao.AuthGroupUserDao -import com.tencent.devops.common.api.exception.OperationException -import com.tencent.devops.common.api.pojo.Result -import com.tencent.devops.common.web.utils.I18nUtil -import org.jooq.DSLContext -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.stereotype.Service - -@Service -class GroupUserService @Autowired constructor( - val dslContext: DSLContext, - val authGroupService: AuthGroupService, - val groupUserDao: AuthGroupUserDao -) { - fun addUser2Group(userId: String, groupId: Int): Result { - logger.info("addUser2Group |$userId| $groupId") - val groupUserRecord = groupUserDao.get( - dslContext = dslContext, - userId = userId, - groupId = groupId.toString() - ) - if (groupUserRecord != null) { - logger.warn("addUser2Group user $userId already in this group $groupId") - throw OperationException( - I18nUtil.getCodeLanMessage( - AuthMessageCode.GROUP_USER_ALREADY_EXIST, - language = I18nUtil.getLanguage(userId) - ) - ) - } - val groupRecord = authGroupService.getGroupCode(groupId) - - if (groupRecord == null) { - logger.warn("addUser2Group group $groupId is not exist") - throw OperationException( - I18nUtil.getCodeLanMessage(AuthMessageCode.GROUP_NOT_EXIST, language = I18nUtil.getLanguage(userId)) - ) - } - // 添加用户至用户组 - groupUserDao.create( - dslContext = dslContext, - userId = userId, - groupId = groupId.toString() - ) - return Result(true) - } - - companion object { - private val logger = LoggerFactory.getLogger(GroupUserService::class.java) - } -} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionGrantService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionGrantService.kt deleted file mode 100644 index 3c9a3304b28..00000000000 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionGrantService.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.tencent.devops.auth.service.iam - -import com.tencent.devops.auth.pojo.dto.GrantInstanceDTO - -interface PermissionGrantService { - fun grantInstancePermission( - projectId: String, - grantInfo: GrantInstanceDTO - ): Boolean -} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionMigrateService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionMigrateService.kt index 477cc8fe7c4..f9769b90ef3 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionMigrateService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionMigrateService.kt @@ -117,4 +117,9 @@ interface PermissionMigrateService { * 修复资源组数据,存在同步iam资源组数据,数据库 iam组id为NULL的情况,需要进行修复 */ fun fixResourceGroups(projectCodes: List): Boolean + + /** + * 开启流水线列表权限控制开关 + */ + fun enablePipelineListPermissionControl(projectCodes: List): Boolean } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceGroupSyncService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceGroupSyncService.kt index c9e5f5c8157..cdc21a05d0c 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceGroupSyncService.kt +++ b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionResourceGroupSyncService.kt @@ -49,6 +49,11 @@ interface PermissionResourceGroupSyncService { */ fun syncGroupAndMember(projectCode: String) + /** + * 同步项目下组 + */ + fun syncProjectGroup(projectCode: String) + /** * 获取项目的同步状态 */ diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionRoleMemberService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionRoleMemberService.kt deleted file mode 100644 index 9c1f7bcdee1..00000000000 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionRoleMemberService.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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.devops.auth.service.iam - -import com.tencent.bk.sdk.iam.constants.ManagerScopesEnum -import com.tencent.bk.sdk.iam.dto.manager.ManagerRoleGroupInfo -import com.tencent.bk.sdk.iam.dto.manager.vo.ManagerGroupMemberVo -import com.tencent.devops.auth.pojo.dto.RoleMemberDTO -import com.tencent.devops.auth.pojo.vo.ProjectMembersVO - -interface PermissionRoleMemberService { - fun createRoleMember( - userId: String, - projectId: Int, - roleId: Int, - members: List, - managerGroup: Boolean, - checkAGradeManager: Boolean? = true - ) - - fun deleteRoleMember( - userId: String, - projectId: Int, - roleId: Int, - id: String, - type: ManagerScopesEnum, - managerGroup: Boolean - ) - - fun getRoleMember(projectId: Int, roleId: Int, page: Int?, pageSize: Int?): ManagerGroupMemberVo - - fun getProjectAllMember(projectId: Int, page: Int?, pageSize: Int?): ProjectMembersVO? - - fun getUserGroups(projectId: Int, userId: String): List? -} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionRoleService.kt b/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionRoleService.kt deleted file mode 100644 index 5feb43e5d95..00000000000 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionRoleService.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.devops.auth.service.iam - -import com.tencent.devops.auth.pojo.DefaultGroup -import com.tencent.devops.auth.pojo.dto.ProjectRoleDTO -import com.tencent.devops.auth.pojo.vo.GroupInfoVo - -interface PermissionRoleService { - fun createPermissionRole(userId: String, projectId: Int, projectCode: String, groupInfo: ProjectRoleDTO): Int - - fun renamePermissionRole(userId: String, projectId: Int, roleId: Int, groupInfo: ProjectRoleDTO) - - fun getPermissionRole(projectId: Int): List - - fun deletePermissionRole(userId: String, projectId: Int, roleId: Int) - - fun getDefaultRole(): List -} diff --git a/src/backend/ci/core/common/common-api/src/main/kotlin/com/tencent/devops/common/api/constant/CommonMessageCode.kt b/src/backend/ci/core/common/common-api/src/main/kotlin/com/tencent/devops/common/api/constant/CommonMessageCode.kt index 7bc6dd624e7..84dd2c98865 100644 --- a/src/backend/ci/core/common/common-api/src/main/kotlin/com/tencent/devops/common/api/constant/CommonMessageCode.kt +++ b/src/backend/ci/core/common/common-api/src/main/kotlin/com/tencent/devops/common/api/constant/CommonMessageCode.kt @@ -185,6 +185,7 @@ object CommonMessageCode { const val SVN_TOKEN_FAIL = "2100135" // SVN Token 不正确 const val SVN_TOKEN_EMPTY = "2100136" // SVN Token 为空, 请检查代码库的凭证类型 + const val ERROR_VARIABLE_NOT_FOUND = "2100137" // SVN Token 为空, 请检查代码库的凭证类型 const val BK_CONTAINER_TIMED_OUT = "bkContainerTimedOut" // 创建容器超时 const val BK_CREATION_FAILED_EXCEPTION_INFORMATION = "bkCreationFailedExceptionInformation" // 创建失败,异常信息 diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionUrlService.kt b/src/backend/ci/core/common/common-api/src/main/kotlin/com/tencent/devops/common/api/exception/VariableNotFoundException.kt similarity index 76% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionUrlService.kt rename to src/backend/ci/core/common/common-api/src/main/kotlin/com/tencent/devops/common/api/exception/VariableNotFoundException.kt index 3cc8e37624d..2cb2cd196a9 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionUrlService.kt +++ b/src/backend/ci/core/common/common-api/src/main/kotlin/com/tencent/devops/common/api/exception/VariableNotFoundException.kt @@ -25,13 +25,19 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.service.iam +package com.tencent.devops.common.api.exception -import com.tencent.devops.auth.pojo.PermissionUrlDTO -import com.tencent.devops.common.api.pojo.Result +import com.tencent.devops.common.api.constant.CommonMessageCode -interface PermissionUrlService { - fun getPermissionUrl(permissionUrlDTO: List): Result - - fun getRolePermissionUrl(projectId: String, groupId: String?): String? -} +/** + * 变量不存在异常 + */ +class VariableNotFoundException( + val variableKey: String?, + errorCode: String = CommonMessageCode.ERROR_VARIABLE_NOT_FOUND +) : + ErrorCodeException( + errorCode = errorCode, + defaultMessage = "variable $variableKey not found", + params = variableKey?.let { arrayOf(it) } + ) diff --git a/src/backend/ci/core/common/common-api/src/main/kotlin/com/tencent/devops/common/api/pojo/PipelineAsCodeSettings.kt b/src/backend/ci/core/common/common-api/src/main/kotlin/com/tencent/devops/common/api/pojo/PipelineAsCodeSettings.kt index 0ee3d4d9e45..a31760df568 100644 --- a/src/backend/ci/core/common/common-api/src/main/kotlin/com/tencent/devops/common/api/pojo/PipelineAsCodeSettings.kt +++ b/src/backend/ci/core/common/common-api/src/main/kotlin/com/tencent/devops/common/api/pojo/PipelineAsCodeSettings.kt @@ -32,5 +32,35 @@ import io.swagger.v3.oas.annotations.media.Schema @Schema(title = "设置-YAML流水线功能设置") data class PipelineAsCodeSettings( @get:Schema(title = "是否支持YAML流水线功能", required = true) - val enable: Boolean = false -) + val enable: Boolean = false, + @get:Schema(title = "项目级流水线语法风格", required = false) + var projectDialect: String? = null, + @get:Schema(title = "是否继承项目流水线语言风格", required = false) + val inheritedDialect: Boolean? = true, + @get:Schema(title = "流水线语言风格", required = false) + var pipelineDialect: String? = null +) { + companion object { + fun initDialect(inheritedDialect: Boolean?, pipelineDialect: String?): PipelineAsCodeSettings { + return PipelineAsCodeSettings( + inheritedDialect = inheritedDialect ?: true, + // 如果继承项目方言配置,置空pipelineDialect字段,防止数据库存储多余数据 + pipelineDialect = if (inheritedDialect == false) { + pipelineDialect + } else { + null + } + ) + } + } + + /** + * 入库时,重置方言字段值 + */ + fun resetDialect() { + projectDialect = null + if (inheritedDialect != false) { + pipelineDialect = null + } + } +} diff --git a/src/backend/ci/core/common/common-event/src/main/kotlin/com/tencent/devops/common/event/enums/PipelineBuildStatusBroadCastEventType.kt b/src/backend/ci/core/common/common-event/src/main/kotlin/com/tencent/devops/common/event/enums/PipelineBuildStatusBroadCastEventType.kt new file mode 100644 index 00000000000..ec680a6bedc --- /dev/null +++ b/src/backend/ci/core/common/common-event/src/main/kotlin/com/tencent/devops/common/event/enums/PipelineBuildStatusBroadCastEventType.kt @@ -0,0 +1,16 @@ +package com.tencent.devops.common.event.enums + +enum class PipelineBuildStatusBroadCastEventType { + BUILD_QUEUE, /*构建排队,包含并发超限时排队、并发组排队。*/ + BUILD_START, /*构建开始,不包含并发超限时排队、并发组排队。*/ + BUILD_END, /*构建结束*/ + BUILD_STAGE_START, /*stage开始*/ + BUILD_STAGE_END, /*stage结束*/ + BUILD_JOB_QUEUE, /*job排队,包含互斥组排队、构建机复用互斥排队、最大job并发排队。*/ + BUILD_JOB_START, /*job开始,不包含BUILD_JOB_QUEUE。如果job SKIP或没有可执行的插件,就不会有该事件。*/ + BUILD_JOB_END, /*job结束,job SKIP或没有可执行的插件时会有该事件。*/ + BUILD_AGENT_START, /*构建机启动,现在仅包含第三方构建机*/ + BUILD_TASK_START, /*插件开始*/ + BUILD_TASK_END, /*插件结束*/ + BUILD_TASK_PAUSE; /*插件前置暂停*/ +} diff --git a/src/backend/ci/core/common/common-event/src/main/kotlin/com/tencent/devops/common/event/pojo/pipeline/PipelineBuildStatusBroadCastEvent.kt b/src/backend/ci/core/common/common-event/src/main/kotlin/com/tencent/devops/common/event/pojo/pipeline/PipelineBuildStatusBroadCastEvent.kt index 2260a9386c1..72c5bb74df3 100644 --- a/src/backend/ci/core/common/common-event/src/main/kotlin/com/tencent/devops/common/event/pojo/pipeline/PipelineBuildStatusBroadCastEvent.kt +++ b/src/backend/ci/core/common/common-event/src/main/kotlin/com/tencent/devops/common/event/pojo/pipeline/PipelineBuildStatusBroadCastEvent.kt @@ -29,6 +29,7 @@ package com.tencent.devops.common.event.pojo.pipeline import com.tencent.devops.common.event.annotation.Event import com.tencent.devops.common.event.enums.ActionType +import com.tencent.devops.common.event.enums.PipelineBuildStatusBroadCastEventType import com.tencent.devops.common.stream.constants.StreamBinding import java.time.LocalDateTime @@ -51,6 +52,8 @@ data class PipelineBuildStatusBroadCastEvent( val buildStatus: String?, val atomCode: String? = null, val eventTime: LocalDateTime? = LocalDateTime.now(), + val type: PipelineBuildStatusBroadCastEventType? = null, + val labels: Map? = null, override var actionType: ActionType, override var delayMills: Int = 0 ) : IPipelineEvent(actionType, source, projectId, pipelineId, userId, delayMills) diff --git a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/ExpressionException.kt b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/ExpressionException.kt index 37d42ccbd2c..48188a1aedb 100644 --- a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/ExpressionException.kt +++ b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/ExpressionException.kt @@ -82,12 +82,6 @@ class FunctionFormatException(override val message: String?) : ExpressionExcepti } } -class ContextNotFoundException(override val message: String?) : ExpressionException() { - companion object { - fun contextNameNotFound(arg0: String) = ContextNotFoundException( - "Expression context $arg0 not found." - ) - } -} +class ContextNotFoundException(override var message: String? = null) : ExpressionException() class ContextJsonFormatException(override val message: String?) : ExpressionException() diff --git a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/ExpressionParser.kt b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/ExpressionParser.kt index 7e473d70363..dd71969721e 100644 --- a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/ExpressionParser.kt +++ b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/ExpressionParser.kt @@ -27,9 +27,11 @@ package com.tencent.devops.common.expression +import com.tencent.devops.common.expression.ExpressionParser.legalizeExpression import com.tencent.devops.common.expression.context.ContextValueNode import com.tencent.devops.common.expression.context.DictionaryContextData import com.tencent.devops.common.expression.context.PipelineContextData +import com.tencent.devops.common.expression.expression.EvaluationOptions import com.tencent.devops.common.expression.expression.ExpressionConstants import com.tencent.devops.common.expression.expression.IExpressionNode import com.tencent.devops.common.expression.expression.IFunctionInfo @@ -74,8 +76,9 @@ object ExpressionParser { nameValue: List, fetchValue: Boolean ): Any? { + val options = EvaluationOptions(false) val result = createTree(expression.legalizeExpression(), null, nameValue, null)!! - .evaluate(null, context, null, null) + .evaluate(null, context, options, null) if (!fetchValue) { return result } @@ -90,10 +93,32 @@ object ExpressionParser { val context = ExecutionContext(DictionaryContextData()) val nameValue = mutableListOf() fillContextByMap(contextMap, context, nameValue) + val options = EvaluationOptions(false) + try { + return doEvaluateByMap( + expression = expression, + context = context, + nameValue = nameValue, + options = options, + fetchValue = fetchValue + ) + } catch (e: Throwable) { + if (options.contextNotNull() && e is ContextNotFoundException) { + throw ContextNotFoundException("Expression context ${options.contextNotNull.errKey()} not found.") + } + throw e + } + } + private fun doEvaluateByMap( + expression: String, + context: ExecutionContext, + nameValue: MutableList, + options: EvaluationOptions, + fetchValue: Boolean + ): Any? { val result = createTree(expression.legalizeExpression(), null, nameValue, null)!! - .evaluate(null, context, null, null) - + .evaluate(null, context, options, null) if (!fetchValue) { return result } @@ -104,8 +129,9 @@ object ExpressionParser { /** * 将流水线变量转换为表达式上下文类型,存在如下情况 * 1、a = str, 直接使用 string 类型的上下文保存即可 - * 2、a.b.c = str, 将 a.b.c 升格为上下文中的嵌套 map 保存 既 a {b: {c: str}} - * 3、a.b.c = str 且 a.b = {"c": "str"}, 需要校验 a.b 所保存的 json 与 a.b.c 结构和数据是否相同后再升格 + * 2、a.b.c = str, 将 a.b.c 转换为上下文中的嵌套 map 保存 既 a {b: {c: str}} + * 3、a.b.c = str 且 a.b = {"c": "str"}, 将 a.b 保存为兼容用户的数据类型,在不涉及引擎计算纯输出的情况下使用 a.b 的原数据, + * 在涉及引擎计算时,则使用 a.b.c 转换后的 map,同 2 中所述 */ fun fillContextByMap( contextMap: Map, diff --git a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/AbsDictionaryContextData.kt b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/AbsDictionaryContextData.kt index 0de7a0d8bf7..c88b3fd03ae 100644 --- a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/AbsDictionaryContextData.kt +++ b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/AbsDictionaryContextData.kt @@ -1,6 +1,7 @@ package com.tencent.devops.common.expression.context import com.fasterxml.jackson.databind.JsonNode +import com.tencent.devops.common.expression.expression.sdk.CollectionPipelineResult import com.tencent.devops.common.expression.expression.sdk.IReadOnlyObject import com.tencent.devops.common.expression.utils.ExpressionJsonUtil import java.util.TreeMap @@ -8,11 +9,7 @@ import java.util.TreeMap /** * dict 的抽象类,总结公共方法 */ -abstract class AbsDictionaryContextData : - PipelineContextData(PipelineContextDataType.DICTIONARY), - Iterable>, - IReadOnlyObject { - +abstract class AbsDictionaryContextData : PipelineContextData(PipelineContextDataType.DICTIONARY), IReadOnlyObject { protected open var mIndexLookup: TreeMap? = null protected open var mList: MutableList = mutableListOf() @@ -24,14 +21,6 @@ abstract class AbsDictionaryContextData : return emptyList() } - override fun tryGetValue(key: String): Pair { - if (mList.isNotEmpty() && indexLookup.containsKey(key)) { - return Pair(mList[indexLookup[key]!!].value, true) - } - - return Pair(null, false) - } - protected val indexLookup: MutableMap get() { if (mIndexLookup == null) { @@ -51,6 +40,29 @@ abstract class AbsDictionaryContextData : return mList } + override operator fun get(key: String): PipelineContextData? { + val index = indexLookup[key] ?: return null + return list[index].value + } + + override fun getRes(key: String): CollectionPipelineResult { + return if (containsKey(key)) { + CollectionPipelineResult(list.getOrNull(indexLookup[key]!!)?.value) + } else { + CollectionPipelineResult.noKey() + } + } + + override fun toJson(): JsonNode { + val json = ExpressionJsonUtil.createObjectNode() + if (mList.isNotEmpty()) { + mList.forEach { + json.set(it.key, it.value?.toJson()) + } + } + return json + } + operator fun set(k: String, value: PipelineContextData?) { // Existing val index = indexLookup[k] @@ -64,22 +76,6 @@ abstract class AbsDictionaryContextData : } } - open operator fun get(k: String): PipelineContextData? { - // Existing - val index = indexLookup[k] ?: return null - return list[index].value - } - - open operator fun IReadOnlyObject.get(key: String): Any? { - val index = indexLookup[key] ?: return null - return list[index].value - } - - operator fun Pair.get(key: Int): Pair { - val pair = mList[key] - return Pair(pair.key, pair.value) - } - fun add(pairs: Iterable>) { pairs.forEach { pair -> add(pair.first, pair.second) @@ -94,18 +90,8 @@ abstract class AbsDictionaryContextData : list.add(DictionaryContextDataPair(key, value)) } - override fun iterator(): Iterator> { - return mList.map { pair -> Pair(pair.key, pair.value); }.iterator() - } - - override fun toJson(): JsonNode { - val json = ExpressionJsonUtil.createObjectNode() - if (mList.isNotEmpty()) { - mList.forEach { - json.set(it.key, it.value?.toJson()) - } - } - return json + fun containsKey(key: String): Boolean { + return mList.isNotEmpty() && indexLookup.containsKey(key) } protected data class DictionaryContextDataPair( diff --git a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/ArrayContextData.kt b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/ArrayContextData.kt index edd43646ef0..7dbb0e41b73 100644 --- a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/ArrayContextData.kt +++ b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/ArrayContextData.kt @@ -28,6 +28,7 @@ package com.tencent.devops.common.expression.context import com.fasterxml.jackson.databind.JsonNode +import com.tencent.devops.common.expression.expression.sdk.CollectionPipelineResult import com.tencent.devops.common.expression.expression.sdk.IReadOnlyArray import com.tencent.devops.common.expression.utils.ExpressionJsonUtil @@ -40,11 +41,14 @@ class ArrayContextData : PipelineContextData(PipelineContextDataType.ARRAY), IRe override val count: Int get() = mItems.count() - fun add(item: PipelineContextData?) { - mItems.add(item) - } + override operator fun get(index: Int): PipelineContextData? = mItems[index] - override fun get(index: Int): Any? = mItems[index] + override fun getRes(index: Int): CollectionPipelineResult { + if (index >= 0 && index <= mItems.lastIndex) { + return CollectionPipelineResult(mItems[index]) + } + return CollectionPipelineResult.noKey() + } override fun clone(): PipelineContextData { val result = ArrayContextData() @@ -71,9 +75,17 @@ class ArrayContextData : PipelineContextData(PipelineContextDataType.ARRAY), IRe val list = mutableListOf() if (mItems.isNotEmpty()) { mItems.forEach { + if (it is DictionaryContextDataWithVal) { + list.add(it.fetchValueNative()) + return@forEach + } list.add(it?.fetchValue() ?: return@forEach) } } return list } + + fun add(item: PipelineContextData?) { + mItems.add(item) + } } diff --git a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/ContextValueNode.kt b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/ContextValueNode.kt index 0843ef0d73e..8fe80fde0c8 100644 --- a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/ContextValueNode.kt +++ b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/ContextValueNode.kt @@ -27,6 +27,7 @@ package com.tencent.devops.common.expression.context +import com.tencent.devops.common.expression.ContextNotFoundException import com.tencent.devops.common.expression.ExecutionContext import com.tencent.devops.common.expression.expression.sdk.EvaluationContext import com.tencent.devops.common.expression.expression.sdk.NamedValue @@ -34,7 +35,12 @@ import com.tencent.devops.common.expression.expression.sdk.ResultMemory class ContextValueNode : NamedValue() { override fun evaluateCore(context: EvaluationContext): Pair { - return Pair(null, (context.state as ExecutionContext).expressionValues[name]) + val value = (context.state as ExecutionContext).expressionValues.getRes(name) + if (context.options.contextNotNull() && value.noKey()) { + context.options.contextNotNull.trace(name) + throw ContextNotFoundException() + } + return Pair(null, value.value) } override fun createNode(): NamedValue { @@ -43,7 +49,7 @@ class ContextValueNode : NamedValue() { override fun subNameValueEvaluateCore(context: EvaluationContext): Pair { val values = (context.state as ExecutionContext).expressionValues - return if (values[name] != null) { + return if (values.containsKey(name)) { Pair(values[name], true) } else { Pair(name, false) diff --git a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/DictionaryContextData.kt b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/DictionaryContextData.kt index ff13dfc165c..61716b4007f 100644 --- a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/DictionaryContextData.kt +++ b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/DictionaryContextData.kt @@ -46,10 +46,14 @@ open class DictionaryContextData : AbsDictionaryContextData() { return result } - override fun fetchValue(): Map { + override fun fetchValue(): Any { val map = mutableMapOf() if (mList.isNotEmpty()) { mList.forEach { + if (it.value is DictionaryContextDataWithVal) { + map[it.key] = it.value.fetchValueNative() + return@forEach + } map[it.key] = it.value?.fetchValue() ?: "" } } diff --git a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/DictionaryContextDataWithVal.kt b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/DictionaryContextDataWithVal.kt index 15bc0e3ba58..b47c08e9645 100644 --- a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/DictionaryContextDataWithVal.kt +++ b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/DictionaryContextDataWithVal.kt @@ -9,7 +9,7 @@ import java.util.TreeMap */ class DictionaryContextDataWithVal( private val oriValue: String -) : AbsDictionaryContextData() { +) : DictionaryContextData() { override var mIndexLookup: TreeMap? = null override var mList: MutableList = mutableListOf() @@ -26,6 +26,9 @@ class DictionaryContextDataWithVal( return result } + /** + * 兼容用户数据,输出的fetchValue + */ override fun fetchValue(): Any { return try { ExpressionJsonUtil.getObjectMapper().readTree(oriValue) @@ -33,4 +36,9 @@ class DictionaryContextDataWithVal( return oriValue } } + + /** + * 引擎中计算使用的fetchValue + */ + fun fetchValueNative() = super.fetchValue() } \ No newline at end of file diff --git a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/RuntimeDictionaryContextData.kt b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/RuntimeDictionaryContextData.kt index f8c6793dae5..3cabb836d5e 100644 --- a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/RuntimeDictionaryContextData.kt +++ b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/context/RuntimeDictionaryContextData.kt @@ -28,7 +28,7 @@ package com.tencent.devops.common.expression.context import com.tencent.devops.common.expression.ContextDataRuntimeException -import com.tencent.devops.common.expression.expression.sdk.IReadOnlyObject +import com.tencent.devops.common.expression.expression.sdk.CollectionPipelineResult import java.lang.Exception import java.util.TreeMap @@ -43,40 +43,24 @@ interface RuntimeNamedValue { * @throws ContextDataRuntimeException */ @Suppress("TooManyFunctions", "ReturnCount") -class RuntimeDictionaryContextData(private val runtimeNamedValue: RuntimeNamedValue) : - DictionaryContextData() { - +class RuntimeDictionaryContextData(private val runtimeNamedValue: RuntimeNamedValue) : DictionaryContextData() { override var mIndexLookup: TreeMap? = null override var mList: MutableList = mutableListOf() - private fun requestAndSaveValue(key: String): PipelineContextData? { - return try { - val value = runtimeNamedValue.getValue(key) - if (value != null) { - set(key, value) - } - value - } catch (ignore: Exception) { - throw ContextDataRuntimeException("RuntimeDictionaryContextData request key:$key 's value error") - } + override operator fun get(key: String): PipelineContextData? { + return getRes(key).value } - override fun tryGetValue(key: String): Pair { - if (mList.isNotEmpty() && indexLookup.containsKey(key)) { - return Pair(mList[indexLookup[key]!!].value, true) + override fun getRes(key: String): CollectionPipelineResult { + if (containsKey(key)) { + return CollectionPipelineResult(mList.getOrNull(indexLookup[key]!!)?.value) } - // 对象中没有则去请求一次 - val value = requestAndSaveValue(key) ?: return Pair(null, false) - - return Pair(value, true) - } - override operator fun get(k: String): PipelineContextData? { - return tryGetValue(k).first - } + // 对象中没有则去请求一次 + // 暂时全部按照无KEY处理,上线后看看情况 + val value = requestAndSaveValue(key) ?: return CollectionPipelineResult.noKey() - override operator fun IReadOnlyObject.get(key: String): Any? { - return tryGetValue(key).first + return CollectionPipelineResult(value) } override fun clone(): PipelineContextData { @@ -91,4 +75,16 @@ class RuntimeDictionaryContextData(private val runtimeNamedValue: RuntimeNamedVa return result } + + private fun requestAndSaveValue(key: String): PipelineContextData? { + return try { + val value = runtimeNamedValue.getValue(key) + if (value != null) { + set(key, value) + } + value + } catch (ignore: Exception) { + throw ContextDataRuntimeException("RuntimeDictionaryContextData request key:$key 's value error") + } + } } diff --git a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/EvaluationOptions.kt b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/EvaluationOptions.kt index 77591db5e4d..00d787b7adc 100644 --- a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/EvaluationOptions.kt +++ b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/EvaluationOptions.kt @@ -27,13 +27,47 @@ package com.tencent.devops.common.expression.expression -class EvaluationOptions() { +/** + * @param contextNotNull 上下文计算时需要不存在的变量抛出异常而不是返回空 + * @param maxMemory 暂未使用 + */ +data class EvaluationOptions( + val contextNotNull: ExceptionInsteadOfNullOption, + var maxMemory: Int = 0 +) { + fun contextNotNull(): Boolean { + return contextNotNull.enable + } - constructor(copy: EvaluationOptions?) : this() { - if (copy != null) { - maxMemory = copy.maxMemory + constructor(contextNotNull: Boolean) : this( + if (contextNotNull) { + ExceptionInsteadOfNullOption.enable() + } else { + ExceptionInsteadOfNullOption.disabled() } + ) +} + +/** + * @param enable 是否开启 + * @param exceptionTraceMsg 存放为空的变量的索引链路,方便排查 + */ +data class ExceptionInsteadOfNullOption( + val enable: Boolean, + val exceptionTraceMsg: MutableList? +) { + fun trace(name: String) { + if (!enable) { + return + } + // 字符串的格式化会带 '' + exceptionTraceMsg?.add(name.removeSurrounding("'")) } - var maxMemory: Int = 0 -} + fun errKey() = exceptionTraceMsg?.joinToString(".") + + companion object { + fun disabled(): ExceptionInsteadOfNullOption = ExceptionInsteadOfNullOption(false, null) + fun enable(): ExceptionInsteadOfNullOption = ExceptionInsteadOfNullOption(true, mutableListOf()) + } +} \ No newline at end of file diff --git a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/IExpressionNode.kt b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/IExpressionNode.kt index f7e118642f4..d1d82c5bdb4 100644 --- a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/IExpressionNode.kt +++ b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/IExpressionNode.kt @@ -34,7 +34,7 @@ interface IExpressionNode { fun evaluate( trace: ITraceWriter?, state: Any?, - options: EvaluationOptions?, + options: EvaluationOptions, expressionOutput: ExpressionOutput? ): EvaluationResult @@ -53,7 +53,7 @@ interface IExpressionNode { fun subNameValueEvaluate( trace: ITraceWriter?, state: Any?, - options: EvaluationOptions?, + options: EvaluationOptions, subInfo: SubNameValueEvaluateInfo, expressionOutput: ExpressionOutput? ): SubNameValueEvaluateResult diff --git a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/CollectionResult.kt b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/CollectionResult.kt new file mode 100644 index 00000000000..8863609ba49 --- /dev/null +++ b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/CollectionResult.kt @@ -0,0 +1,63 @@ +package com.tencent.devops.common.expression.expression.sdk + +import com.tencent.devops.common.expression.ContextNotFoundException +import com.tencent.devops.common.expression.context.PipelineContextData + +/** + * 从集合类型上下文取出值的封装对象 + * 如 IReadOnlyArray,IReadOnlyObject + */ +open class CollectionResult( + open val type: CollectionResultType, + open val value: Any? +) { + constructor(value: Any?) : this( + type = if (value == null) { + CollectionResultType.NO_VALUE + } else { + CollectionResultType.VALUE + }, + value = value + ) + + fun noKey() = type == CollectionResultType.NO_KEY + + fun throwIfNoKey() { + if (noKey()) { + throw ContextNotFoundException() + } + } + + companion object { + fun noKey() = CollectionPipelineResult(CollectionResultType.NO_KEY, null) + } +} + +enum class CollectionResultType { + // 集合中不存在取值的KEY + NO_KEY, + + // 集合中存在KEY但是取值为空 + NO_VALUE, + + // 存在KEY且取值不为空 + VALUE +} + +data class CollectionPipelineResult( + override val type: CollectionResultType, + override val value: PipelineContextData? +) : CollectionResult(type, value) { + constructor(value: PipelineContextData?) : this( + type = if (value == null) { + CollectionResultType.NO_VALUE + } else { + CollectionResultType.VALUE + }, + value = value + ) + + companion object { + fun noKey() = CollectionPipelineResult(CollectionResultType.NO_KEY, null) + } +} \ No newline at end of file diff --git a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/EvaluationContext.kt b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/EvaluationContext.kt index 5921c4f3887..a7df27314e4 100644 --- a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/EvaluationContext.kt +++ b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/EvaluationContext.kt @@ -35,11 +35,10 @@ import com.tencent.devops.common.expression.expression.ITraceWriter class EvaluationContext( val trace: ITraceWriter?, val state: Any?, - ops: EvaluationOptions?, + val options: EvaluationOptions, val node: ExpressionNode?, val expressionOutput: ExpressionOutput? ) { - val options: EvaluationOptions = EvaluationOptions(ops) val memory: EvaluationMemory private val mTraceResults = mutableMapOf() private val mTraceMemory: MemoryCounter diff --git a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/ExpressionNode.kt b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/ExpressionNode.kt index eaa54dbcc77..663ed5287b3 100644 --- a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/ExpressionNode.kt +++ b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/ExpressionNode.kt @@ -69,10 +69,14 @@ abstract class ExpressionNode : IExpressionNode { protected abstract val traceFullyRealized: Boolean + // 用来展示打印这个节点 + protected open val formatValue: String? = null + open fun format() = formatValue ?: name + override fun evaluate( trace: ITraceWriter?, state: Any?, - options: EvaluationOptions?, + options: EvaluationOptions, expressionOutput: ExpressionOutput? ): EvaluationResult { if (container != null) { @@ -140,7 +144,7 @@ abstract class ExpressionNode : IExpressionNode { override fun subNameValueEvaluate( trace: ITraceWriter?, state: Any?, - options: EvaluationOptions?, + options: EvaluationOptions, subInfo: SubNameValueEvaluateInfo, expressionOutput: ExpressionOutput? ): SubNameValueEvaluateResult { diff --git a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/IReadOnlyArray.kt b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/IReadOnlyArray.kt index 4ec03b5960a..3e563188d7f 100644 --- a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/IReadOnlyArray.kt +++ b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/IReadOnlyArray.kt @@ -30,5 +30,14 @@ package com.tencent.devops.common.expression.expression.sdk interface IReadOnlyArray : Iterable { val count: Int + /** + * 使用kotlin原生List的get方法 + * 使用时需要注意场景,小心IndexOutOfBoundsException + */ operator fun get(index: Int): Any? + + /** + * 封装get,令返回结果更清晰 + */ + fun getRes(index: Int): CollectionResult } diff --git a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/IReadOnlyObject.kt b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/IReadOnlyObject.kt index 625a61d1eb0..0ae24065792 100644 --- a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/IReadOnlyObject.kt +++ b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/IReadOnlyObject.kt @@ -30,5 +30,14 @@ package com.tencent.devops.common.expression.expression.sdk interface IReadOnlyObject { val values: Iterable - fun tryGetValue(key: String): Pair + /** + * 使用kotlin原生Map的get方法 + * 使用时需要注意场景 + */ + operator fun get(key: String): Any? + + /** + * 封装get,令返回结果更清晰 + */ + fun getRes(key: String): CollectionResult } diff --git a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/Literal.kt b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/Literal.kt index 910250c3e2d..561b46d757f 100644 --- a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/Literal.kt +++ b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/Literal.kt @@ -44,6 +44,8 @@ class Literal(v: Any?) : ExpressionNode() { // 这样可以避免不必要地复制内存中的值。 override val traceFullyRealized = false + override fun format() = ExpressionUtility.formatValue(value, kind) + override fun convertToExpression() = ExpressionUtility.formatValue(value, kind) override fun convertToRealizedExpression(context: EvaluationContext) = ExpressionUtility.formatValue(value, kind) diff --git a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/operators/Index.kt b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/operators/Index.kt index 3f1e1101c9b..d7b5094d244 100644 --- a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/operators/Index.kt +++ b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/sdk/operators/Index.kt @@ -27,9 +27,12 @@ package com.tencent.devops.common.expression.expression.sdk.operators +import com.tencent.devops.common.expression.ContextNotFoundException import com.tencent.devops.common.expression.ExecutionContext import com.tencent.devops.common.expression.context.ContextValueNode import com.tencent.devops.common.expression.expression.EvaluationResult +import com.tencent.devops.common.expression.expression.ExpressionConstants +import com.tencent.devops.common.expression.expression.sdk.CollectionResult import com.tencent.devops.common.expression.expression.sdk.Container import com.tencent.devops.common.expression.expression.sdk.EvaluationContext import com.tencent.devops.common.expression.expression.sdk.ExpressionNode @@ -47,9 +50,10 @@ class Index : Container() { override val traceFullyRealized = true + override val formatValue: String = ExpressionConstants.DEREFERENCE.toString() + override fun convertToExpression(): String { - // 验证我们是否可以简化表达式,我们宁愿返回 - // github.sha 然后 github['sha'] 所以我们检查这是否是一个简单的案例。 + // 验证我们是否可以简化表达式,我们宁愿返回 github.sha 然后 github['sha'] 所以我们检查这是否是一个简单的案例 return if (parameters[1] is Literal && (parameters[1] as Literal).value is String && ExpressionUtility.isLegalKeyword((parameters[1] as Literal).value as String) @@ -68,12 +72,15 @@ class Index : Container() { } return parameters[0].convertToRealizedExpression(context) + - "[${parameters[1].convertToRealizedExpression(context)}]" + "[${parameters[1].convertToRealizedExpression(context)}]" } override fun evaluateCore(context: EvaluationContext): Pair { val left = parameters[0].evaluate(context) - + // 如果有多个索引如 a.b.c 因为是递归计算,到 c的时候拿到的左参数一定是 a.b 中的 .,所以追踪左参数只追踪第一个 + if (parameters[0] !is Index) { + context.options.contextNotNull.trace(parameters[0].format()) + } // Not a collection val (collection, ok) = left.tryGetCollectionInterface() if (!ok) { @@ -136,6 +143,7 @@ class Index : Container() { return Pair(result, true) } + @Suppress("ComplexMethod") private fun handleFilteredArray( context: EvaluationContext, filteredArray: FilteredArray @@ -161,9 +169,13 @@ class Index : Container() { } // String else if (index.hasStringIndex) { - val (nestedObjectValue, res) = nestedCollection.tryGetValue(index.stringIndex!!) - if (res) { - result.add(nestedObjectValue) + val key = index.stringIndex!! + val nestedObjectValueRes = nestedCollection.getRes(key) + if (context.options.contextNotNull()) { + nestedObjectValueRes.throwIfNoKey() + } + if (!nestedObjectValueRes.noKey()) { + result.add(nestedObjectValueRes.value) counter.add(Int.SIZE_BYTES) } } @@ -209,9 +221,16 @@ class Index : Container() { } // String else { - val (result, ok) = obj.tryGetValue(index.stringIndex ?: return Pair(null, null)) - if (index.hasStringIndex && ok) { - return Pair(null, result) + val key = index.stringIndex + if (key == null && context.options.contextNotNull()) { + throw ContextNotFoundException() + } + val result = obj.getRes(key ?: return Pair(null, null)) + if (context.options.contextNotNull()) { + result.throwIfNoKey() + } + if (index.hasStringIndex && !result.noKey()) { + return Pair(null, result.value) } } @@ -241,6 +260,9 @@ class Index : Container() { else if (index.hasIntegerIndex && index.integerIndex < array.count) { return Pair(null, array[index.integerIndex]) } + if (context.options.contextNotNull()) { + throw ContextNotFoundException() + } return Pair(null, null) } @@ -260,19 +282,29 @@ class Index : Container() { return mList[index] } + override fun getRes(index: Int): CollectionResult { + if (index >= 0 && index <= mList.lastIndex) { + return CollectionResult(mList[index]) + } + return CollectionResult.noKey() + } + override fun iterator(): Iterator { return mList.iterator() } } private class IndexHelper(context: EvaluationContext, val parameter: ExpressionNode) { - private val mResult: EvaluationResult + val mResult: EvaluationResult private val mIntegerIndex: Lazy private val mStringIndex: Lazy init { mResult = parameter.evaluate(context) + // 追踪右参数 + context.options.contextNotNull.trace(parameter.format()) + mIntegerIndex = lazy { var doubleIndex = mResult.convertToNumber() if (doubleIndex.isNaN() || doubleIndex < 0.0) { diff --git a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/specialFuctions/hashFiles/HashFilesFunction.kt b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/specialFuctions/hashFiles/HashFilesFunction.kt index 5dd4ed7b08c..6cd16664970 100644 --- a/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/specialFuctions/hashFiles/HashFilesFunction.kt +++ b/src/backend/ci/core/common/common-expression/src/main/kotlin/com/tencent/devops/common/expression/expression/specialFuctions/hashFiles/HashFilesFunction.kt @@ -48,18 +48,12 @@ class HashFilesFunction : Function() { val contextValues = context.state as ExecutionContext var workspaceData = contextValues.expressionValues[ciWorkSpaceKey.split(".")[0]] workspaceData = if (workspaceData == null) { - if (contextValues.expressionValues[workSpaceKey] == null) { - throw ContextNotFoundException("$workSpaceKey/$ciWorkSpaceKey") - } else { - contextValues.expressionValues[workSpaceKey] - } + contextValues.expressionValues[workSpaceKey] + ?: throw ContextNotFoundException("$workSpaceKey/$ciWorkSpaceKey") } else { - if (workspaceData !is DictionaryContextData || workspaceData[ciWorkSpaceKey.split(".")[1]] == null) { - if (contextValues.expressionValues[workSpaceKey] == null) { - throw ContextNotFoundException("$workSpaceKey/$ciWorkSpaceKey") - } else { - contextValues.expressionValues[workSpaceKey] - } + if (workspaceData !is DictionaryContextData || !workspaceData.containsKey(ciWorkSpaceKey.split(".")[1])) { + contextValues.expressionValues[workSpaceKey] + ?: throw ContextNotFoundException("$workSpaceKey/$ciWorkSpaceKey") } else { workspaceData[ciWorkSpaceKey.split(".")[1]] } diff --git a/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/context/RuntimeDictionaryContextDataTest.kt b/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/context/RuntimeDictionaryContextDataTest.kt index 2d49d08d896..be44807b5c4 100644 --- a/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/context/RuntimeDictionaryContextDataTest.kt +++ b/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/context/RuntimeDictionaryContextDataTest.kt @@ -29,6 +29,7 @@ package com.tencent.devops.common.expression.context import com.tencent.devops.common.expression.ExecutionContext import com.tencent.devops.common.expression.ExpressionParser +import com.tencent.devops.common.expression.expression.EvaluationOptions import com.tencent.devops.common.expression.expression.sdk.NamedValueInfo import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeAll @@ -46,7 +47,7 @@ internal class RuntimeDictionaryContextDataTest { fun contextSingleBuildTest() { val context = RuntimeDictionaryContextData(RuntimeNamedValueImpl()) data.forEach { (k, v) -> - Assertions.assertEquals(v, context.tryGetValue(k).first) + Assertions.assertEquals(v, context[k]) } Assertions.assertEquals(data.map { it.value }, context.values) } @@ -59,12 +60,13 @@ internal class RuntimeDictionaryContextDataTest { "settings.access_token.access_token == '456' => true", "settings['appId'].secretKey == '101112' => true", "settings.token['username'] == " + - "fromJSON('{\"token\":\"212223\",\"username\":\"789\",\"password\":\"123\"}').username => true" + "fromJSON('{\"token\":\"212223\",\"username\":\"789\",\"password\":\"123\"}').username => true" ] ) fun contextExpressionParseTest(expAndExpect: String) { val (exp, expect) = expAndExpect.split(" => ") - val res = ExpressionParser.createTree(exp, null, nameValue, null)!!.evaluate(null, ev, null, null).value + val res = ExpressionParser.createTree(exp, null, nameValue, null)!! + .evaluate(null, ev, EvaluationOptions(false), null).value Assertions.assertEquals( if (expect == "true" || expect == "false") { expect.toBoolean() diff --git a/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/expression/EvaluationOptionsTest.kt b/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/expression/EvaluationOptionsTest.kt new file mode 100644 index 00000000000..069d07a4dbe --- /dev/null +++ b/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/expression/EvaluationOptionsTest.kt @@ -0,0 +1,102 @@ +package com.tencent.devops.common.expression.expression + +import com.tencent.devops.common.expression.ContextNotFoundException +import com.tencent.devops.common.expression.ExecutionContext +import com.tencent.devops.common.expression.ExpressionParser +import com.tencent.devops.common.expression.context.ArrayContextData +import com.tencent.devops.common.expression.context.ContextValueNode +import com.tencent.devops.common.expression.context.DictionaryContextData +import com.tencent.devops.common.expression.context.StringContextData +import com.tencent.devops.common.expression.expression.sdk.NamedValueInfo +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import org.slf4j.LoggerFactory + +@Suppress("ComplexMethod", "LongMethod", "MaxLineLength") +@DisplayName("测试EvaluationOptions配置的不同选项") +class EvaluationOptionsTest { + @DisplayName("exceptionInsteadOfNull相关场景测试") + @Nested + inner class ExceptionTest { + @DisplayName("配置了exceptionInsteadOfNull") + @ParameterizedTest + @ValueSource( + strings = [ + "string => string", + "obj.a.obj_obj1[2] == 1 => obj.a", + "arr[0].arr_obj_1_n2 => arr.0.arr_obj_1_n2", + "obj.obj_obj1.obj_obj1_n1.a => obj.obj_obj1.obj_obj1_n1.a" + ] + ) + fun exceptionInsteadOfNull(group: String) { + val (exp, exArg) = group.split(" => ") + val options = EvaluationOptions(true) + assertThrows { + ExpressionParser.createTree(exp, null, nameValue, null)!! + .evaluate(TestTraceWriter(), ev, options, null) + } + println(options.contextNotNull.exceptionTraceMsg) + Assertions.assertEquals( + ContextNotFoundException(exArg).message, + options.contextNotNull.exceptionTraceMsg?.joinToString(".") + ) + } + + @DisplayName("不配置exceptionInsteadOfNull") + @ParameterizedTest + @ValueSource( + strings = [ + "string == null", + "obj.a.obj_obj1[2] == null", + "arr[0].arr_obj_1_n2 == null", + "obj.obj_obj1.obj_obj1_n1.a == null" + ] + ) + fun noExceptionInsteadOfNull(exp: String) { + val options = EvaluationOptions(false) + val result = ExpressionParser.createTree(exp, null, nameValue, null)!! + .evaluate(TestTraceWriter(), ev, options, null) + Assertions.assertTrue(result.equalsTrue) + } + + private val nameValue = mutableListOf().apply { + add(NamedValueInfo("string", ContextValueNode())) + add(NamedValueInfo("obj", ContextValueNode())) + add(NamedValueInfo("arr", ContextValueNode())) + } + private val ev = ExecutionContext(DictionaryContextData().apply { + add("obj", DictionaryContextData().apply { + add("obj_arr1", ArrayContextData().apply { + add(null) + }) + add("obj_obj1", DictionaryContextData().apply { + add("obj_obj1_n1", DictionaryContextData().apply { + }) + }) + }) + add("arr", ArrayContextData().apply { + add(DictionaryContextData().apply { + add("arr_obj_1_n1", StringContextData("arr_obj_1_n1_v")) + }) + }) + }) + } +} + +class TestTraceWriter : ITraceWriter { + override fun info(message: String?) { + logger.info(message ?: return) + } + + override fun verbose(message: String?) { + logger.debug(message ?: return) + } + + companion object { + private val logger = LoggerFactory.getLogger(TestTraceWriter::class.java) + } +} \ No newline at end of file diff --git a/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/expression/ExpressionParserTest.kt b/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/expression/ExpressionParserTest.kt index 17ad9d05165..8eb866a45bb 100644 --- a/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/expression/ExpressionParserTest.kt +++ b/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/expression/ExpressionParserTest.kt @@ -117,11 +117,6 @@ class ExpressionParserTest { @DisplayName("测试流水线变量中对象的转换") @Test fun variablesObjectConvert() { -// val variablesWithError = mapOf( -// "matrix.power" to "{url=cn.com, project=p-xxx}", -// "matrix.power.url" to "cn.com", -// "matrix.power.project" to "p-xxx" -// ) val variables = mapOf( "matrix.power" to "{ \"url\" : \"cn.com\", \"project\": \"p-xxx\" }", "matrix.power.url" to "cn.com", @@ -249,9 +244,50 @@ class ExpressionParserTest { } Assertions.assertEquals(v, ExpressionParser.evaluateByMap(k, variables, true)) } -// assertThrows { -// ExpressionParser.evaluateByMap("matrix.power.url=='cn.com'", variablesWithError, true) -// } + } + + @DisplayName("测试流水线变量中对象的转换2") + @Test + fun variablesObjectConvert2() { + val variables = mapOf( + "matrix.power" to "{ \"url\" : \"cn.com\", \"project\": \"p-xxx\" }", + "matrix.power.url" to "c1.com", + "matrix.power.project" to "p-xxx", + "jsonStr" to "{ \"url\" : \"cn.com\", \"project\": \"p-1xx\" }" + ) + val jsonKeys = setOf( + "matrix.power" + ) + val mapKeys = setOf( + "matrix" + ) + val expAndExpect = mapOf( + "matrix.power" to "{ \"url\" : \"cn.com\", \"project\": \"p-xxx\" }", + "matrix.power.url" to "c1.com", + "matrix.power.project" to "p-xxx", + "matrix" to mapOf("power" to mapOf("url" to "c1.com", "project" to "p-xxx")), + "fromJSON(toJSON(matrix.power)).url" to "c1.com", + "fromJSON(jsonStr).project" to "p-1xx" + ) + expAndExpect.forEach { (exp, expect) -> + if (exp in jsonKeys) { + Assertions.assertEquals( + JsonUtil.getObjectMapper().readTree(expect.toString()), + JsonUtil.getObjectMapper().readTree( + ExpressionParser.evaluateByMap(exp, variables, true).toString() + ) + ) + return@forEach + } + if (exp in mapKeys) { + Assertions.assertEquals( + JsonUtil.getObjectMapper().readTree(JsonUtil.toJson(expect)), + JsonUtil.getObjectMapper() + .readTree(JsonUtil.toJson(ExpressionParser.evaluateByMap(exp, variables, true)!!)) + ) + } + Assertions.assertEquals(expect, ExpressionParser.evaluateByMap(exp, variables, true)) + } } @DisplayName("测试解析文字") @@ -267,7 +303,8 @@ class ExpressionParserTest { ) literals.forEach { (exp, v) -> - val res = ExpressionParser.createTree(exp, null, null, null)!!.evaluate(null, null, null, null).value + val res = ExpressionParser.createTree(exp, null, null, null)!! + .evaluate(null, null, EvaluationOptions(false), null).value Assertions.assertEquals(v, res) } } @@ -495,7 +532,8 @@ class ExpressionParserTest { ) fun functionFromJsonTest(fromJson: String) { val (index, exp) = fromJson.split(" => ") - val res = ExpressionParser.createTree(exp, null, nameValue, null)!!.evaluate(null, ev, null, null).value + val res = ExpressionParser.createTree(exp, null, nameValue, null)!! + .evaluate(null, ev, EvaluationOptions(false), null).value when (index.toInt()) { 1 -> { Assertions.assertTrue(res is DictionaryContextData) @@ -536,7 +574,8 @@ class ExpressionParserTest { ) fun functionJoinTest(join: String) { val (index, exp) = join.split(" => ") - val res = ExpressionParser.createTree(exp, null, nameValue, null)!!.evaluate(null, ev, null, null).value + val res = ExpressionParser.createTree(exp, null, nameValue, null)!! + .evaluate(null, ev, EvaluationOptions(false), null).value when (index.toInt()) { 1 -> { Assertions.assertEquals("push|mr|tag", res) @@ -583,7 +622,13 @@ class ExpressionParserTest { val result = items[1] val subInfo = SubNameValueEvaluateInfo() val tree = ExpressionParser.createSubNameValueEvaluateTree(exp, null, parametersNameValue, null, subInfo)!! - var (res, isComplete, type) = tree.subNameValueEvaluate(null, parametersEv, null, subInfo, null) + var (res, isComplete, type) = tree.subNameValueEvaluate( + null, + parametersEv, + EvaluationOptions(false), + subInfo, + null + ) if (isComplete && (type == SubNameValueResultType.ARRAY || type == SubNameValueResultType.DICT)) { res = res.replace("\\\"", "\"") } @@ -598,7 +643,8 @@ class ExpressionParserTest { private fun valuesTest(param: String) { val (exp, result) = param.split(" => ") - val res = ExpressionParser.createTree(exp, null, nameValue, null)!!.evaluate(null, ev, null, null).value + val res = ExpressionParser.createTree(exp, null, nameValue, null)!! + .evaluate(null, ev, EvaluationOptions(false), null).value Assertions.assertEquals( when (result) { "true", "false" -> { diff --git a/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/expression/functions/FormatTest.kt b/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/expression/functions/FormatTest.kt index 8413abe9ab6..76f7f77a59d 100644 --- a/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/expression/functions/FormatTest.kt +++ b/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/expression/functions/FormatTest.kt @@ -37,6 +37,7 @@ import com.tencent.devops.common.expression.context.ContextValueNode import com.tencent.devops.common.expression.context.DictionaryContextData import com.tencent.devops.common.expression.context.NumberContextData import com.tencent.devops.common.expression.context.StringContextData +import com.tencent.devops.common.expression.expression.EvaluationOptions import com.tencent.devops.common.expression.expression.sdk.NamedValueInfo import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeAll @@ -56,28 +57,28 @@ internal class FormatTest { ExpressionParser.createTree( "format('{0}-{1}:{3}', variables.str, variables.doub, variables.arry)", null, nameValue, null - )!!.evaluate(null, ev, null, null) + )!!.evaluate(null, ev, EvaluationOptions(false), null) } Assertions.assertThrows(FunctionFormatException::class.java) { ExpressionParser.createTree( "format('{0}-{1}:3}', variables.str, variables.doub, variables.arry)", null, nameValue, null - )!!.evaluate(null, ev, null, null) + )!!.evaluate(null, ev, EvaluationOptions(false), null) } Assertions.assertThrows(FunctionFormatException::class.java) { ExpressionParser.createTree( "format('{0}-{1}:{3', variables.str, variables.doub, variables.arry)", null, nameValue, null - )!!.evaluate(null, ev, null, null) + )!!.evaluate(null, ev, EvaluationOptions(false), null) } Assertions.assertThrows(FunctionFormatException::class.java) { ExpressionParser.createTree( "format('{0:yyyyMMdd}', variables.str, variables.doub, variables.arry)", null, nameValue, null - )!!.evaluate(null, ev, null, null) + )!!.evaluate(null, ev, EvaluationOptions(false), null) } } @@ -92,7 +93,7 @@ internal class FormatTest { ) fun evaluateCoreTest(format: String) { val (exp, expect) = format.split(" => ") - val res = ExpressionParser.createTree(exp, null, nameValue, null)!!.evaluate(null, ev, null, null).value + val res = ExpressionParser.createTree(exp, null, nameValue, null)!!.evaluate(null, ev, EvaluationOptions(false), null).value Assertions.assertEquals(expect, res) } @@ -109,7 +110,7 @@ internal class FormatTest { val res = ExpressionParser .createSubNameValueEvaluateTree(exp, null, parametersNameValue, null, SubNameValueEvaluateInfo())!! - .subNameValueEvaluate(null, parametersEv, null, SubNameValueEvaluateInfo(), null).value + .subNameValueEvaluate(null, parametersEv, EvaluationOptions(false), SubNameValueEvaluateInfo(), null).value Assertions.assertEquals(expect, res) } diff --git a/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/expression/functions/StrToTimeTest.kt b/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/expression/functions/StrToTimeTest.kt index c027658e949..31a1ecc0c8d 100644 --- a/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/expression/functions/StrToTimeTest.kt +++ b/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/expression/functions/StrToTimeTest.kt @@ -37,6 +37,7 @@ import com.tencent.devops.common.expression.context.ContextValueNode import com.tencent.devops.common.expression.context.DictionaryContextData import com.tencent.devops.common.expression.context.NumberContextData import com.tencent.devops.common.expression.context.StringContextData +import com.tencent.devops.common.expression.expression.EvaluationOptions import com.tencent.devops.common.expression.expression.sdk.NamedValueInfo import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeAll @@ -56,7 +57,7 @@ internal class StrToTimeTest { ExpressionParser.createTree( "strToTime('2023-3-15')", null, nameValue, null - )!!.evaluate(null, ev, null, null) + )!!.evaluate(null, ev, EvaluationOptions(false), null) } } @@ -69,8 +70,8 @@ internal class StrToTimeTest { ) fun evaluateCoreTest(format: String) { val (exp, expect) = format.split(" => ") - val res1 = ExpressionParser.createTree(exp, null, nameValue, null)!!.evaluate(null, ev, null, null).value - val res2 = ExpressionParser.createTree(expect, null, nameValue, null)!!.evaluate(null, ev, null, null).value + val res1 = ExpressionParser.createTree(exp, null, nameValue, null)!!.evaluate(null, ev, EvaluationOptions(false), null).value + val res2 = ExpressionParser.createTree(expect, null, nameValue, null)!!.evaluate(null, ev, EvaluationOptions(false), null).value Assertions.assertEquals(res1, res2) } @@ -84,7 +85,7 @@ internal class StrToTimeTest { ) fun eqTest(format: String) { val (exp, expect) = format.split(" => ") - val res = ExpressionParser.createTree(exp, null, nameValue, null)!!.evaluate(null, ev, null, null).value + val res = ExpressionParser.createTree(exp, null, nameValue, null)!!.evaluate(null, ev, EvaluationOptions(false), null).value Assertions.assertEquals(expect, res.toString()) } @@ -100,7 +101,7 @@ internal class StrToTimeTest { val res = ExpressionParser .createSubNameValueEvaluateTree(exp, null, parametersNameValue, null, SubNameValueEvaluateInfo())!! - .subNameValueEvaluate(null, parametersEv, null, SubNameValueEvaluateInfo(), null).value + .subNameValueEvaluate(null, parametersEv, EvaluationOptions(false), SubNameValueEvaluateInfo(), null).value Assertions.assertEquals(expect, res) } diff --git a/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/expression/specialFuctions/hashFiles/HashFilesFunctionTest.kt b/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/expression/specialFuctions/hashFiles/HashFilesFunctionTest.kt index f65cb065926..1fd06d75d5c 100644 --- a/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/expression/specialFuctions/hashFiles/HashFilesFunctionTest.kt +++ b/src/backend/ci/core/common/common-expression/src/test/kotlin/com/tencent/devops/common/expression/expression/specialFuctions/hashFiles/HashFilesFunctionTest.kt @@ -36,6 +36,7 @@ import com.tencent.devops.common.expression.context.ContextValueNode import com.tencent.devops.common.expression.context.DictionaryContextData import com.tencent.devops.common.expression.context.NumberContextData import com.tencent.devops.common.expression.context.StringContextData +import com.tencent.devops.common.expression.expression.EvaluationOptions import com.tencent.devops.common.expression.expression.FunctionInfo import com.tencent.devops.common.expression.expression.sdk.NamedValueInfo import org.junit.jupiter.api.Assertions @@ -62,11 +63,11 @@ internal class HashFilesFunctionTest { fun noRootPath() { val exp = "hashFiles('/data/a/a')" Assertions.assertThrows(RuntimeException::class.java) { - ExpressionParser.createTree(exp, null, nameValue, null)!!.evaluate(null, ev, null, null).value + ExpressionParser.createTree(exp, null, nameValue, null)!!.evaluate(null, ev, EvaluationOptions(false), null).value } val exp1 = "hashFiles('D: \\data\\.\\..')" Assertions.assertThrows(RuntimeException::class.java) { - ExpressionParser.createTree(exp1, null, nameValue, null)!!.evaluate(null, ev, null, null).value + ExpressionParser.createTree(exp1, null, nameValue, null)!!.evaluate(null, ev, EvaluationOptions(false), null).value } } @@ -75,7 +76,7 @@ internal class HashFilesFunctionTest { fun noIndexPath() { val exp = "hashFiles('/data/./..')" Assertions.assertThrows(RuntimeException::class.java) { - ExpressionParser.createTree(exp, null, nameValue, null)!!.evaluate(null, ev, null, null).value + ExpressionParser.createTree(exp, null, nameValue, null)!!.evaluate(null, ev, EvaluationOptions(false), null).value } } } @@ -103,7 +104,7 @@ internal class HashFilesFunctionTest { HashFilesFunction() ) ) - )!!.evaluate(null, ev, null, null).value + )!!.evaluate(null, ev, EvaluationOptions(false), null).value ) } @@ -129,7 +130,7 @@ internal class HashFilesFunctionTest { ) ), subInfo - )!!.subNameValueEvaluate(null, parametersEv, null, subInfo, null).value + )!!.subNameValueEvaluate(null, parametersEv, EvaluationOptions(false), subInfo, null).value ) } diff --git a/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/transfer/ModelTransfer.kt b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/transfer/ModelTransfer.kt index 093fe644a3b..6e366899724 100644 --- a/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/transfer/ModelTransfer.kt +++ b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/transfer/ModelTransfer.kt @@ -28,9 +28,11 @@ package com.tencent.devops.process.yaml.transfer import com.tencent.devops.common.api.constant.CommonMessageCode.YAML_NOT_VALID +import com.tencent.devops.common.api.pojo.PipelineAsCodeSettings import com.tencent.devops.common.client.Client import com.tencent.devops.common.pipeline.Model import com.tencent.devops.common.pipeline.container.Stage +import com.tencent.devops.common.pipeline.dialect.PipelineDialectType import com.tencent.devops.common.pipeline.pojo.setting.PipelineRunLockType import com.tencent.devops.common.pipeline.pojo.setting.PipelineSetting import com.tencent.devops.common.pipeline.pojo.setting.Subscription @@ -42,6 +44,7 @@ import com.tencent.devops.process.yaml.transfer.VariableDefault.nullIfDefault import com.tencent.devops.process.yaml.transfer.aspect.PipelineTransferAspectWrapper import com.tencent.devops.process.yaml.transfer.pojo.ModelTransferInput import com.tencent.devops.process.yaml.transfer.pojo.YamlTransferInput +import com.tencent.devops.process.yaml.v3.enums.SyntaxDialectType import com.tencent.devops.process.yaml.v3.models.Concurrency import com.tencent.devops.process.yaml.v3.models.Extends import com.tencent.devops.process.yaml.v3.models.GitNotices @@ -98,7 +101,7 @@ class ModelTransfer @Autowired constructor( maxQueueSize = yaml.concurrency?.queueLength ?: VariableDefault.DEFAULT_PIPELINE_SETTING_MAX_QUEUE_SIZE, maxConRunningQueueSize = yaml.concurrency?.maxParallel ?: PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX, labels = yaml2Labels(yamlInput), - pipelineAsCodeSettings = yamlInput.asCodeSettings, + pipelineAsCodeSettings = yamlSyntaxDialect2Setting(yaml.syntaxDialect), successSubscriptionList = yamlNotice2Setting( projectId = yamlInput.projectCode, notices = yaml.notices?.filter { it.checkNotifyForSuccess() } @@ -118,6 +121,22 @@ class ModelTransfer @Autowired constructor( } } + private fun yamlSyntaxDialect2Setting(syntaxDialectType: String?): PipelineAsCodeSettings? { + if (syntaxDialectType.isNullOrBlank()) return null + return when (syntaxDialectType) { + SyntaxDialectType.INHERIT.name -> PipelineAsCodeSettings(inheritedDialect = true) + SyntaxDialectType.CLASSIC.name -> PipelineAsCodeSettings( + inheritedDialect = false, + pipelineDialect = PipelineDialectType.CLASSIC.name + ) + SyntaxDialectType.CONSTRAINT.name -> PipelineAsCodeSettings( + inheritedDialect = false, + pipelineDialect = PipelineDialectType.CONSTRAINED.name + ) + else -> null + } + } + private fun prepareModelGroups(projectId: String, notice: Subscription): Subscription { if (notice.groups.isEmpty()) return notice val info = transferCache.getProjectGroupAndUsers(projectId)?.associateBy { it.displayName } ?: return notice @@ -207,7 +226,8 @@ class ModelTransfer @Autowired constructor( desc = modelInput.setting.desc.ifEmpty { null }, label = label, resources = modelInput.model.resources, - notices = makeNoticesV3(modelInput.setting) + notices = makeNoticesV3(modelInput.setting), + syntaxDialect = makeSyntaxDialect(modelInput.setting) ) else -> { throw PipelineTransferException( @@ -404,6 +424,16 @@ class ModelTransfer @Autowired constructor( } } + private fun makeSyntaxDialect(setting: PipelineSetting): String? { + val asCodeSettings = setting.pipelineAsCodeSettings ?: return null + return when { + asCodeSettings.inheritedDialect == true -> SyntaxDialectType.INHERIT.name + asCodeSettings.pipelineDialect == PipelineDialectType.CLASSIC.name -> SyntaxDialectType.CLASSIC.name + asCodeSettings.pipelineDialect == PipelineDialectType.CONSTRAINED.name -> SyntaxDialectType.CONSTRAINT.name + else -> null + } + } + @Suppress("NestedBlockDepth") private fun preparePipelineLabels( userId: String, diff --git a/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v2/parsers/template/ParametersExpressionParse.kt b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v2/parsers/template/ParametersExpressionParse.kt index bba8eade3cd..aaea3fdcabb 100644 --- a/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v2/parsers/template/ParametersExpressionParse.kt +++ b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v2/parsers/template/ParametersExpressionParse.kt @@ -15,6 +15,7 @@ import com.tencent.devops.common.expression.context.ExpressionContextData import com.tencent.devops.common.expression.context.NumberContextData import com.tencent.devops.common.expression.context.PipelineContextData import com.tencent.devops.common.expression.context.StringContextData +import com.tencent.devops.common.expression.expression.EvaluationOptions import com.tencent.devops.common.expression.expression.FunctionInfo import com.tencent.devops.common.expression.expression.sdk.NamedValueInfo import com.tencent.devops.common.expression.expression.specialFuctions.hashFiles.HashFilesFunction @@ -398,7 +399,7 @@ object ParametersExpressionParse { val (value, isComplete, type) = try { ExpressionParser.createSubNameValueEvaluateTree( expression, null, nameValues, functionList, subInfo - )?.subNameValueEvaluate(null, context, null, subInfo, null) + )?.subNameValueEvaluate(null, context, EvaluationOptions(false), subInfo, null) ?: throw YamlTemplateException("create evaluate tree is null") } catch (e: Throwable) { throw error(Constants.EXPRESSION_EVALUATE_ERROR.format(path, expression, e.message)) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionGradeService.kt b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/enums/SyntaxDialectType.kt similarity index 90% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionGradeService.kt rename to src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/enums/SyntaxDialectType.kt index 7196c76544e..47e63569be2 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/iam/PermissionGradeService.kt +++ b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/enums/SyntaxDialectType.kt @@ -23,11 +23,15 @@ * 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.devops.auth.service.iam +package com.tencent.devops.process.yaml.v3.enums -interface PermissionGradeService { - fun checkGradeManagerUser(userId: String, projectId: Int) +/** + * 流水线方言类型 + */ +enum class SyntaxDialectType { + INHERIT, + CLASSIC, + CONSTRAINT } diff --git a/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/PreScriptBuildYamlParser.kt b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/PreScriptBuildYamlParser.kt index fb547685247..403ad5a6647 100644 --- a/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/PreScriptBuildYamlParser.kt +++ b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/PreScriptBuildYamlParser.kt @@ -57,6 +57,7 @@ interface PreScriptBuildYamlIParser : YamlVersionParser { val disablePipeline: Boolean? val recommendedVersion: RecommendedVersion? val customBuildNum: String? + val syntaxDialect: String? } /** @@ -83,7 +84,8 @@ data class PreScriptBuildYamlParser( override val concurrency: Concurrency? = null, override val disablePipeline: Boolean? = null, override val recommendedVersion: RecommendedVersion? = null, - override val customBuildNum: String? = null + override val customBuildNum: String? = null, + override val syntaxDialect: String? ) : PreScriptBuildYamlIParser { override fun yamlVersion() = YamlVersion.V2_0 } diff --git a/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/PreScriptBuildYamlV3Parser.kt b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/PreScriptBuildYamlV3Parser.kt index 7c4933e59cb..c8777006258 100644 --- a/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/PreScriptBuildYamlV3Parser.kt +++ b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/PreScriptBuildYamlV3Parser.kt @@ -61,7 +61,8 @@ data class PreScriptBuildYamlV3Parser( override val concurrency: Concurrency? = null, override val disablePipeline: Boolean? = null, override val recommendedVersion: RecommendedVersion? = null, - override val customBuildNum: String? = null + override val customBuildNum: String? = null, + override val syntaxDialect: String? ) : PreScriptBuildYamlIParser { override fun yamlVersion() = YamlVersion.V3_0 } diff --git a/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/PreTemplateScriptBuildYamlParser.kt b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/PreTemplateScriptBuildYamlParser.kt index 35dc3c90cd0..87a06af8357 100644 --- a/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/PreTemplateScriptBuildYamlParser.kt +++ b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/PreTemplateScriptBuildYamlParser.kt @@ -63,6 +63,7 @@ interface IPreTemplateScriptBuildYamlParser : YamlVersionParser { var disablePipeline: Boolean? var recommendedVersion: RecommendedVersion? var customBuildNum: String? + var syntaxDialect: String? fun replaceTemplate(f: (param: ITemplateFilter) -> PreScriptBuildYamlIParser) @@ -132,7 +133,9 @@ data class PreTemplateScriptBuildYamlParser( @JsonProperty("recommended-version") override var recommendedVersion: RecommendedVersion? = null, @JsonProperty("custom-build-num") - override var customBuildNum: String? = null + override var customBuildNum: String? = null, + @JsonProperty("syntax-dialect") + override var syntaxDialect: String? ) : IPreTemplateScriptBuildYamlParser, ITemplateFilter { init { @@ -149,7 +152,8 @@ data class PreTemplateScriptBuildYamlParser( triggerOn = triggerOn, resources = resources, notices = notices, - concurrency = concurrency + concurrency = concurrency, + syntaxDialect = syntaxDialect ) } diff --git a/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/PreTemplateScriptBuildYamlV3Parser.kt b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/PreTemplateScriptBuildYamlV3Parser.kt index 3085cdc7ad5..4a94b46520f 100644 --- a/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/PreTemplateScriptBuildYamlV3Parser.kt +++ b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/models/PreTemplateScriptBuildYamlV3Parser.kt @@ -68,7 +68,9 @@ data class PreTemplateScriptBuildYamlV3Parser( @JsonProperty("recommended-version") override var recommendedVersion: RecommendedVersion? = null, @JsonProperty("custom-build-num") - override var customBuildNum: String? = null + override var customBuildNum: String? = null, + @JsonProperty("syntax-dialect") + override var syntaxDialect: String? = null ) : IPreTemplateScriptBuildYamlParser, ITemplateFilter { companion object { private val logger = LoggerFactory.getLogger(PreTemplateScriptBuildYamlV3Parser::class.java) @@ -89,7 +91,8 @@ data class PreTemplateScriptBuildYamlV3Parser( resources = resources, notices = notices, concurrency = concurrency, - disablePipeline = disablePipeline + disablePipeline = disablePipeline, + syntaxDialect = syntaxDialect ) } diff --git a/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/parsers/template/ParametersExpressionParse.kt b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/parsers/template/ParametersExpressionParse.kt index 22c64bee830..3aa2e9508a6 100644 --- a/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/parsers/template/ParametersExpressionParse.kt +++ b/src/backend/ci/core/common/common-pipeline-yaml/src/main/kotlin/com/tencent/devops/process/yaml/v3/parsers/template/ParametersExpressionParse.kt @@ -15,6 +15,7 @@ import com.tencent.devops.common.expression.context.ExpressionContextData import com.tencent.devops.common.expression.context.NumberContextData import com.tencent.devops.common.expression.context.PipelineContextData import com.tencent.devops.common.expression.context.StringContextData +import com.tencent.devops.common.expression.expression.EvaluationOptions import com.tencent.devops.common.expression.expression.FunctionInfo import com.tencent.devops.common.expression.expression.sdk.NamedValueInfo import com.tencent.devops.common.expression.expression.specialFuctions.hashFiles.HashFilesFunction @@ -399,7 +400,7 @@ object ParametersExpressionParse { val (value, isComplete, type) = try { ExpressionParser.createSubNameValueEvaluateTree( expression, null, nameValues, functionList, subInfo - )?.subNameValueEvaluate(null, context, null, subInfo, null) + )?.subNameValueEvaluate(null, context, EvaluationOptions(false), subInfo, null) ?: throw YamlTemplateException("create evaluate tree is null") } catch (e: Throwable) { throw error(Constants.EXPRESSION_EVALUATE_ERROR.format(path, expression, e.message)) diff --git a/src/backend/ci/core/common/common-pipeline-yaml/src/main/resources/schema/V3_0/ci.json b/src/backend/ci/core/common/common-pipeline-yaml/src/main/resources/schema/V3_0/ci.json index 4082bc8bb12..4edadcaff14 100644 --- a/src/backend/ci/core/common/common-pipeline-yaml/src/main/resources/schema/V3_0/ci.json +++ b/src/backend/ci/core/common/common-pipeline-yaml/src/main/resources/schema/V3_0/ci.json @@ -1980,6 +1980,9 @@ }, "gpus" : { "type" : "string" + }, + "privileged" : { + "type" : "string" } } }, @@ -2470,6 +2473,9 @@ }, "gpus" : { "type" : "string" + }, + "privileged" : { + "type" : "string" } } }, diff --git a/src/backend/ci/core/common/common-pipeline/src/main/java/com/tencent/devops/common/pipeline/ExprReplaceEnvVarUtil.java b/src/backend/ci/core/common/common-pipeline/src/main/java/com/tencent/devops/common/pipeline/ExprReplaceEnvVarUtil.java new file mode 100644 index 00000000000..8f68673917b --- /dev/null +++ b/src/backend/ci/core/common/common-pipeline/src/main/java/com/tencent/devops/common/pipeline/ExprReplaceEnvVarUtil.java @@ -0,0 +1,159 @@ +/* + * 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.devops.common.pipeline; + +import com.tencent.devops.common.api.util.JsonSchemaUtil; +import com.tencent.devops.common.api.util.JsonUtil; +import com.tencent.devops.common.api.util.ReflectUtil; +import com.tencent.devops.common.pipeline.utils.ExprReplacementUtil; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class ExprReplaceEnvVarUtil { + + public static Object replaceEnvVar(Object obj, Map envMap) { + return replaceEnvVar(obj, + new ExprReplacementOptions(envMap, false, null, null, null) + ); + } + + /** + * 把对象字段值中的表达式替换成环境变量 + * @param obj 需要把占位符替换环境变量的对象(对象如果是集合对象,注意要选择支持增加、删除等操作的集合类型,不要选择类似SingletonMap这种) + * @param envMap 环境变量Map + * @return 变量替换后的对象 + */ + @SuppressWarnings("all") + public static Object replaceEnvVar(Object obj, ExprReplacementOptions options) { + Map envMap = options.getContextMap(); + if (obj instanceof Map) { + // 递归替换map对象中的变量 + Set> entrySet = ((Map) obj).entrySet(); + for (Map.Entry entry : entrySet) { + Object value = entry.getValue(); + if (!isNormalReplaceEnvVar(value)) { + entry.setValue(replaceEnvVar(value, options)); + } else { + entry.setValue(handleNormalEnvVar(value, options)); + } + } + } else if (obj instanceof List) { + // 递归替换list对象中的变量 + List dataList = (List) obj; + for (int i = 0; i < dataList.size(); i++) { + Object value = dataList.get(i); + if (!isNormalReplaceEnvVar(value)) { + dataList.set(i, replaceEnvVar(value, options)); + } else { + dataList.set(i, handleNormalEnvVar(value, options)); + } + } + } else if (obj instanceof Set) { + // 递归替换set对象中的变量 + Set objSet = (Set) obj; + Set replaceObjSet = new HashSet(objSet); + Iterator it = replaceObjSet.iterator(); + while (it.hasNext()) { + Object value = it.next(); + objSet.remove(value); + if (!isNormalReplaceEnvVar(value)) { + objSet.add(replaceEnvVar(value, options)); + } else { + objSet.add(handleNormalEnvVar(value, options)); + } + } + } else if (isNormalReplaceEnvVar(obj)) { + // 替换基本类型对象或字符串对象中的变量 + obj = handleNormalEnvVar(obj, options); + } else { + try { + // 把对象转换成map后进行递归替换变量 + Map dataMap = JsonUtil.INSTANCE.toMap(obj); + replaceEnvVar(dataMap, options); + obj = JsonUtil.INSTANCE.to(JsonUtil.INSTANCE.toJson(dataMap, true), obj.getClass()); + } catch (Throwable e) { + // 转换不了map的对象则进行直接替换 + obj = ExprReplacementUtil.INSTANCE.parseExpression( + JsonUtil.INSTANCE.toJson(obj, true), options + ); + } + } + return obj; + } + + private static Object handleNormalEnvVar(Object obj, ExprReplacementOptions options) { + // 只有字符串参数才需要进行变量替换,其它基本类型参数无需进行变量替换 + if (obj instanceof String) { + String objStr = ((String) obj).trim(); + if (objStr.startsWith("{") && objStr.endsWith("}") && JsonSchemaUtil.INSTANCE.validateJson(objStr)) { + try { + Object dataObj = JsonUtil.INSTANCE.to((String) obj, Map.class); + // string能正常转换成map,则说明是json串,那么把dataObj进行递归替换变量后再转成json串 + dataObj = replaceEnvVar(dataObj, options); + obj = JsonUtil.INSTANCE.toJson(dataObj, true); + } catch (Throwable e) { + // 转换不了map的字符串对象则直接替换 + obj = ExprReplacementUtil.INSTANCE.parseExpression( + JsonUtil.INSTANCE.toJson(obj, true), options + ); + } + } else if (objStr.startsWith("[") && objStr.endsWith("]") && JsonSchemaUtil.INSTANCE.validateJson(objStr)) { + try { + Object dataObj = JsonUtil.INSTANCE.to((String) obj, List.class); + // string能正常转成list,说明是json串,把dataObj进行递归替换变量后再转成json串 + dataObj = replaceEnvVar(dataObj, options); + obj = JsonUtil.INSTANCE.toJson(dataObj, true); + } catch (Throwable e1) { + // 转换不了list的字符串对象则直接替换 + obj = ExprReplacementUtil.INSTANCE.parseExpression( + JsonUtil.INSTANCE.toJson(obj, true), options + ); + } + } else { + // 转换不了map或者list的字符串对象则直接替换 + obj = ExprReplacementUtil.INSTANCE.parseExpression( + JsonUtil.INSTANCE.toJson(obj, true), options + ); + } + } + return obj; + } + + /** + * 判断对象是否是普通替换对象 + * @param obj 需要把占位符替换环境变量的对象(对象如果是集合对象,注意要选择支持增加、删除等操作的集合类型,不要选择类似SingletonMap这种) + * @return 是否是普通替换对象 + */ + private static Boolean isNormalReplaceEnvVar(Object obj) { + return obj == null || ReflectUtil.INSTANCE.isNativeType(obj) || obj instanceof String; + } +} diff --git a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/EnvReplacementParser.kt b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/EnvReplacementParser.kt index 161f22831f2..3624042aa80 100644 --- a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/EnvReplacementParser.kt +++ b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/EnvReplacementParser.kt @@ -30,20 +30,14 @@ package com.tencent.devops.common.pipeline import com.tencent.devops.common.api.util.JsonUtil import com.tencent.devops.common.api.util.ObjectReplaceEnvVarUtil import com.tencent.devops.common.expression.ExecutionContext -import com.tencent.devops.common.expression.ExpressionParseException -import com.tencent.devops.common.expression.ExpressionParser -import com.tencent.devops.common.expression.context.ContextValueNode -import com.tencent.devops.common.expression.context.DictionaryContextData -import com.tencent.devops.common.expression.context.PipelineContextData -import com.tencent.devops.common.expression.context.RuntimeDictionaryContextData import com.tencent.devops.common.expression.context.RuntimeNamedValue import com.tencent.devops.common.expression.expression.ExpressionOutput import com.tencent.devops.common.expression.expression.IFunctionInfo import com.tencent.devops.common.expression.expression.sdk.NamedValueInfo -import org.apache.tools.ant.filters.StringInputStream +import com.tencent.devops.common.pipeline.dialect.IPipelineDialect +import com.tencent.devops.common.pipeline.utils.ExprReplacementUtil import org.slf4j.LoggerFactory -import java.io.BufferedReader -import java.io.InputStreamReader +import java.util.regex.Pattern @Suppress( "LoopWithTooManyJumpStatements", @@ -56,6 +50,7 @@ import java.io.InputStreamReader object EnvReplacementParser { private val logger = LoggerFactory.getLogger(EnvReplacementParser::class.java) + private val expressionPattern = Pattern.compile("\\$[{]{2}([^$^{}]+)[}]{2}") /** * 根据环境变量map进行object处理并保持原类型 @@ -67,259 +62,75 @@ object EnvReplacementParser { * @param output 表达式计算时输出 */ fun parse( - value: String?, + value: Any?, contextMap: Map, onlyExpression: Boolean? = false, contextPair: Pair>? = null, functions: Iterable? = null, output: ExpressionOutput? = null ): String { - if (value.isNullOrBlank()) return "" - return if (onlyExpression == true) { - try { - val (context, nameValues) = contextPair - ?: getCustomExecutionContextByMap(contextMap) - ?: return value - parseExpression( - value = value, - context = context, - nameValues = nameValues, - functions = functions, - output = output - ) - } catch (ignore: Throwable) { - logger.warn("[$value]|EnvReplacementParser expression invalid: ", ignore) - value - } - } else { - ObjectReplaceEnvVarUtil.replaceEnvVar(value, contextMap).let { - JsonUtil.toJson(it, false) - } - } - } - - fun getCustomExecutionContextByMap( - variables: Map, - extendNamedValueMap: List? = null - ): Pair>? { - try { - val context = ExecutionContext(DictionaryContextData()) - val nameValue = mutableListOf() - extendNamedValueMap?.forEach { namedValue -> - nameValue.add(NamedValueInfo(namedValue.key, ContextValueNode())) - context.expressionValues.add( - namedValue.key, - RuntimeDictionaryContextData(namedValue) - ) - } - ExpressionParser.fillContextByMap(variables, context, nameValue) - return Pair(context, nameValue) - } catch (ignore: Throwable) { - logger.warn("EnvReplacementParser context invalid: $variables", ignore) - return null - } - } - - private fun parseExpression( - value: String, - nameValues: List, - context: ExecutionContext, - functions: Iterable? = null, - output: ExpressionOutput? = null - ): String { - val strReader = InputStreamReader(StringInputStream(value)) - val bufferReader = BufferedReader(strReader) - val newValue = StringBuilder() - try { - var line = bufferReader.readLine() - while (line != null) { - // 跳过空行和注释行 - val blocks = findExpressions(line) - if (line.isBlank() || blocks.isEmpty()) { - newValue.append(line).append("\n") - line = bufferReader.readLine() - continue - } - val onceResult = parseExpressionLine( - value = line, - blocks = blocks, - context = context, - nameValues = nameValues, - functions = functions, - output = output - ) - - val newLine = findExpressions(onceResult).let { - if (it.isEmpty()) { - onceResult - } else { - parseExpressionLine( - value = onceResult, - blocks = it, - context = context, - nameValues = nameValues, - functions = functions, - output = output - ) - } - } - newValue.append(newLine).append("\n") - line = bufferReader.readLine() - } - } finally { - strReader.close() - bufferReader.close() - } - return newValue.toString().removeSuffix("\n") + val options = ExprReplacementOptions( + contextMap = contextMap, + contextPair = contextPair, + functions = functions, + output = output + ) + return parse(value = value, onlyExpression = onlyExpression, options = options) } /** - * 解析表达式,根据 findExpressions 寻找的括号优先级进行解析 + * 根据环境变量map进行object处理并保持原类型 + * 根据方言的配置判断是否能够使用${}或者变量值是否存在 */ - private fun parseExpressionLine( - value: String, - blocks: List>, - nameValues: List, - context: ExecutionContext, + fun parse( + value: Any?, + contextMap: Map, + dialect: IPipelineDialect, + contextPair: Pair>? = null, functions: Iterable? = null, output: ExpressionOutput? = null ): String { - var chars = value.toList() - blocks.forEachIndexed nextBlockLevel@{ blockLevel, blocksInLevel -> - blocksInLevel.forEachIndexed nextBlock@{ blockI, block -> - // 表达式因为含有 ${{ }} 所以起始向后推3位,末尾往前推两位 - val expression = chars.joinToString("").substring(block.startIndex + 3, block.endIndex - 1) - if (expression.isBlank()) return@nextBlock - var result = try { - ExpressionParser.createTree(expression, null, nameValues, functions)!! - .evaluate(null, context, null, output).value.let { - if (it is PipelineContextData) it.fetchValue() else it - }?.let { - JsonUtil.toJson(it, false) - } ?: "" - } catch (ignore: ExpressionParseException) { - return@nextBlock - } - - if ((blockLevel + 1 < blocks.size) && - !( - (block.startIndex - 1 >= 0 && chars[block.startIndex - 1] == '.') || - (block.endIndex + 1 < chars.size && chars[block.endIndex + 1] == '.') - ) - ) { - result = "'$result'" - } - - val charList = result.toList() - - // 将替换后的表达式嵌入原本的line - val startSub = if (block.startIndex - 1 < 0) { - listOf() - } else { - chars.slice(0 until block.startIndex) - } - val endSub = if (block.endIndex + 1 >= chars.size) { - listOf() - } else { - chars.slice(block.endIndex + 1 until chars.size) - } - chars = startSub + charList + endSub - - // 将替换后的字符查传递给后边的括号位数 - val diffNum = charList.size - (block.endIndex - block.startIndex + 1) - blocks.forEachIndexed { i, bl -> - bl.forEachIndexed level@{ j, b -> - if (i <= blockLevel && j <= blockI) { - return@level - } - if (blocks[i][j].startIndex > block.endIndex) { - blocks[i][j].startIndex += diffNum - } - if (blocks[i][j].endIndex > block.endIndex) { - blocks[i][j].endIndex += diffNum - } - } - } - } - } - - return chars.joinToString("") + val options = ExprReplacementOptions( + contextMap = contextMap, + contextNotNull = !dialect.supportMissingVar(), + contextPair = contextPair, + functions = functions, + output = output + ) + return parse( + value = value, + onlyExpression = dialect.supportUseExpression(), + options = options + ) } - /** - * 寻找语句中包含 ${{}}的表达式的位置,返回成对的位置坐标,并根据优先级排序 - * 优先级算法目前暂定为 从里到外,从左到右 - * @param levelMax 返回的最大层数,从深到浅。默认为2层 - * 例如: 替换顺序如数字所示 ${{ 4 ${{ 2 ${{ 1 }} }} ${{ 3 }} }} - * @return [ 层数次序 [ 括号 ] ] [[1], [2, 3], [4]]]] - */ - private fun findExpressions(condition: String, levelMax: Int = 2): List> { - val stack = ArrayDeque() - var index = 0 - val chars = condition.toCharArray() - val levelMap = mutableMapOf>() - while (index < chars.size) { - if (index + 2 < chars.size && chars[index] == '$' && chars[index + 1] == '{' && chars[index + 2] == '{' - ) { - stack.addLast(index) - index += 3 - continue - } - - if (index + 1 < chars.size && chars[index] == '}' && chars[index + 1] == '}' - ) { - val start = stack.removeLastOrNull() - if (start != null) { - // 栈里剩下几个前括号,这个完整括号的优先级就是多少 - val level = stack.size + 1 - if (levelMap.containsKey(level)) { - levelMap[level]!!.add(ExpressionBlock(start, index + 1)) - } else { - levelMap[level] = mutableListOf(ExpressionBlock(start, index + 1)) - } - } - index += 2 - continue - } - - index++ - } - - if (levelMap.isEmpty()) { - return listOf() + fun parse( + value: Any?, + onlyExpression: Boolean?, + options: ExprReplacementOptions + ): String { + if (value == null) return "" + return if (onlyExpression == true) { + ExprReplaceEnvVarUtil.replaceEnvVar(value, options) + } else { + ObjectReplaceEnvVarUtil.replaceEnvVar(value, options.contextMap) + }.let { + JsonUtil.toJson(it, false) } + } - val result = mutableListOf>() - var max = 0 - var listIndex = 0 - run end@{ - levelMap.keys.sortedDescending().forEach result@{ level -> - val blocks = levelMap[level] ?: return@result - blocks.sortBy { it.startIndex } - blocks.forEach { block -> - if (result.size < listIndex + 1) { - result.add(mutableListOf(block)) - } else { - result[listIndex].add(block) - } - } - listIndex++ - max++ - if (max == levelMax) { - return@end - } - } - } - return result + fun getCustomExecutionContextByMap( + variables: Map, + extendNamedValueMap: List? = null + ): Pair>? { + return ExprReplacementUtil.getCustomExecutionContextByMap( + variables = variables, + extendNamedValueMap = extendNamedValueMap + ) } - /** - * 表达式括号项 ${{ }} - * @param startIndex 括号开始位置即 $ 位置 - * @param endIndex 括号结束位置即最后一个 } 位置 - */ - data class ExpressionBlock( - var startIndex: Int, - var endIndex: Int - ) + fun containsExpressions(value: String?): Boolean { + if (value == null) return false + return expressionPattern.matcher(value).find() + } } diff --git a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceGroupStrategyResource.kt b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/ExprReplacementOptions.kt similarity index 61% rename from src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceGroupStrategyResource.kt rename to src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/ExprReplacementOptions.kt index 76dbfab87d4..b3f0abd8989 100644 --- a/src/backend/ci/core/auth/api-auth/src/main/kotlin/com/tencent/devops/auth/api/service/ServiceGroupStrategyResource.kt +++ b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/ExprReplacementOptions.kt @@ -25,24 +25,25 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.api.service +package com.tencent.devops.common.pipeline -import com.tencent.devops.auth.pojo.StrategyEntity -import io.swagger.v3.oas.annotations.tags.Tag -import io.swagger.v3.oas.annotations.Operation -import javax.ws.rs.Consumes -import javax.ws.rs.GET -import javax.ws.rs.Path -import javax.ws.rs.Produces -import javax.ws.rs.core.MediaType +import com.tencent.devops.common.expression.ExecutionContext +import com.tencent.devops.common.expression.expression.ExpressionOutput +import com.tencent.devops.common.expression.expression.IFunctionInfo +import com.tencent.devops.common.expression.expression.sdk.NamedValueInfo +import io.swagger.v3.oas.annotations.media.Schema -@Tag(name = "AUTH_GROUP_STRATEGY", description = "权限-用户-策略") -@Path("/service/auth/strategy") -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -interface ServiceGroupStrategyResource { - @GET - @Path("/getGroupStrategy") - @Operation(summary = "获取组策略") - fun getGroupStrategy(): List -} +/** + * 表达式替换上下文 + */ +@Schema(title = "表达式替换参数") +data class ExprReplacementOptions( + @get:Schema(title = "环境变量", required = true) + val contextMap: Map, + @get:Schema(title = "值是否能不存在", required = true) + val contextNotNull: Boolean = false, + @get:Schema(title = "表达式上下文", required = true) + val contextPair: Pair>? = null, + val functions: Iterable? = null, + val output: ExpressionOutput? = null +) diff --git a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/PipelineVersionWithModel.kt b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/PipelineVersionWithModel.kt index 88ce275873f..6717fea9cf5 100644 --- a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/PipelineVersionWithModel.kt +++ b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/PipelineVersionWithModel.kt @@ -51,5 +51,9 @@ data class PipelineVersionWithModel( @get:Schema(title = "是否支持YAML解析", required = true) val yamlSupported: Boolean, @get:Schema(title = "YAML解析异常信息") - val yamlInvalidMsg: String? + val yamlInvalidMsg: String?, + @get:Schema(title = "更新操作人", required = true) + val updater: String?, + @get:Schema(title = "版本修改时间", required = true) + val updateTime: Long? ) diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionUrlServiceImpl.kt b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/dialect/ClassicPipelineDialect.kt similarity index 73% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionUrlServiceImpl.kt rename to src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/dialect/ClassicPipelineDialect.kt index f1a00323d0f..8b090955d07 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionUrlServiceImpl.kt +++ b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/dialect/ClassicPipelineDialect.kt @@ -25,18 +25,21 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.provider.sample.service +package com.tencent.devops.common.pipeline.dialect -import com.tencent.devops.auth.pojo.PermissionUrlDTO -import com.tencent.devops.auth.service.iam.PermissionUrlService -import com.tencent.devops.common.api.pojo.Result +/** + * 传统模式流水线方言 + */ +class ClassicPipelineDialect : IPipelineDialect { + override fun getPipelineDialectType() = PipelineDialectType.CLASSIC.name + + override fun supportUseExpression() = false + + override fun supportUseSingleCurlyBracesVar() = true + + override fun supportLongVarValue() = true -class SamplePermissionUrlServiceImpl : PermissionUrlService { - override fun getPermissionUrl(permissionUrlDTO: List): Result { - return Result("") - } + override fun supportChineseVarName() = true - override fun getRolePermissionUrl(projectId: String, groupId: String?): String? { - return null - } + override fun supportMissingVar() = true } diff --git a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/dialect/ConstrainedPipelineDialect.kt b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/dialect/ConstrainedPipelineDialect.kt new file mode 100644 index 00000000000..a3efb42ae1c --- /dev/null +++ b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/dialect/ConstrainedPipelineDialect.kt @@ -0,0 +1,45 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.devops.common.pipeline.dialect + +/** + * 约束模式语法风格 + */ +class ConstrainedPipelineDialect : IPipelineDialect { + override fun getPipelineDialectType() = PipelineDialectType.CONSTRAINED.name + + override fun supportUseExpression() = true + + override fun supportUseSingleCurlyBracesVar() = false + + override fun supportLongVarValue() = false + + override fun supportChineseVarName() = false + + override fun supportMissingVar() = false +} diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceGroupStrategyResourceImpl.kt b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/dialect/IPipelineDialect.kt similarity index 70% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceGroupStrategyResourceImpl.kt rename to src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/dialect/IPipelineDialect.kt index 1f76b72900e..c04fbff1d0a 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/resources/service/ServiceGroupStrategyResourceImpl.kt +++ b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/dialect/IPipelineDialect.kt @@ -25,19 +25,37 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.resources.service +package com.tencent.devops.common.pipeline.dialect -import com.tencent.devops.auth.api.service.ServiceGroupStrategyResource -import com.tencent.devops.auth.pojo.StrategyEntity -import com.tencent.devops.auth.service.StrategyService -import com.tencent.devops.common.web.RestResource -import org.springframework.beans.factory.annotation.Autowired +/** + * 流水线语法风格 + */ +interface IPipelineDialect { + + fun getPipelineDialectType(): String + + /** + * 支持插件变量使用表达式 + */ + fun supportUseExpression(): Boolean + /** + * 是否支持${}变量引用 + * + */ + fun supportUseSingleCurlyBracesVar(): Boolean + + /** + * 是否支持长变量 + */ + fun supportLongVarValue(): Boolean + + /** + * 是否支持中文变量名 + */ + fun supportChineseVarName(): Boolean -@RestResource -class ServiceGroupStrategyResourceImpl @Autowired constructor( - private val strategyService: StrategyService -) : ServiceGroupStrategyResource { - override fun getGroupStrategy(): List { - return strategyService.listStrategy() - } + /** + * 是否支持变量不存在 + */ + fun supportMissingVar(): Boolean } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionGradeService.kt b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/dialect/PipelineDialectType.kt similarity index 83% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionGradeService.kt rename to src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/dialect/PipelineDialectType.kt index 4a69f0e4343..7ab9c207f71 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/sample/service/SamplePermissionGradeService.kt +++ b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/dialect/PipelineDialectType.kt @@ -25,12 +25,16 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.provider.sample.service +package com.tencent.devops.common.pipeline.dialect -import com.tencent.devops.auth.service.iam.PermissionGradeService +/** + * 流水线语法风格 + * + */ +enum class PipelineDialectType(val dialect: IPipelineDialect) { + // 传统模式 + CLASSIC(ClassicPipelineDialect()), -class SamplePermissionGradeService : PermissionGradeService { - override fun checkGradeManagerUser(userId: String, projectId: Int) { - TODO("Not yet implemented") - } + // 约束模式 + CONSTRAINED(ConstrainedPipelineDialect()); } diff --git a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/dialect/PipelineDialectUtil.kt b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/dialect/PipelineDialectUtil.kt new file mode 100644 index 00000000000..07b3c51f667 --- /dev/null +++ b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/dialect/PipelineDialectUtil.kt @@ -0,0 +1,115 @@ +/* + * 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.devops.common.pipeline.dialect + +import com.tencent.devops.common.api.pojo.PipelineAsCodeSettings +import com.tencent.devops.common.pipeline.dialect.PipelineDialectType.CLASSIC +import com.tencent.devops.common.pipeline.dialect.PipelineDialectType.CONSTRAINED +import com.tencent.devops.common.pipeline.enums.ChannelCode + +object PipelineDialectUtil { + fun getPipelineDialect(pipelineDialectType: String?): IPipelineDialect { + return pipelineDialectType?.let { + PipelineDialectType.valueOf(it).dialect + } ?: CLASSIC.dialect + } + + fun getPipelineDialect(asCodeSettings: PipelineAsCodeSettings?): IPipelineDialect { + if (asCodeSettings == null) return CLASSIC.dialect + return with(asCodeSettings) { + getPipelineDialect( + inheritedDialect = inheritedDialect, + projectDialect = projectDialect, + pipelineDialect = pipelineDialect + ) + } + } + + fun getPipelineDialect( + inheritedDialect: Boolean?, + projectDialect: String?, + pipelineDialect: String? + ): IPipelineDialect { + return getPipelineDialectType( + inheritedDialect = inheritedDialect, + projectDialect = projectDialect, + pipelineDialect = pipelineDialect + ).dialect + } + + fun getPipelineDialectType( + inheritedDialect: Boolean?, + projectDialect: String?, + pipelineDialect: String? + ): PipelineDialectType { + return when { + // inheritedDialect为空和true都继承项目配置 + inheritedDialect != false && projectDialect != null -> + PipelineDialectType.valueOf(projectDialect) + + inheritedDialect == false && pipelineDialect != null -> + PipelineDialectType.valueOf(pipelineDialect) + + else -> + CLASSIC + } + } + + fun getPipelineDialectType(asCodeSettings: PipelineAsCodeSettings?): PipelineDialectType { + if (asCodeSettings == null) return CLASSIC + return with(asCodeSettings) { + getPipelineDialectType( + inheritedDialect = inheritedDialect, + projectDialect = projectDialect, + pipelineDialect = pipelineDialect + ) + } + } + + fun getPipelineDialectType( + channelCode: ChannelCode, + asCodeSettings: PipelineAsCodeSettings? + ): PipelineDialectType { + return when { + asCodeSettings == null -> CLASSIC + // stream并且开启pac需要使用制约模式 + channelCode == ChannelCode.GIT && asCodeSettings.enable -> + CONSTRAINED + + else -> { + with(asCodeSettings) { + getPipelineDialectType( + inheritedDialect = inheritedDialect, + projectDialect = projectDialect, + pipelineDialect = pipelineDialect + ) + } + } + } + } +} diff --git a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/event/CallBackData.kt b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/event/CallBackData.kt index 996d86cb0cb..5a5fec47db8 100644 --- a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/event/CallBackData.kt +++ b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/event/CallBackData.kt @@ -85,25 +85,34 @@ class CallBackData( val data: T ) +/** + * + * @see com.tencent.devops.common.event.enums.PipelineBuildStatusBroadCastEventType + */ enum class CallBackEvent { - DELETE_PIPELINE, - CREATE_PIPELINE, - UPDATE_PIPELINE, - STREAM_ENABLED, - RESTORE_PIPELINE, - BUILD_START, - BUILD_END, - BUILD_TASK_START, - BUILD_TASK_END, - BUILD_STAGE_START, - BUILD_STAGE_END, - BUILD_JOB_START, - BUILD_JOB_END, - BUILD_TASK_PAUSE, - PROJECT_CREATE, - PROJECT_UPDATE, - PROJECT_ENABLE, - PROJECT_DISABLE + DELETE_PIPELINE, /*流水线删除*/ + CREATE_PIPELINE, /*流水线创建*/ + UPDATE_PIPELINE, /*流水线更新,包括model和setting。*/ + STREAM_ENABLED, /*stream ci 开启/关闭*/ + RESTORE_PIPELINE, /*流水线恢复*/ + + BUILD_QUEUE, /*构建排队,包含并发超限时排队、并发组排队。*/ + BUILD_START, /*构建开始,不包含并发超限时排队、并发组排队。*/ + BUILD_END, /*构建结束*/ + BUILD_STAGE_START, /*stage开始*/ + BUILD_STAGE_END, /*stage结束*/ + BUILD_JOB_QUEUE, /*job排队,包含互斥组排队、构建机复用互斥排队、最大job并发排队。*/ + BUILD_JOB_START, /*job开始,不包含BUILD_JOB_QUEUE。如果job SKIP或没有可执行的插件,就不会有该事件。*/ + BUILD_JOB_END, /*job结束,job SKIP或没有可执行的插件时会有该事件。*/ + BUILD_AGENT_START, /*构建机启动,现在仅包含第三方构建机*/ + BUILD_TASK_START, /*插件开始*/ + BUILD_TASK_END, /*插件结束*/ + BUILD_TASK_PAUSE, /*插件前置暂停*/ + + PROJECT_CREATE, /*项目创建*/ + PROJECT_UPDATE, /*项目更新*/ + PROJECT_ENABLE, /*项目启用*/ + PROJECT_DISABLE /*项目禁用*/ } data class PipelineEvent( diff --git a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/extend/ModelCheckPlugin.kt b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/extend/ModelCheckPlugin.kt index a4da973a95b..7b66e9ffca4 100644 --- a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/extend/ModelCheckPlugin.kt +++ b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/extend/ModelCheckPlugin.kt @@ -30,6 +30,7 @@ package com.tencent.devops.common.pipeline.extend import com.tencent.devops.common.api.exception.ErrorCodeException import com.tencent.devops.common.pipeline.Model import com.tencent.devops.common.pipeline.container.Container +import com.tencent.devops.common.pipeline.dialect.IPipelineDialect import com.tencent.devops.common.pipeline.option.JobControlOption import com.tencent.devops.common.pipeline.pojo.element.Element import com.tencent.devops.common.pipeline.pojo.element.atom.BeforeDeleteParam @@ -45,6 +46,7 @@ interface ModelCheckPlugin { * 检查[model]编排的完整性,并返回[JobSize + ElementSize = MetaSize]所有元素数量 * @param userId 操作人 * @param oauthUser 当前流水线权限代持人 + * @param pipelineDialect 流水线方言,只有新增/编辑流水线或模版时才需要传入 * @throws RuntimeException 子类 将检查失败或异常的以[ErrorCodeException]类抛出 */ @Throws(ErrorCodeException::class) @@ -53,7 +55,8 @@ interface ModelCheckPlugin { projectId: String?, userId: String, isTemplate: Boolean = false, - oauthUser: String? = null + oauthUser: String? = null, + pipelineDialect: IPipelineDialect? = null ): Int /** diff --git a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/option/MatrixControlOption.kt b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/option/MatrixControlOption.kt index c0deded3ff5..8a120250a09 100644 --- a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/option/MatrixControlOption.kt +++ b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/option/MatrixControlOption.kt @@ -72,7 +72,7 @@ data class MatrixControlOption( /** * 根据[strategyStr], [includeCaseStr], [excludeCaseStr]计算后得到的矩阵配置 */ - fun convertMatrixConfig(buildContext: Map, asCodeEnabled: Boolean? = false): MatrixConfig { + fun convertMatrixConfig(buildContext: Map): MatrixConfig { val matrixConfig = try { // 由于yaml和json结构不同,就不放在同一函数进行解析了 convertStrategyYaml(buildContext) diff --git a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/StagePauseCheck.kt b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/StagePauseCheck.kt index 54a00096db9..5238e314f49 100644 --- a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/StagePauseCheck.kt +++ b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/StagePauseCheck.kt @@ -30,6 +30,7 @@ package com.tencent.devops.common.pipeline.pojo import com.tencent.devops.common.api.util.UUIDUtil import com.tencent.devops.common.api.util.timestampmilli import com.tencent.devops.common.pipeline.EnvReplacementParser +import com.tencent.devops.common.pipeline.dialect.IPipelineDialect import com.tencent.devops.common.pipeline.enums.BuildStatus import com.tencent.devops.common.pipeline.enums.ManualReviewAction import com.tencent.devops.common.pipeline.option.StageControlOption @@ -180,25 +181,44 @@ data class StagePauseCheck( /** * 进入审核流程前完成所有审核人变量替换 */ - fun parseReviewVariables(variables: Map, asCodeEnabled: Boolean?) { + fun parseReviewVariables(variables: Map, dialect: IPipelineDialect) { + val contextPair = EnvReplacementParser.getCustomExecutionContextByMap(variables) reviewGroups?.forEach { group -> if (group.status != null) return@forEach if (group.reviewers.isNotEmpty()) { val reviewers = group.reviewers.joinToString(",") - val realReviewers = EnvReplacementParser.parse(reviewers, variables, asCodeEnabled) - .split(",").toList() + val realReviewers = EnvReplacementParser.parse( + value = reviewers, + contextMap = variables, + onlyExpression = dialect.supportUseExpression(), + contextPair = contextPair + ).split(",").toList() group.reviewers = realReviewers } if (group.groups.isNotEmpty()) { val groups = group.groups.joinToString(",") - val realGroups = EnvReplacementParser.parse(groups, variables, asCodeEnabled) - .split(",").toList() + val realGroups = EnvReplacementParser.parse( + value = groups, + contextMap = variables, + onlyExpression = dialect.supportUseExpression(), + contextPair = contextPair + ).split(",").toList() group.groups = realGroups } } - reviewDesc = EnvReplacementParser.parse(reviewDesc, variables, asCodeEnabled) + reviewDesc = EnvReplacementParser.parse( + value = reviewDesc, + contextMap = variables, + onlyExpression = dialect.supportUseExpression(), + contextPair = contextPair + ) notifyGroup = notifyGroup?.map { - EnvReplacementParser.parse(it, variables, asCodeEnabled) + EnvReplacementParser.parse( + value = it, + contextMap = variables, + onlyExpression = dialect.supportUseExpression(), + contextPair = contextPair + ) }?.toMutableList() reviewParams?.forEach { it.parseValueWithType(variables) } } diff --git a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/TemplateInstanceCreateRequest.kt b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/TemplateInstanceCreateRequest.kt index c9bea63c3e8..9d9e8948e1d 100644 --- a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/TemplateInstanceCreateRequest.kt +++ b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/TemplateInstanceCreateRequest.kt @@ -48,5 +48,9 @@ data class TemplateInstanceCreateRequest( @get:Schema(title = "是否为空模板", required = false) var emptyTemplate: Boolean? = false, @get:Schema(title = "静态流水线组", required = false) - var staticViews: List = emptyList() + var staticViews: List = emptyList(), + @get:Schema(title = "是否继承项目流水线语言风格", required = false) + var inheritedDialect: Boolean? = true, + @get:Schema(title = "流水线语言风格", required = false) + var pipelineDialect: String? = null ) diff --git a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/setting/PipelineSetting.kt b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/setting/PipelineSetting.kt index 0b20127f6a8..41c077ce668 100644 --- a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/setting/PipelineSetting.kt +++ b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/pojo/setting/PipelineSetting.kt @@ -101,7 +101,9 @@ data class PipelineSetting( pipelineId: String, pipelineName: String, maxPipelineResNum: Int? = null, - failSubscription: Subscription? = null + failSubscription: Subscription? = null, + inheritedDialectSetting: Boolean? = null, + pipelineDialectSetting: String? = null ): PipelineSetting { return PipelineSetting( projectId = projectId, @@ -117,7 +119,10 @@ data class PipelineSetting( failSubscription = null, successSubscriptionList = emptyList(), failSubscriptionList = failSubscription?.let { listOf(it) }, - pipelineAsCodeSettings = PipelineAsCodeSettings() + pipelineAsCodeSettings = PipelineAsCodeSettings.initDialect( + inheritedDialect = inheritedDialectSetting, + pipelineDialect = pipelineDialectSetting + ) ) } } diff --git a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/utils/EventUtils.kt b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/utils/EventUtils.kt index e2ba28f9420..44685a17626 100644 --- a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/utils/EventUtils.kt +++ b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/utils/EventUtils.kt @@ -33,7 +33,11 @@ import com.tencent.devops.common.pipeline.event.CallBackEvent @Suppress("ComplexMethod") object EventUtils { + private val callBackEventMap = CallBackEvent.values().associateBy { it.name } fun PipelineBuildStatusBroadCastEvent.toEventType(): CallBackEvent? { + if (type != null && callBackEventMap[type!!.name] != null) { + return callBackEventMap[type!!.name] + } if (!taskId.isNullOrBlank()) { if (actionType == ActionType.START) { return CallBackEvent.BUILD_TASK_START diff --git a/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/utils/ExprReplacementUtil.kt b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/utils/ExprReplacementUtil.kt new file mode 100644 index 00000000000..6a923f71b0d --- /dev/null +++ b/src/backend/ci/core/common/common-pipeline/src/main/kotlin/com/tencent/devops/common/pipeline/utils/ExprReplacementUtil.kt @@ -0,0 +1,324 @@ +/* + * 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.devops.common.pipeline.utils + +import com.tencent.devops.common.api.exception.VariableNotFoundException +import com.tencent.devops.common.api.util.JsonUtil +import com.tencent.devops.common.expression.ContextNotFoundException +import com.tencent.devops.common.expression.ExecutionContext +import com.tencent.devops.common.expression.ExpressionParseException +import com.tencent.devops.common.expression.ExpressionParser +import com.tencent.devops.common.expression.context.ContextValueNode +import com.tencent.devops.common.expression.context.DictionaryContextData +import com.tencent.devops.common.expression.context.PipelineContextData +import com.tencent.devops.common.expression.context.RuntimeDictionaryContextData +import com.tencent.devops.common.expression.context.RuntimeNamedValue +import com.tencent.devops.common.expression.expression.EvaluationOptions +import com.tencent.devops.common.expression.expression.ExpressionOutput +import com.tencent.devops.common.expression.expression.IFunctionInfo +import com.tencent.devops.common.expression.expression.ParseExceptionKind +import com.tencent.devops.common.expression.expression.sdk.NamedValueInfo +import com.tencent.devops.common.pipeline.ExprReplacementOptions +import org.apache.tools.ant.filters.StringInputStream +import org.slf4j.LoggerFactory +import java.io.BufferedReader +import java.io.InputStreamReader + +@Suppress( + "LoopWithTooManyJumpStatements", + "ComplexCondition", + "ComplexMethod", + "NestedBlockDepth", + "ReturnCount", + "LongParameterList" +) +object ExprReplacementUtil { + private val logger = LoggerFactory.getLogger(ExprReplacementUtil::class.java) + + fun parseExpression(value: String, options: ExprReplacementOptions): String { + with(options) { + return try { + val (executeContext, nameValues) = contextPair + ?: getCustomExecutionContextByMap(contextMap) + ?: return value + parseExpression( + value = value, + context = executeContext, + nameValues = nameValues, + functions = functions, + output = output, + contextNotNull = contextNotNull + ) + } catch (ex: VariableNotFoundException) { + throw ex + } catch (ignore: Throwable) { + logger.warn("[$value]|EnvReplacementParser expression invalid: ", ignore) + value + } + } + } + + fun getCustomExecutionContextByMap( + variables: Map, + extendNamedValueMap: List? = null + ): Pair>? { + try { + val context = ExecutionContext(DictionaryContextData()) + val nameValue = mutableListOf() + extendNamedValueMap?.forEach { namedValue -> + nameValue.add(NamedValueInfo(namedValue.key, ContextValueNode())) + context.expressionValues.add( + namedValue.key, + RuntimeDictionaryContextData(namedValue) + ) + } + ExpressionParser.fillContextByMap(variables, context, nameValue) + return Pair(context, nameValue) + } catch (ignore: Throwable) { + logger.warn("EnvReplacementParser context invalid: $variables", ignore) + return null + } + } + + private fun parseExpression( + value: String, + nameValues: List, + context: ExecutionContext, + functions: Iterable? = null, + output: ExpressionOutput? = null, + contextNotNull: Boolean + ): String { + val strReader = InputStreamReader(StringInputStream(value)) + val bufferReader = BufferedReader(strReader) + val newValue = StringBuilder() + try { + var line = bufferReader.readLine() + while (line != null) { + // 跳过空行和注释行 + val blocks = findExpressions(line) + if (line.isBlank() || blocks.isEmpty()) { + newValue.append(line).append("\n") + line = bufferReader.readLine() + continue + } + val onceResult = parseExpressionLine( + value = line, + blocks = blocks, + context = context, + nameValues = nameValues, + functions = functions, + output = output, + contextNotNull = contextNotNull + ) + + val newLine = findExpressions(onceResult).let { + if (it.isEmpty()) { + onceResult + } else { + parseExpressionLine( + value = onceResult, + blocks = it, + context = context, + nameValues = nameValues, + functions = functions, + output = output, + contextNotNull = contextNotNull + ) + } + } + newValue.append(newLine).append("\n") + line = bufferReader.readLine() + } + } finally { + strReader.close() + bufferReader.close() + } + return newValue.toString().removeSuffix("\n") + } + + /** + * 解析表达式,根据 findExpressions 寻找的括号优先级进行解析 + */ + private fun parseExpressionLine( + value: String, + blocks: List>, + nameValues: List, + context: ExecutionContext, + functions: Iterable? = null, + output: ExpressionOutput? = null, + contextNotNull: Boolean + ): String { + var chars = value.toList() + blocks.forEachIndexed nextBlockLevel@{ blockLevel, blocksInLevel -> + blocksInLevel.forEachIndexed nextBlock@{ blockI, block -> + // 表达式因为含有 ${{ }} 所以起始向后推3位,末尾往前推两位 + val expression = chars.joinToString("").substring(block.startIndex + 3, block.endIndex - 1) + if (expression.isBlank()) return@nextBlock + val options = EvaluationOptions(contextNotNull) + var result = try { + ExpressionParser.createTree(expression, null, nameValues, functions)!! + .evaluate(null, context, options, output).value.let { + if (it is PipelineContextData) it.fetchValue() else it + }?.let { + JsonUtil.toJson(it, false) + } ?: "" + } catch (ignore: ContextNotFoundException) { + throw VariableNotFoundException( + variableKey = options.contextNotNull.errKey() + ) + } catch (ignore: ExpressionParseException) { + if (contextNotNull && ignore.kind == ParseExceptionKind.UnrecognizedNamedValue) { + throw VariableNotFoundException( + variableKey = ignore.expression + ) + } + return@nextBlock + } + + if ((blockLevel + 1 < blocks.size) && + !( + (block.startIndex - 1 >= 0 && chars[block.startIndex - 1] == '.') || + (block.endIndex + 1 < chars.size && chars[block.endIndex + 1] == '.') + ) + ) { + result = "'$result'" + } + + val charList = result.toList() + + // 将替换后的表达式嵌入原本的line + val startSub = if (block.startIndex - 1 < 0) { + listOf() + } else { + chars.slice(0 until block.startIndex) + } + val endSub = if (block.endIndex + 1 >= chars.size) { + listOf() + } else { + chars.slice(block.endIndex + 1 until chars.size) + } + chars = startSub + charList + endSub + + // 将替换后的字符查传递给后边的括号位数 + val diffNum = charList.size - (block.endIndex - block.startIndex + 1) + blocks.forEachIndexed { i, bl -> + bl.forEachIndexed level@{ j, b -> + if (i <= blockLevel && j <= blockI) { + return@level + } + if (blocks[i][j].startIndex > block.endIndex) { + blocks[i][j].startIndex += diffNum + } + if (blocks[i][j].endIndex > block.endIndex) { + blocks[i][j].endIndex += diffNum + } + } + } + } + } + + return chars.joinToString("") + } + + /** + * 寻找语句中包含 ${{}}的表达式的位置,返回成对的位置坐标,并根据优先级排序 + * 优先级算法目前暂定为 从里到外,从左到右 + * @param levelMax 返回的最大层数,从深到浅。默认为2层 + * 例如: 替换顺序如数字所示 ${{ 4 ${{ 2 ${{ 1 }} }} ${{ 3 }} }} + * @return [ 层数次序 [ 括号 ] ] [[1], [2, 3], [4]]]] + */ + private fun findExpressions(condition: String, levelMax: Int = 2): List> { + val stack = ArrayDeque() + var index = 0 + val chars = condition.toCharArray() + val levelMap = mutableMapOf>() + while (index < chars.size) { + if (index + 2 < chars.size && chars[index] == '$' && chars[index + 1] == '{' && chars[index + 2] == '{' + ) { + stack.addLast(index) + index += 3 + continue + } + + if (index + 1 < chars.size && chars[index] == '}' && chars[index + 1] == '}' + ) { + val start = stack.removeLastOrNull() + if (start != null) { + // 栈里剩下几个前括号,这个完整括号的优先级就是多少 + val level = stack.size + 1 + if (levelMap.containsKey(level)) { + levelMap[level]!!.add(ExpressionBlock(start, index + 1)) + } else { + levelMap[level] = mutableListOf(ExpressionBlock(start, index + 1)) + } + } + index += 2 + continue + } + + index++ + } + + if (levelMap.isEmpty()) { + return listOf() + } + + val result = mutableListOf>() + var max = 0 + var listIndex = 0 + run end@{ + levelMap.keys.sortedDescending().forEach result@{ level -> + val blocks = levelMap[level] ?: return@result + blocks.sortBy { it.startIndex } + blocks.forEach { block -> + if (result.size < listIndex + 1) { + result.add(mutableListOf(block)) + } else { + result[listIndex].add(block) + } + } + listIndex++ + max++ + if (max == levelMax) { + return@end + } + } + } + return result + } + + /** + * 表达式括号项 ${{ }} + * @param startIndex 括号开始位置即 $ 位置 + * @param endIndex 括号结束位置即最后一个 } 位置 + */ + data class ExpressionBlock( + var startIndex: Int, + var endIndex: Int + ) +} diff --git a/src/backend/ci/core/common/common-pipeline/src/test/kotlin/com/tencent/devops/common/pipeline/EnvReplacementParserTest.kt b/src/backend/ci/core/common/common-pipeline/src/test/kotlin/com/tencent/devops/common/pipeline/EnvReplacementParserTest.kt index fda0762b9c4..d41c80b7b5b 100644 --- a/src/backend/ci/core/common/common-pipeline/src/test/kotlin/com/tencent/devops/common/pipeline/EnvReplacementParserTest.kt +++ b/src/backend/ci/core/common/common-pipeline/src/test/kotlin/com/tencent/devops/common/pipeline/EnvReplacementParserTest.kt @@ -346,10 +346,19 @@ internal class EnvReplacementParserTest { val command8 = "echo \${{ variables.hello }}" val command9 = "echo \${{ ci.workspace }}" + val command10 = mutableMapOf( + "params" to mutableListOf( + mutableMapOf( + "key" to "instance", + "value" to "\${{variables.instance}}" + ) + ) + ) val data = mapOf( "variables.abc" to "variables.value", - "variables.hello" to "hahahahaha" + "variables.hello" to "hahahahaha", + "variables.instance" to "{\"instances\":[{\"cluster\":\"ci-prod\",\"pod\":\"ci-123\"}]}" ) // 与EnvUtils的差异点:不支持传可空对象 // Assertions.assertEquals("", EnvReplacementParser.parse(null, data)) @@ -396,6 +405,13 @@ internal class EnvReplacementParserTest { onlyExpression = true ) ) + val command10Expected = """ + {"params":[{"key":"instance","value":"{\"instances\":[{\"cluster\":\"ci-prod\",\"pod\":\"ci-123\"}]}"}]} + """.trimIndent() + Assertions.assertEquals( + command10Expected, + EnvReplacementParser.parse(command10, data, true) + ) } @Test @@ -559,4 +575,52 @@ echo true""" println("template=$template\nreplaced=$buff\n") Assertions.assertEquals(expect, buff) } + + @Test + fun containsExpressions() { + val command = "{\"age\": \${{age}} , \"sex\": \"boy\", \"name\": \${{name}}}" + Assertions.assertTrue(EnvReplacementParser.containsExpressions(command)) + + val command1 = "hello \${{variables.abc}} world" + Assertions.assertTrue(EnvReplacementParser.containsExpressions(command1)) + + val command2 = "\${{variables.abc}}world" + Assertions.assertTrue(EnvReplacementParser.containsExpressions(command2)) + + val command3 = "hello\${{variables.abc}}" + Assertions.assertTrue(EnvReplacementParser.containsExpressions(command3)) + + val command4 = "hello\${{variables.abc" + Assertions.assertFalse(EnvReplacementParser.containsExpressions(command4)) + + val command5 = "hello\${{variables.abc}" + Assertions.assertFalse(EnvReplacementParser.containsExpressions(command5)) + + val command6 = "hello\${variables.abc}}" + Assertions.assertFalse(EnvReplacementParser.containsExpressions(command6)) + + val command7 = "hello\$variables.abc}}" + Assertions.assertFalse(EnvReplacementParser.containsExpressions(command7)) + + val command8 = "echo \${{ variables.hello }}" + Assertions.assertTrue(EnvReplacementParser.containsExpressions(command8)) + + val command9 = "echo \${{ ci.workspace }} || \${{variables.hello}}" + Assertions.assertTrue(EnvReplacementParser.containsExpressions(command9)) + + val command10 = "echo \${{ ci.xyz == 'zzzz' }}" + Assertions.assertTrue(EnvReplacementParser.containsExpressions(command10)) + + val command11 = "echo \${{ variables.xyz == 'zzzz' }}" + Assertions.assertTrue(EnvReplacementParser.containsExpressions(command11)) + + val command12 = "echo \${{ strToTime(variables.date) > strToTime('2023-03-16 12:06:21') }}" + Assertions.assertTrue(EnvReplacementParser.containsExpressions(command12)) + + val command13 = "echo \${{ strToTime(variables.date) > strToTime('2023-03-14 12:06:21') }}" + Assertions.assertTrue(EnvReplacementParser.containsExpressions(command13)) + + val command14 = "\${{ strToTime(\${{variables.date}}) > strToTime('2023-03-14 12:06:21') }}" + Assertions.assertTrue(EnvReplacementParser.containsExpressions(command14)) + } } diff --git a/src/backend/ci/core/common/common-pipeline/src/test/kotlin/com/tencent/devops/common/pipeline/ExprReplaceEnvVarUtilTest.kt b/src/backend/ci/core/common/common-pipeline/src/test/kotlin/com/tencent/devops/common/pipeline/ExprReplaceEnvVarUtilTest.kt new file mode 100644 index 00000000000..f6a11c54c0a --- /dev/null +++ b/src/backend/ci/core/common/common-pipeline/src/test/kotlin/com/tencent/devops/common/pipeline/ExprReplaceEnvVarUtilTest.kt @@ -0,0 +1,319 @@ +/* + * 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.devops.common.pipeline + +import com.tencent.devops.common.api.util.JsonUtil +import com.tencent.devops.common.api.util.JsonUtil.toJson +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +@Suppress("ALL", "UNCHECKED_CAST") +class ExprReplaceEnvVarUtilTest { + + private val envMap: MutableMap = HashMap() + + @BeforeEach + fun setup() { + envMap["normalStrEnvVar"] = "123" + envMap["specStrEnvVar"] = "D:\\tmp\\hha" + envMap["jsonStrEnvVar"] = "{\"abc\":\"123\"}" + } + + private val lineSeparator = System.getProperty("line.separator") + private val jsonExcept = "{$lineSeparator" + + " \"abc\" : \"变量替换测试_{\\\"abc\\\":\\\"123\\\"}\"$lineSeparator" + + "}" + + private val arrayJsonExcept = "[ \"变量替换测试_{\\\"abc\\\":\\\"123\\\"}\" ]" + + @Test + fun replaceList() { + val testBean = TestBean( + testBeanKey = "bean变量替换测试_\${{specStrEnvVar}}", + testBeanValue = "{\"abc\":\"变量替换测试_\${{jsonStrEnvVar}}\"}" + ) + // 对list对象进行变量替换 + val originDataListObj = ArrayList() + originDataListObj.add("变量替换测试_\${{normalStrEnvVar}}") + originDataListObj.add("变量替换测试_\${{specStrEnvVar}}") + originDataListObj.add("变量替换测试_\${{jsonStrEnvVar}}") + originDataListObj.add("{\"abc\":\"变量替换测试_\${{jsonStrEnvVar}}\"}") + originDataListObj.add("[\"变量替换测试_\${{jsonStrEnvVar}}\"]") + originDataListObj.add(testBean) + val dataMapObj: MutableMap = HashMap() + dataMapObj["dataMapKey"] = "变量替换测试_\${{specStrEnvVar}}" + dataMapObj["testBean"] = testBean + originDataListObj.add(dataMapObj) + val convertDataObj = ExprReplaceEnvVarUtil.replaceEnvVar(originDataListObj, envMap) as List<*> + + assertEquals("变量替换测试_${envMap["normalStrEnvVar"]}", convertDataObj[0]) + assertEquals("变量替换测试_${envMap["specStrEnvVar"]}", convertDataObj[1]) + assertEquals("变量替换测试_${envMap["jsonStrEnvVar"]}", convertDataObj[2]) + assertEquals(jsonExcept, convertDataObj[3]) + assertEquals(arrayJsonExcept, convertDataObj[4]) + + val convertTestBean = convertDataObj[5] as TestBean + assertEquals("bean变量替换测试_${envMap["specStrEnvVar"]}", convertTestBean.testBeanKey) + assertEquals(jsonExcept, convertTestBean.testBeanValue) + } + + @Test + fun replaceIllegalJson() { + val objectJson = "{\"abc:\"变量替换测试_\${{normalStrEnvVar}}\"" + val convertDataObj1 = ExprReplaceEnvVarUtil.replaceEnvVar(objectJson, envMap) + println(convertDataObj1) + assertEquals("{\"abc:\"变量替换测试_${envMap["normalStrEnvVar"]}\"", convertDataObj1) + + val arrayJson = "[1, \"变量替换测试_\${{normalStrEnvVar}}\"" + val convertDataObj2 = ExprReplaceEnvVarUtil.replaceEnvVar(arrayJson, envMap) + println(convertDataObj2) + assertEquals("[1, \"变量替换测试_${envMap["normalStrEnvVar"]}\"", convertDataObj2) + } + + @Test + fun replaceSet() { + + val testBean = TestBean( + testBeanKey = "bean变量替换测试_\${{specStrEnvVar}}", + testBeanValue = "{\"abc\":\"变量替换测试_\${{jsonStrEnvVar}}\"}" + ) + // 对set对象进行变量替换 + val originDataSetObj = HashSet() + originDataSetObj.add("1变量替换测试_\${{normalStrEnvVar}}") + originDataSetObj.add("2变量替换测试_\${{specStrEnvVar}}") + originDataSetObj.add("3变量替换测试_\${{jsonStrEnvVar}}") + originDataSetObj.add("{\"abc\":\"变量替换测试_\${{jsonStrEnvVar}}\"}") + originDataSetObj.add("[\"变量替换测试_\${{jsonStrEnvVar}}\"]") + originDataSetObj.add(testBean) + + val setDataMapObj: MutableMap = HashMap() + setDataMapObj["dataMapKey"] = "变量替换测试_\${{specStrEnvVar}}" + setDataMapObj["testBean"] = testBean + originDataSetObj.add(setDataMapObj) + val convertDataObj = (ExprReplaceEnvVarUtil.replaceEnvVar(originDataSetObj, envMap) as Set<*>) + + convertDataObj.forEach { member -> + when { + member is Map<*, *> -> { + member.forEach { sm -> + when { + sm.key.toString() == "testBean" -> { + assertEquals( + "bean变量替换测试_${envMap["specStrEnvVar"]}", + (sm.value as TestBean).testBeanKey + ) + assertEquals(jsonExcept, (sm.value as TestBean).testBeanValue) + } + sm.key.toString() == "dataMapKey" -> { + assertEquals("变量替换测试_${envMap["specStrEnvVar"]}", sm.value) + } + else -> { + assertEquals(member.toString(), "setDataMapObj") + } + } + } + } + member is TestBean -> { + assertEquals("bean变量替换测试_${envMap["specStrEnvVar"]}", member.testBeanKey) + assertEquals(jsonExcept, member.testBeanValue) + } + member.toString().startsWith("1") -> { + assertEquals("1变量替换测试_${envMap["normalStrEnvVar"]}", member) + } + member.toString().startsWith("2") -> { + assertEquals("2变量替换测试_${envMap["specStrEnvVar"]}", member) + } + member.toString().startsWith("3") -> { + assertEquals("3变量替换测试_${envMap["jsonStrEnvVar"]}", member) + } + member.toString().startsWith("{") -> { + assertEquals(jsonExcept, member) + } + member.toString().startsWith("[") -> { + assertEquals(arrayJsonExcept, member) + } + else -> { + assertEquals(member.toString(), "convertDataObj") + } + } + } + } + + @Test + fun replaceMapWithTestBean() { + // 对map对象进行变量替换 + val originDataMapObj: MutableMap = HashMap() + originDataMapObj["normalStrEnvVarKey"] = "变量替换测试_\${{normalStrEnvVar}}" + originDataMapObj["specStrEnvVarKey"] = "变量替换测试_\${{specStrEnvVar}}" + originDataMapObj["jsonStrEnvVarKey1"] = "变量替换测试_\${{jsonStrEnvVar}}" + originDataMapObj["jsonStrEnvVarKey2"] = "{\"abc\":\"变量替换测试_\${{jsonStrEnvVar}}\"}" + originDataMapObj["jsonStrEnvVarKey3"] = "\${{jsonStrEnvVar}}" + var originSubDataMapObj: MutableMap? = HashMap() + originSubDataMapObj!!["normalStrEnvVarKey"] = "变量替换测试_\${{normalStrEnvVar}}" + originSubDataMapObj["specStrEnvVarKey"] = "变量替换测试_\${{specStrEnvVar}}" + originSubDataMapObj["jsonStrEnvVarKey1"] = "变量替换测试_\${{jsonStrEnvVar}}" + originSubDataMapObj["jsonStrEnvVarKey2"] = "\${{jsonStrEnvVar}}" + + val testBean = TestBean( + testBeanKey = "变量替换测试_\${{specStrEnvVar}}", + testBeanValue = "{\"abc\":\"变量替换测试_\${{jsonStrEnvVar}}\"}" + ) + originSubDataMapObj["testBean"] = testBean + originDataMapObj["originSubDataMapObj"] = originSubDataMapObj + + val cpb = ExprReplaceEnvVarUtil.replaceEnvVar(originDataMapObj, envMap) + val testBeanMap = ((cpb as Map)["originSubDataMapObj"] as Map)["testBean"] as TestBean + assertEquals("变量替换测试_${envMap["specStrEnvVar"]}", testBeanMap.testBeanKey) + assertEquals(jsonExcept, testBeanMap.testBeanValue) + // 判断map中jsonStrEnvVarKey3对应的值进行变量替换后能否正常转换为json串 + assertEquals(envMap["jsonStrEnvVar"], (cpb as Map)["jsonStrEnvVarKey3"]!!) + originSubDataMapObj = cpb["originSubDataMapObj"] as MutableMap? + // 判断嵌套的map中jsonStrEnvVarKey2对应的值进行变量替换后能否正常转换为json串 + assertEquals(envMap["jsonStrEnvVar"], originSubDataMapObj!!["jsonStrEnvVarKey2"]!!) + } + + @Test + fun replaceTestComplexBean() { + // 对普通的javaBean对象进行转换 + val testComplexBean = TestComplexBean() + testComplexBean.testBeanKey = "变量替换测试_\${{specStrEnvVar}}" + testComplexBean.testBeanValue = "[\"变量替换测试_\${{jsonStrEnvVar}}\"]" + + val dataList = ArrayList() + dataList.add("变量替换测试_\${{normalStrEnvVar}}") + dataList.add("变量替换测试_\${{specStrEnvVar}}") + dataList.add("变量替换测试_\${{jsonStrEnvVar}}") + dataList.add("{\"abc\":\"变量替换测试_\${{jsonStrEnvVar}}\"}") + dataList.add("[\"变量替换测试_\${{jsonStrEnvVar}}\"]") + testComplexBean.dataList = dataList + + var dataMap: MutableMap = HashMap() + dataMap["normalStrEnvVarKey"] = " 变量替换测试_\${{normalStrEnvVar}} " + dataMap["specStrEnvVarKey"] = "变量替换测试_\${{specStrEnvVar}}" + dataMap["jsonStrEnvVarKey1"] = "变量替换测试_\${{jsonStrEnvVar}}" + dataMap["jsonStrEnvVarKey2"] = "{\"abc\":\"变量替换测试_\${{jsonStrEnvVar}}\"}" + dataMap["jsonStrEnvVarKey3"] = "[\"变量替换测试_\${{jsonStrEnvVar}}\"]" + val subDataMap: MutableMap = HashMap() + subDataMap["normalStrEnvVarKey"] = "变量替换测试_\${{normalStrEnvVar}}" + subDataMap["specStrEnvVarKey"] = "变量替换测试_\${{specStrEnvVar}}" + subDataMap["jsonStrEnvVarKey1"] = "变量替换测试_\${{jsonStrEnvVar}}" + subDataMap["jsonStrEnvVarKey2"] = "{\"abc\":\"变量替换测试_\${{jsonStrEnvVar}}\"}" + + val testBean = TestBean( + testBeanKey = "bean变量替换测试_\${{specStrEnvVar}}", + testBeanValue = "{\"abc\":\"bean变量替换测试_\${{jsonStrEnvVar}}\"}" + ) + subDataMap["testBean"] = testBean + dataMap["subDataMap"] = subDataMap + testComplexBean.dataMap = dataMap + + val dataSet = HashSet() + dataSet.add("变量替换测试_\${{normalStrEnvVar}}") + dataSet.add("变量替换测试_\${{specStrEnvVar}}") + dataSet.add("变量替换测试_\${{jsonStrEnvVar}}") + dataSet.add("{\"abc\":\"变量替换测试_\${{jsonStrEnvVar}}\"}") + dataSet.add("[\"变量替换测试_\${{jsonStrEnvVar}}\"]") + testComplexBean.dataSet = dataSet + + // start to test + var convertDataObj = ExprReplaceEnvVarUtil.replaceEnvVar(testComplexBean, envMap) + val convertBean = convertDataObj as TestComplexBean + assertEquals("变量替换测试_${envMap["specStrEnvVar"]}", convertBean.testBeanKey) + + assertEquals("变量替换测试_${envMap["normalStrEnvVar"]}", convertBean.dataList!![0]) + assertEquals("变量替换测试_${envMap["specStrEnvVar"]}", convertBean.dataList!![1]) + assertEquals("变量替换测试_${envMap["jsonStrEnvVar"]}", convertBean.dataList!![2]) + assertEquals(jsonExcept, convertBean.dataList!![3]) + assertEquals("[ \"变量替换测试_{\\\"abc\\\":\\\"123\\\"}\" ]", convertBean.dataList!![4]) + + assertEquals(" 变量替换测试_${envMap["normalStrEnvVar"]} ", convertBean.dataMap!!["normalStrEnvVarKey"]) + assertEquals("变量替换测试_${envMap["specStrEnvVar"]}", convertBean.dataMap!!["specStrEnvVarKey"]) + assertEquals("变量替换测试_${envMap["jsonStrEnvVar"]}", convertBean.dataMap!!["jsonStrEnvVarKey1"]) + assertEquals(jsonExcept, convertBean.dataMap!!["jsonStrEnvVarKey2"]) + assertEquals(arrayJsonExcept, convertBean.dataMap!!["jsonStrEnvVarKey3"]) + + // 替换包含null的对象 + dataMap = HashMap() + dataMap["key1"] = "变量" + dataMap["key2"] = arrayOf(null, "哈哈") + + convertDataObj = ExprReplaceEnvVarUtil.replaceEnvVar(dataMap, envMap) as Map<*, *> + assertEquals(dataMap["key1"], convertDataObj["key1"]) + assertEquals(toJson(dataMap["key2"]!!), convertDataObj["key2"]) + println("convertDataObj=$convertDataObj") + } + + @Test + fun replaceEnvVar() { + + // 对普通字符串进行普通字符串变量替换 + var originDataObj: Any = "变量替换测试_\${{normalStrEnvVar}}" + var convertDataObj = ExprReplaceEnvVarUtil.replaceEnvVar(originDataObj, envMap) + assertEquals("变量替换测试_123", toJson(convertDataObj)) + + // 对普通字符串进行带特殊字符字符串变量替换 + originDataObj = "变量替换测试_\${{specStrEnvVar}}" + convertDataObj = ExprReplaceEnvVarUtil.replaceEnvVar(originDataObj, envMap) + assertEquals("变量替换测试_D:\\tmp\\hha", toJson(convertDataObj)) + + // 对普通字符串进行json字符串变量替换 + originDataObj = "变量替换测试_\${{jsonStrEnvVar}}" + convertDataObj = ExprReplaceEnvVarUtil.replaceEnvVar(originDataObj, envMap) + assertEquals("变量替换测试_{\"abc\":\"123\"}", toJson(convertDataObj)) + + // number类型变量替换 + originDataObj = "[1,2,3]" + convertDataObj = ExprReplaceEnvVarUtil.replaceEnvVar(originDataObj, envMap) + println(toJson(convertDataObj)) + assertEquals(toJson(JsonUtil.to(originDataObj, List::class.java)), toJson(convertDataObj)) + + // 魔法数字符创测试 + convertDataObj = ExprReplaceEnvVarUtil.replaceEnvVar("12E2", envMap) + assertEquals("12E2", toJson(convertDataObj)) + // 替换”[133]-[sid-${normalStrEnvVar}]-[sid-zhiliang-test1]“带多个[]的字符串 + convertDataObj = ExprReplaceEnvVarUtil.replaceEnvVar( + "[133]-[sid-\${{normalStrEnvVar}}]-[sid-zhiliang-test1]", + envMap + ) + assertEquals("[133]-[sid-123]-[sid-zhiliang-test1]", toJson(convertDataObj)) + } + + internal data class TestBean( + var testBeanKey: String? = null, + var testBeanValue: String? = null + ) + + internal data class TestComplexBean( + var testBeanKey: String? = null, + var testBeanValue: String? = null, + var dataList: List<*>? = null, + var dataMap: Map<*, *>? = null, + var dataSet: Set<*>? = null + ) +} diff --git a/src/backend/ci/core/common/common-pipeline/src/test/kotlin/com/tencent/devops/common/pipeline/dialect/PipelineDialectUtilTest.kt b/src/backend/ci/core/common/common-pipeline/src/test/kotlin/com/tencent/devops/common/pipeline/dialect/PipelineDialectUtilTest.kt new file mode 100644 index 00000000000..c3878111cea --- /dev/null +++ b/src/backend/ci/core/common/common-pipeline/src/test/kotlin/com/tencent/devops/common/pipeline/dialect/PipelineDialectUtilTest.kt @@ -0,0 +1,82 @@ +/* + * 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.devops.common.pipeline.dialect + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class PipelineDialectUtilTest { + + @Test + fun testGetDialect() { + val actual = PipelineDialectUtil.getPipelineDialect(null, null, null) + Assertions.assertEquals(PipelineDialectType.CLASSIC.dialect, actual) + + val actual2 = PipelineDialectUtil.getPipelineDialect( + inheritedDialect = null, + projectDialect = PipelineDialectType.CLASSIC.name, + pipelineDialect = null + ) + Assertions.assertEquals(PipelineDialectType.CLASSIC.dialect, actual2) + + val actual3 = PipelineDialectUtil.getPipelineDialect( + inheritedDialect = true, + projectDialect = null, + pipelineDialect = null + ) + Assertions.assertEquals(PipelineDialectType.CLASSIC.dialect, actual3) + + val actual4 = PipelineDialectUtil.getPipelineDialect( + inheritedDialect = true, + projectDialect = null, + pipelineDialect = PipelineDialectType.CONSTRAINED.name + ) + Assertions.assertEquals(PipelineDialectType.CLASSIC.dialect, actual4) + + val actual5 = PipelineDialectUtil.getPipelineDialect( + inheritedDialect = false, + projectDialect = null, + pipelineDialect = PipelineDialectType.CONSTRAINED.name + ) + Assertions.assertEquals(PipelineDialectType.CONSTRAINED.dialect, actual5) + + val actual6 = PipelineDialectUtil.getPipelineDialect( + inheritedDialect = true, + projectDialect = PipelineDialectType.CLASSIC.name, + pipelineDialect = PipelineDialectType.CONSTRAINED.name + ) + Assertions.assertEquals(PipelineDialectType.CLASSIC.dialect, actual6) + + val actual7 = PipelineDialectUtil.getPipelineDialect( + inheritedDialect = false, + projectDialect = PipelineDialectType.CLASSIC.name, + pipelineDialect = PipelineDialectType.CONSTRAINED.name + ) + Assertions.assertEquals(PipelineDialectType.CONSTRAINED.dialect, actual7) + } +} diff --git a/src/backend/ci/core/common/common-pipeline/src/test/kotlin/com/tencent/devops/common/pipeline/pojo/element/ElementTest.kt b/src/backend/ci/core/common/common-pipeline/src/test/kotlin/com/tencent/devops/common/pipeline/pojo/element/ElementTest.kt index 4fde8155858..9aa331569ad 100644 --- a/src/backend/ci/core/common/common-pipeline/src/test/kotlin/com/tencent/devops/common/pipeline/pojo/element/ElementTest.kt +++ b/src/backend/ci/core/common/common-pipeline/src/test/kotlin/com/tencent/devops/common/pipeline/pojo/element/ElementTest.kt @@ -51,7 +51,7 @@ class ElementTest { @Test fun testElementJsonOrder() { val jsonFile = ElementTest::class.java.classLoader.getResource("windowsElement.json") - val expected = jsonFile!!.readText().trim('\n') + val expected = jsonFile!!.readText().trim() val wel = WindowsScriptElement( id = "e-326ce1c320204980a3d2a0f241bccd63", name = "batch script", diff --git a/src/backend/ci/core/common/common-service/src/main/kotlin/com/tencent/devops/common/service/utils/CommonUtils.kt b/src/backend/ci/core/common/common-service/src/main/kotlin/com/tencent/devops/common/service/utils/CommonUtils.kt index 082e862d9ac..2ee25f686e5 100755 --- a/src/backend/ci/core/common/common-service/src/main/kotlin/com/tencent/devops/common/service/utils/CommonUtils.kt +++ b/src/backend/ci/core/common/common-service/src/main/kotlin/com/tencent/devops/common/service/utils/CommonUtils.kt @@ -247,7 +247,8 @@ object CommonUtils { private fun getProdDbClusterName(profile: Profile): String { // 从配置文件获取db集群名称列表 val dbClusterNames = (SpringContextUtil.getValue("bk.db.clusterNames") ?: PROFILE_PRODUCTION).split(",") - val activeProfiles = profile.getActiveProfiles() + val activeProfiles = profile.getActiveProfiles().toMutableList() + activeProfiles.remove(PROFILE_PRODUCTION) var finalDbClusterName = PROFILE_PRODUCTION run breaking@{ // 获取当前服务器集群对应的db集群名称 diff --git a/src/backend/ci/core/common/common-webhook/api-common-webhook/src/main/kotlin/com/tencent/devops/common/webhook/pojo/code/github/GithubPullRequestEvent.kt b/src/backend/ci/core/common/common-webhook/api-common-webhook/src/main/kotlin/com/tencent/devops/common/webhook/pojo/code/github/GithubPullRequestEvent.kt index 67f37e2c695..75dd1413c2a 100644 --- a/src/backend/ci/core/common/common-webhook/api-common-webhook/src/main/kotlin/com/tencent/devops/common/webhook/pojo/code/github/GithubPullRequestEvent.kt +++ b/src/backend/ci/core/common/common-webhook/api-common-webhook/src/main/kotlin/com/tencent/devops/common/webhook/pojo/code/github/GithubPullRequestEvent.kt @@ -118,7 +118,7 @@ data class GithubPullRequest( @JsonProperty("html_url") val htmlUrl: String, // https://github.com/yongyiduan/webhook-test/pull/1 @JsonProperty("id") - val id: Int, // 973279061 + val id: Long, // 973279061 @JsonProperty("issue_url") val issueUrl: String, // https://api.github.com/repos/yongyiduan/webhook-test/issues/1 @JsonProperty("labels") @@ -189,7 +189,7 @@ data class GithubMilestone( @JsonProperty("html_url") val htmlUrl: String, // https://github.com/octocat/Hello-World/milestones/v1.0 @JsonProperty("id") - val id: Int, // 1002604 + val id: Long, // 1002604 // @JsonProperty("labels_url") // val labelsUrl: String, // https://api.github.com/repos/octocat/Hello-World/milestones/1/labels @JsonProperty("node_id") diff --git a/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/dao/PipelineDockerIPInfoDao.kt b/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/dao/PipelineDockerIPInfoDao.kt index f3bbed81eb6..202d01b730b 100644 --- a/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/dao/PipelineDockerIPInfoDao.kt +++ b/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/dao/PipelineDockerIPInfoDao.kt @@ -272,7 +272,6 @@ class PipelineDockerIPInfoDao { fun getAvailableDockerIpList( dslContext: DSLContext, - grayEnv: Boolean, clusterName: DockerHostClusterType, cpuLoad: Int, memLoad: Int, @@ -285,7 +284,6 @@ class PipelineDockerIPInfoDao { val conditions = mutableListOf( ENABLE.eq(true), - GRAY_ENV.eq(grayEnv), CLUSTER_NAME.eq(clusterName.name) ) @@ -316,14 +314,12 @@ class PipelineDockerIPInfoDao { fun getDockerIpList( dslContext: DSLContext, enable: Boolean, - grayEnv: Boolean, clusterName: DockerHostClusterType? = null ): Result { with(TDispatchPipelineDockerIpInfo.T_DISPATCH_PIPELINE_DOCKER_IP_INFO) { val conditions = mutableListOf( - ENABLE.eq(enable), - GRAY_ENV.eq(grayEnv) + ENABLE.eq(enable) ) if (clusterName != null) { @@ -338,14 +334,12 @@ class PipelineDockerIPInfoDao { fun getEnableDockerIpCount( dslContext: DSLContext, - grayEnv: Boolean, clusterName: DockerHostClusterType ): Long { with(TDispatchPipelineDockerIpInfo.T_DISPATCH_PIPELINE_DOCKER_IP_INFO) { return dslContext.selectCount() .from(this) .where(ENABLE.eq(true)) - .and(GRAY_ENV.eq(grayEnv)) .and(CLUSTER_NAME.eq(clusterName.name)) .fetchOne(0, Long::class.java)!! } @@ -353,14 +347,12 @@ class PipelineDockerIPInfoDao { fun getAllDockerIpCount( dslContext: DSLContext, - grayEnv: Boolean, clusterName: DockerHostClusterType ): Long? { with(TDispatchPipelineDockerIpInfo.T_DISPATCH_PIPELINE_DOCKER_IP_INFO) { return dslContext.selectCount() .from(this) - .where(GRAY_ENV.eq(grayEnv)) - .and(CLUSTER_NAME.eq(clusterName.name)) + .where(CLUSTER_NAME.eq(clusterName.name)) .fetchOne(0, Long::class.java) } } diff --git a/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/schedule/VmStatusScheduler.kt b/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/schedule/VmStatusScheduler.kt index 63a877bba02..ad2d1f869c9 100644 --- a/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/schedule/VmStatusScheduler.kt +++ b/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/schedule/VmStatusScheduler.kt @@ -30,7 +30,6 @@ package com.tencent.devops.dispatch.docker.schedule import com.tencent.devops.common.api.util.timestamp import com.tencent.devops.common.redis.RedisLock import com.tencent.devops.common.redis.RedisOperation -import com.tencent.devops.common.service.gray.Gray import com.tencent.devops.dispatch.docker.common.Constants import com.tencent.devops.dispatch.docker.dao.PipelineDockerIPInfoDao import com.tencent.devops.model.dispatch.tables.records.TDispatchPipelineDockerIpInfoRecord @@ -43,7 +42,6 @@ import org.springframework.stereotype.Component @Component class VmStatusScheduler @Autowired constructor( private val dslContext: DSLContext, - private val gray: Gray, private val pipelineDockerIpInfoDao: PipelineDockerIPInfoDao, private val redisOperation: RedisOperation ) { @@ -61,8 +59,8 @@ class VmStatusScheduler @Autowired constructor( try { val lockSuccess = redisLock.tryLock() if (lockSuccess) { - logger.info("Start check VM status gray: ${gray.isGray()}") - val dockerIpList = pipelineDockerIpInfoDao.getDockerIpList(dslContext, true, gray.isGray()) + logger.info("Start check VM status.") + val dockerIpList = pipelineDockerIpInfoDao.getDockerIpList(dslContext, true) dockerIpList.stream().forEach { singleDockerIpCheck(it) } diff --git a/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/service/DispatchDockerService.kt b/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/service/DispatchDockerService.kt index 1c3f0550638..9224cfbd3ca 100644 --- a/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/service/DispatchDockerService.kt +++ b/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/service/DispatchDockerService.kt @@ -140,8 +140,7 @@ class DispatchDockerService @Autowired constructor( logger.info("$userId update all docker enable.") val dockerUnavailableList = pipelineDockerIPInfoDao.getDockerIpList( dslContext = dslContext, - enable = false, - grayEnv = gray.isGray() + enable = false ) dockerUnavailableList.forEach { diff --git a/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/service/DockerHostBuildService.kt b/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/service/DockerHostBuildService.kt index 0e093a580b5..fb73fb689e5 100644 --- a/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/service/DockerHostBuildService.kt +++ b/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/service/DockerHostBuildService.kt @@ -121,7 +121,6 @@ class DockerHostBuildService @Autowired constructor( val dockerIpList = pipelineDockerIPInfoDao.getDockerIpList( dslContext = dslContext, enable = true, - grayEnv = false, clusterName = clusterType ) @@ -135,7 +134,6 @@ class DockerHostBuildService @Autowired constructor( enableNode = dockerIpList.size, totalNode = pipelineDockerIPInfoDao.getAllDockerIpCount( dslContext = dslContext, - grayEnv = false, clusterName = clusterType )?.toInt() ?: 0 ) @@ -163,7 +161,6 @@ class DockerHostBuildService @Autowired constructor( enableNode = dockerIpList.size, totalNode = pipelineDockerIPInfoDao.getAllDockerIpCount( dslContext = dslContext, - grayEnv = false, clusterName = clusterType )?.toInt() ?: 0 ) diff --git a/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/utils/DockerHostUtils.kt b/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/utils/DockerHostUtils.kt index fb0c1f5196c..1c2e0db51f7 100644 --- a/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/utils/DockerHostUtils.kt +++ b/src/backend/ci/core/dispatch/biz-dispatch-docker/src/main/kotlin/com/tencent/devops/dispatch/docker/utils/DockerHostUtils.kt @@ -85,8 +85,6 @@ class DockerHostUtils @Autowired constructor( unAvailableIpList: Set = setOf(), clusterName: DockerHostClusterType = DockerHostClusterType.COMMON ): Pair { - val grayEnv = bkTag.getFinalTag().contains("gray") - // 获取负载配置 val dockerHostLoadConfigTriple = getLoadConfig() logger.debug("Docker host load config: ${JsonUtil.toJson(dockerHostLoadConfigTriple)}") @@ -94,7 +92,6 @@ class DockerHostUtils @Autowired constructor( // 先取容量负载比较小的,同时满足负载条件的(负载阈值具体由OP平台配置),从满足的节点中随机选择一个 val firstPair = dockerLoadCheck( dockerHostLoadConfig = dockerHostLoadConfigTriple.first, - grayEnv = grayEnv, clusterName = clusterName, specialIpSet = specialIpSet, unAvailableIpList = unAvailableIpList @@ -102,7 +99,6 @@ class DockerHostUtils @Autowired constructor( val dockerPair = if (firstPair.first.isEmpty()) { val secondPair = dockerLoadCheck( dockerHostLoadConfig = dockerHostLoadConfigTriple.second, - grayEnv = grayEnv, clusterName = clusterName, specialIpSet = specialIpSet, unAvailableIpList = unAvailableIpList @@ -110,7 +106,6 @@ class DockerHostUtils @Autowired constructor( if (secondPair.first.isEmpty()) { dockerLoadCheck( dockerHostLoadConfig = dockerHostLoadConfigTriple.third, - grayEnv = grayEnv, clusterName = clusterName, specialIpSet = specialIpSet, unAvailableIpList = unAvailableIpList, @@ -408,7 +403,6 @@ class DockerHostUtils @Autowired constructor( private fun dockerLoadCheck( dockerHostLoadConfig: DockerHostLoadConfig, - grayEnv: Boolean, clusterName: DockerHostClusterType, specialIpSet: Set, unAvailableIpList: Set, @@ -417,7 +411,6 @@ class DockerHostUtils @Autowired constructor( val dockerIpList = pipelineDockerIpInfoDao.getAvailableDockerIpList( dslContext = dslContext, - grayEnv = grayEnv, clusterName = clusterName, cpuLoad = dockerHostLoadConfig.cpuLoadThreshold, memLoad = dockerHostLoadConfig.memLoadThreshold, @@ -428,7 +421,7 @@ class DockerHostUtils @Autowired constructor( ) return if (dockerIpList.isNotEmpty && - sufficientResources(finalCheck, dockerIpList.size, grayEnv, clusterName)) { + sufficientResources(finalCheck, dockerIpList.size, clusterName)) { selectAvailableDockerIp(dockerIpList, unAvailableIpList) } else { Pair("", 0) @@ -438,10 +431,9 @@ class DockerHostUtils @Autowired constructor( private fun sufficientResources( finalCheck: Boolean, fittingIpCount: Int, - grayEnv: Boolean, clusterName: DockerHostClusterType ): Boolean { - val enableIpCount = pipelineDockerIpInfoDao.getEnableDockerIpCount(dslContext, grayEnv, clusterName) + val enableIpCount = pipelineDockerIpInfoDao.getEnableDockerIpCount(dslContext, clusterName) // 最后一次check无论还剩几个可用ip,都要顶上,或者集群规模小于10不做判断 if (enableIpCount < 10 || finalCheck) { return true diff --git a/src/backend/ci/core/dispatch/biz-dispatch/src/main/kotlin/com/tencent/devops/dispatch/service/ThirdPartyAgentService.kt b/src/backend/ci/core/dispatch/biz-dispatch/src/main/kotlin/com/tencent/devops/dispatch/service/ThirdPartyAgentService.kt index 039bdb0595c..68729c1547a 100644 --- a/src/backend/ci/core/dispatch/biz-dispatch/src/main/kotlin/com/tencent/devops/dispatch/service/ThirdPartyAgentService.kt +++ b/src/backend/ci/core/dispatch/biz-dispatch/src/main/kotlin/com/tencent/devops/dispatch/service/ThirdPartyAgentService.kt @@ -44,7 +44,12 @@ import com.tencent.devops.common.api.util.timestampmilli import com.tencent.devops.common.auth.api.AuthResourceType import com.tencent.devops.common.client.Client import com.tencent.devops.common.client.ClientTokenService +import com.tencent.devops.common.event.dispatcher.SampleEventDispatcher +import com.tencent.devops.common.event.enums.ActionType +import com.tencent.devops.common.event.enums.PipelineBuildStatusBroadCastEventType +import com.tencent.devops.common.event.pojo.pipeline.PipelineBuildStatusBroadCastEvent import com.tencent.devops.common.notify.enums.NotifyType +import com.tencent.devops.common.pipeline.enums.BuildStatus import com.tencent.devops.common.pipeline.type.agent.ThirdPartyAgentDockerInfoDispatch import com.tencent.devops.common.redis.RedisOperation import com.tencent.devops.common.service.utils.HomeHostUtil @@ -69,12 +74,6 @@ import com.tencent.devops.model.dispatch.tables.records.TDispatchThirdpartyAgent import com.tencent.devops.notify.api.service.ServiceNotifyMessageTemplateResource import com.tencent.devops.notify.pojo.SendNotifyMessageTemplateRequest import com.tencent.devops.process.api.service.ServiceBuildResource -import org.jooq.DSLContext -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.beans.factory.annotation.Value -import org.springframework.dao.DeadlockLoserDataAccessException -import org.springframework.stereotype.Service import java.time.LocalDateTime import java.util.concurrent.CancellationException import java.util.concurrent.CompletableFuture @@ -83,6 +82,12 @@ import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.ThreadPoolExecutor import java.util.concurrent.TimeUnit import javax.ws.rs.NotFoundException +import org.jooq.DSLContext +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Value +import org.springframework.dao.DeadlockLoserDataAccessException +import org.springframework.stereotype.Service @Service @Suppress("ALL") @@ -94,7 +99,8 @@ class ThirdPartyAgentService @Autowired constructor( private val thirdPartyAgentBuildDao: ThirdPartyAgentBuildDao, private val thirdPartyAgentDockerService: ThirdPartyAgentDockerService, private val tokenService: ClientTokenService, - private val commonUtil: TPACommonUtil + private val commonUtil: TPACommonUtil, + private val pipelineEventDispatcher: SampleEventDispatcher ) { @Value("\${thirdagent.workerErrorTemplate:#{null}}") val workerErrorRtxTemplate: String? = null @@ -205,7 +211,7 @@ class ThirdPartyAgentService @Autowired constructor( if (agentResult.data!!.secretKey != secretKey) { logger.warn( "The secretKey($secretKey) is not match the expect one(${agentResult.data!!.secretKey} " + - "of project($projectId) and agent($agentId)" + "of project($projectId) and agent($agentId)" ) throw NotFoundException("Fail to get the agent") } @@ -238,9 +244,27 @@ class ThirdPartyAgentService @Autowired constructor( } catch (e: RemoteServiceException) { logger.warn( "notify agent task[$build.projectId|${build.buildId}|${build.vmSeqId}|$agentId]" + - " claim failed, cause: ${e.message} agent project($projectId)" + " claim failed, cause: ${e.message} agent project($projectId)" ) } + pipelineEventDispatcher.dispatch( + // 第三方构建机启动 + PipelineBuildStatusBroadCastEvent( + source = "third-party-agent-start-$agentId", projectId = build.projectId, + pipelineId = build.pipelineId, userId = "", + buildId = build.buildId, taskId = null, actionType = ActionType.START, + containerHashId = build.containerHashId, jobId = build.jobId, stageId = null, + stepId = null, atomCode = null, executeCount = build.executeCount, + buildStatus = BuildStatus.RUNNING.name, + type = PipelineBuildStatusBroadCastEventType.BUILD_AGENT_START, + labels = mapOf( + "agentId" to build.agentId, + "envHashId" to (build.envId?.let { HashUtil.encodeLongId(it) } ?: ""), + "nodeHashId" to (build.nodeId?.let { HashUtil.encodeLongId(it) } ?: ""), + "agentIp" to build.agentIp + ) + ) + ) // 第三方构建机docker启动获取镜像凭据 val dockerInfo = if (build.dockerInfo == null) { @@ -256,9 +280,9 @@ class ThirdPartyAgentService @Autowired constructor( // 只有凭据ID的参与计算 if (dockerInfo != null) { if (( - dockerInfo.credential?.user.isNullOrBlank() && - dockerInfo.credential?.password.isNullOrBlank() - ) && + dockerInfo.credential?.user.isNullOrBlank() && + dockerInfo.credential?.password.isNullOrBlank() + ) && !(dockerInfo.credential?.credentialId.isNullOrBlank()) ) { val (userName, password) = try { @@ -470,7 +494,7 @@ class ThirdPartyAgentService @Autowired constructor( private fun finishBuild(record: TDispatchThirdpartyAgentBuildRecord, success: Boolean) { logger.info( "Finish the third party agent(${record.agentId}) build(${record.buildId}) " + - "of seq(${record.vmSeqId}) and status(${record.status})" + "of seq(${record.vmSeqId}) and status(${record.status})" ) val agentResult = client.get(ServiceThirdPartyAgentResource::class) .getAgentByIdGlobal(record.projectId, record.agentId) @@ -514,9 +538,9 @@ class ThirdPartyAgentService @Autowired constructor( // 有些并发情况可能会导致在finish时AgentBuild状态没有被置为Done在这里改一下 val buildRecord = thirdPartyAgentBuildDao.get(dslContext, buildInfo.buildId, buildInfo.vmSeqId) if (buildRecord != null && ( - buildRecord.status != PipelineTaskStatus.DONE.status || - buildRecord.status != PipelineTaskStatus.FAILURE.status - ) + buildRecord.status != PipelineTaskStatus.DONE.status || + buildRecord.status != PipelineTaskStatus.FAILURE.status + ) ) { thirdPartyAgentBuildDao.updateStatus( dslContext = dslContext, @@ -572,9 +596,9 @@ class ThirdPartyAgentService @Autowired constructor( } // 构建需要使用构建的项目id跳转,防止是共享agent,agent链接使用上报的项目Id即可 val buildUrl = "${HomeHostUtil.innerServerHost()}/console/pipeline/${buildRecord.projectId}/" + - "${buildRecord.pipelineId}/detail/${buildRecord.buildId}/executeDetail" + "${buildRecord.pipelineId}/detail/${buildRecord.buildId}/executeDetail" val agentUrl = "${HomeHostUtil.innerServerHost()}/console/environment/$projectId/" + - "nodeDetail/${agentResult.data!!.nodeId}" + "nodeDetail/${agentResult.data!!.nodeId}" client.get(ServiceNotifyMessageTemplateResource::class).sendNotifyMessageByTemplate( SendNotifyMessageTemplateRequest( templateCode = workerErrorRtxTemplate!!, @@ -819,7 +843,7 @@ class ThirdPartyAgentService @Autowired constructor( "oldIp" to agent.ip, "newIp" to newIp, "url" to "${HomeHostUtil.innerServerHost()}/console/environment/$projectId/" + - "nodeDetail/$nodeHashId" + "nodeDetail/$nodeHashId" ) ) ) diff --git a/src/backend/ci/core/metrics/api-metrics/src/main/kotlin/com/tencent/devops/metrics/pojo/po/MetricsUserPO.kt b/src/backend/ci/core/metrics/api-metrics/src/main/kotlin/com/tencent/devops/metrics/pojo/po/MetricsUserPO.kt index 2d83cd143b3..4197fa98f8c 100644 --- a/src/backend/ci/core/metrics/api-metrics/src/main/kotlin/com/tencent/devops/metrics/pojo/po/MetricsUserPO.kt +++ b/src/backend/ci/core/metrics/api-metrics/src/main/kotlin/com/tencent/devops/metrics/pojo/po/MetricsUserPO.kt @@ -44,7 +44,8 @@ data class MetricsUserPO( val status: String, val atomCode: String?, val eventType: CallBackEvent, - var endTime: LocalDateTime? + var endTime: LocalDateTime?, + val labels: String? ) { constructor(event: PipelineBuildStatusBroadCastEvent) : this( startTime = event.eventTime ?: LocalDateTime.now(), @@ -56,7 +57,8 @@ data class MetricsUserPO( status = checkNotNull(event.buildStatus), atomCode = event.atomCode, eventType = checkNotNull(event.toEventType()), - endTime = null + endTime = null, + labels = event.labels?.entries?.joinToString(separator = ";") { "${it.key}=${it.value}" } ) companion object { @@ -64,7 +66,7 @@ data class MetricsUserPO( fun load(str: String?): MetricsUserPO? { if (str.isNullOrBlank()) return null val list = str.split(DELIMITER) - if (list.size != 10) return null + if (list.size < 10) return null return MetricsUserPO( LocalDateTime.ofInstant(Instant.ofEpochSecond(list[0].toLong()), ZoneOffset.ofHours(8)), list[1], @@ -77,7 +79,8 @@ data class MetricsUserPO( CallBackEvent.valueOf(list[8]), list[9].ifEmpty { null }?.let { LocalDateTime.ofInstant(Instant.ofEpochSecond(it.toLong()), ZoneOffset.ofHours(8)) - } + }, + list.getOrNull(10)?.ifEmpty { null } ) } } @@ -92,6 +95,7 @@ data class MetricsUserPO( status + DELIMITER + (atomCode ?: "") + DELIMITER + eventType.name + DELIMITER + - (endTime?.toInstant(ZoneOffset.ofHours(8))?.epochSecond?.toString() ?: "") + (endTime?.toInstant(ZoneOffset.ofHours(8))?.epochSecond?.toString() ?: "") + DELIMITER + + (labels ?: "") } } diff --git a/src/backend/ci/core/metrics/biz-metrics/src/main/kotlin/com/tencent/devops/metrics/config/MetricsUserConfig.kt b/src/backend/ci/core/metrics/biz-metrics/src/main/kotlin/com/tencent/devops/metrics/config/MetricsUserConfig.kt index 99253eb5516..9f88593dad1 100644 --- a/src/backend/ci/core/metrics/biz-metrics/src/main/kotlin/com/tencent/devops/metrics/config/MetricsUserConfig.kt +++ b/src/backend/ci/core/metrics/biz-metrics/src/main/kotlin/com/tencent/devops/metrics/config/MetricsUserConfig.kt @@ -45,9 +45,12 @@ import org.springframework.context.annotation.Configuration class MetricsUserConfig { companion object { + const val gaugeBuildQueueKey = "pipeline_queue_time_seconds" const val gaugeBuildKey = "pipeline_running_time_seconds" const val gaugeBuildStatusKey = "pipeline_status_info" + const val gaugeBuildJobQueueKey = "pipeline_job_queue_time_seconds" const val gaugeBuildJobKey = "pipeline_job_running_time_seconds" + const val gaugeBuildAgentKey = "pipeline_agent_running_time_seconds" const val gaugeBuildStepKey = "pipeline_step_running_time_seconds" const val gaugeBuildStepStatusKey = "pipeline_step_status_info" } @@ -85,9 +88,12 @@ class MetricsUserConfig { fun scrape(): String { return meterRegistry.scrape( TextFormat.CONTENT_TYPE_004, setOf( + gaugeBuildQueueKey, gaugeBuildKey, gaugeBuildStatusKey, + gaugeBuildJobQueueKey, gaugeBuildJobKey, + gaugeBuildAgentKey, gaugeBuildStepKey, gaugeBuildStepStatusKey ) diff --git a/src/backend/ci/core/metrics/biz-metrics/src/main/kotlin/com/tencent/devops/metrics/service/builds/MetricsCacheService.kt b/src/backend/ci/core/metrics/biz-metrics/src/main/kotlin/com/tencent/devops/metrics/service/builds/MetricsCacheService.kt index 06ce9fc887a..9c0c1f5ab2b 100644 --- a/src/backend/ci/core/metrics/biz-metrics/src/main/kotlin/com/tencent/devops/metrics/service/builds/MetricsCacheService.kt +++ b/src/backend/ci/core/metrics/biz-metrics/src/main/kotlin/com/tencent/devops/metrics/service/builds/MetricsCacheService.kt @@ -113,17 +113,30 @@ class MetricsCacheService @Autowired constructor( redisHashOperation.hdelete(metricsHeartBeatService.podKey(metricsHeartBeatService.getPodName()), key) } - fun buildCacheStart( + fun buildQueue( buildId: String, executeCount: Int, data: MetricsUserPO ): String { + val key = hash("$buildId-$executeCount-queue") + cacheStart(key, data) + return key + } + + fun buildStart( + buildId: String, + executeCount: Int, + data: MetricsUserPO + ): String { + val queueKey = hash("$buildId-$executeCount-queue") + val queueData = data.copy(endTime = data.startTime) + cacheEnd(queueKey, queueData) val key = hash("$buildId-$executeCount") cacheStart(key, data) return key } - fun buildCacheEnd( + fun buildEnd( buildId: String, executeCount: Int, data: MetricsUserPO @@ -133,18 +146,32 @@ class MetricsCacheService @Autowired constructor( return key } - fun jobCacheStart( + fun jobQueue( + buildId: String, + jobId: String, + executeCount: Int, + data: MetricsUserPO + ): String { + val key = hash("$buildId-$jobId-$executeCount-queue") + cacheStart(key, data) + return key + } + + fun jobStart( buildId: String, jobId: String, executeCount: Int, data: MetricsUserPO ): String { + val queueKey = hash("$buildId-$jobId-$executeCount-queue") + val queueData = data.copy(endTime = data.startTime) + cacheEnd(queueKey, queueData) val key = hash("$buildId-$jobId-$executeCount") cacheStart(key, data) return key } - fun jobCacheEnd( + fun jobEnd( buildId: String, jobId: String, executeCount: Int, @@ -155,6 +182,28 @@ class MetricsCacheService @Autowired constructor( return key } + fun agentStart( + buildId: String, + jobId: String, + executeCount: Int, + data: MetricsUserPO + ): String { + val key = hash("$buildId-$jobId-$executeCount-agent") + cacheStart(key, data) + return key + } + + fun agentEnd( + buildId: String, + jobId: String, + executeCount: Int, + data: MetricsUserPO + ): String { + val key = hash("$buildId-$jobId-$executeCount-agent") + cacheEnd(key, data) + return key + } + fun stepCacheStart( buildId: String, stepId: String, @@ -207,11 +256,11 @@ class MetricsCacheService @Autowired constructor( val cacheKey = cache[key] as MetricsUserPO? if (cacheKey != null) { cache[key] = data.apply { startTime = cacheKey.startTime } - redisHashOperation.hset( - key = metricsHeartBeatService.podKey(metricsHeartBeatService.getPodName()), - hashKey = key, - values = data.toString() - ) +// redisHashOperation.hset( +// key = metricsHeartBeatService.podKey(metricsHeartBeatService.getPodName()), +// hashKey = key, +// values = data.toString() +// ) } else { redisHashOperation.hset(metricsHeartBeatService.updateKey(), key, data.toString()) } @@ -302,6 +351,7 @@ class MetricsCacheService @Autowired constructor( * * @return 无 */ + /*TODO 失效检查可以区分build job step 多级细粒度*/ private fun executeCheck() { /* 运行超过一个小时的,加入检查队列 */ val limit = LocalDateTime.now().plusHours(-1) diff --git a/src/backend/ci/core/metrics/biz-metrics/src/main/kotlin/com/tencent/devops/metrics/service/builds/MetricsUserService.kt b/src/backend/ci/core/metrics/biz-metrics/src/main/kotlin/com/tencent/devops/metrics/service/builds/MetricsUserService.kt index 1ffba587c06..c9556c1d94e 100644 --- a/src/backend/ci/core/metrics/biz-metrics/src/main/kotlin/com/tencent/devops/metrics/service/builds/MetricsUserService.kt +++ b/src/backend/ci/core/metrics/biz-metrics/src/main/kotlin/com/tencent/devops/metrics/service/builds/MetricsUserService.kt @@ -41,9 +41,11 @@ import com.tencent.devops.process.api.service.ServiceBuildResource import com.tencent.devops.project.api.service.ServiceProjectResource import io.micrometer.core.instrument.Gauge import io.micrometer.core.instrument.Meter +import io.micrometer.core.instrument.Tag import io.micrometer.prometheus.PrometheusMeterRegistry import java.time.Duration import java.time.LocalDateTime +import java.util.Collections import java.util.LinkedList import java.util.concurrent.ConcurrentMap import java.util.concurrent.TimeUnit @@ -68,9 +70,9 @@ class MetricsUserService @Autowired constructor( .concurrencyLevel(10) .makeMap() - /* 延迟删除队列 */ + /* 延迟删除队列 需要线程安全*/ val delayArray: LinkedList>> = - LinkedList(MutableList(DELAY_LIMIT) { mutableListOf() }) + LinkedList(MutableList(DELAY_LIMIT) { Collections.synchronizedList(LinkedList()) }) /* 疑似构建状态未同步队列,以buildId为单位 */ val uncheckArray: MutableSet = mutableSetOf() @@ -104,7 +106,10 @@ class MetricsUserService @Autowired constructor( */ @Scheduled(cron = "0 0/10 * * * ?") fun checkBuildStatusJob() { - logger.info("=========>> check build status job start|${local.size}|${uncheckArray.size}<<=========") + logger.info( + "=========>> check build status job start|${local.size}|" + + "${uncheckArray.size}|${registry.meters.size}<<=========" + ) // 生成快照 val unchecks = uncheckArray.toList() val ready2delete = mutableListOf() @@ -165,7 +170,7 @@ class MetricsUserService @Autowired constructor( * @return 无 */ private fun execute() { - delayArray.addFirst(mutableListOf()) + delayArray.addFirst(Collections.synchronizedList(LinkedList())) val ready = delayArray.removeLast() logger.info("DeleteDelayProcess|ready to delete|${ready.size}") ready.forEachIndexed { index, data -> @@ -200,9 +205,28 @@ class MetricsUserService @Autowired constructor( /*防止mq队列堆积导致的延迟信息进入处理,如果生产超过5分钟就丢弃*/ if (date.startTime < LocalDateTime.now().plusMinutes(-5)) return when (date.eventType) { + CallBackEvent.BUILD_QUEUE -> { + date.startTime = checkNotNull(event.eventTime) + metricsCacheService.buildQueue(event.buildId, checkNotNull(event.executeCount), date) + } + CallBackEvent.BUILD_START -> { date.startTime = checkNotNull(event.eventTime) - metricsCacheService.buildCacheStart(event.buildId, checkNotNull(event.executeCount), date) + metricsCacheService.buildStart(event.buildId, checkNotNull(event.executeCount), date) + } + + CallBackEvent.BUILD_JOB_QUEUE -> { + if (event.jobId.isNullOrBlank()) { + // job id 用户没填写将不会上报指标 + return + } + date.startTime = checkNotNull(event.eventTime) + metricsCacheService.jobQueue( + event.buildId, + checkNotNull(event.jobId), + checkNotNull(event.executeCount), + date + ) } CallBackEvent.BUILD_JOB_START -> { @@ -211,7 +235,21 @@ class MetricsUserService @Autowired constructor( return } date.startTime = checkNotNull(event.eventTime) - metricsCacheService.jobCacheStart( + metricsCacheService.jobStart( + event.buildId, + checkNotNull(event.jobId), + checkNotNull(event.executeCount), + date + ) + } + + CallBackEvent.BUILD_AGENT_START -> { + if (event.jobId.isNullOrBlank()) { + // job id 用户没填写将不会上报指标 + return + } + date.startTime = checkNotNull(event.eventTime) + metricsCacheService.agentStart( event.buildId, checkNotNull(event.jobId), checkNotNull(event.executeCount), @@ -235,7 +273,7 @@ class MetricsUserService @Autowired constructor( CallBackEvent.BUILD_END -> { date.endTime = checkNotNull(event.eventTime) - metricsCacheService.buildCacheEnd(event.buildId, checkNotNull(event.executeCount), date) + metricsCacheService.buildEnd(event.buildId, checkNotNull(event.executeCount), date) } CallBackEvent.BUILD_JOB_END -> { @@ -248,7 +286,13 @@ class MetricsUserService @Autowired constructor( return } date.endTime = checkNotNull(event.eventTime) - metricsCacheService.jobCacheEnd( + metricsCacheService.jobEnd( + event.buildId, + checkNotNull(event.jobId), + checkNotNull(event.executeCount), + date + ) + metricsCacheService.agentEnd( event.buildId, checkNotNull(event.jobId), checkNotNull(event.executeCount), @@ -280,13 +324,35 @@ class MetricsUserService @Autowired constructor( logger.debug("metricsAdd|key={}|value={}|localSize={}", key, value, local.size) with(value) { when (eventType) { + CallBackEvent.BUILD_QUEUE -> { + val buildGauge = registerBuildQueueGauge( + key = key, + projectId = projectId, + pipelineId = pipelineId, + buildId = buildId, + description = "build queue metrics for $buildId", + labels = labels + ) + local[key]?.meters?.add(buildGauge) + val buildStatusGauge = registerBuildStatusGauge( + projectId = projectId, + pipelineId = pipelineId, + buildId = buildId, + status = status, + description = "build status metrics for $buildId", + labels = labels + ) + local[key]?.meters?.add(buildStatusGauge) + } + CallBackEvent.BUILD_START -> { val buildGauge = registerBuildGauge( key = key, projectId = projectId, pipelineId = pipelineId, buildId = buildId, - description = "build metrics for $buildId" + description = "build metrics for $buildId", + labels = labels ) local[key]?.meters?.add(buildGauge) val buildStatusGauge = registerBuildStatusGauge( @@ -294,11 +360,25 @@ class MetricsUserService @Autowired constructor( pipelineId = pipelineId, buildId = buildId, status = status, - description = "build status metrics for $buildId" + description = "build status metrics for $buildId", + labels = labels ) local[key]?.meters?.add(buildStatusGauge) } + CallBackEvent.BUILD_JOB_QUEUE -> { + val buildJobGauge = registerBuildJobQueueGauge( + key = key, + projectId = projectId, + pipelineId = pipelineId, + buildId = buildId, + jobId = checkNotNull(jobId), + description = "job queue metrics for $buildId|$jobId", + labels = labels + ) + local[key]?.meters?.add(buildJobGauge) + } + CallBackEvent.BUILD_JOB_START -> { val buildJobGauge = registerBuildJobGauge( key = key, @@ -306,7 +386,21 @@ class MetricsUserService @Autowired constructor( pipelineId = pipelineId, buildId = buildId, jobId = checkNotNull(jobId), - description = "job metrics for $buildId|$jobId" + description = "job metrics for $buildId|$jobId", + labels = labels + ) + local[key]?.meters?.add(buildJobGauge) + } + + CallBackEvent.BUILD_AGENT_START -> { + val buildJobGauge = registerBuildAgentGauge( + key = key, + projectId = projectId, + pipelineId = pipelineId, + buildId = buildId, + jobId = checkNotNull(jobId), + description = "agent metrics for $buildId|$jobId", + labels = labels ) local[key]?.meters?.add(buildJobGauge) } @@ -320,7 +414,8 @@ class MetricsUserService @Autowired constructor( jobId = checkNotNull(jobId), stepId = checkNotNull(stepId), atomCode = checkNotNull(atomCode), - description = "step metrics for $buildId|$stepId" + description = "step metrics for $buildId|$stepId", + labels = labels ) local[key]?.meters?.add(buildStepGauge) val buildStepStatusGauge = registerBuildStepStatusGauge( @@ -330,7 +425,8 @@ class MetricsUserService @Autowired constructor( jobId = jobId!!, stepId = stepId!!, status = status, - description = "step status metrics for $buildId|$stepId" + description = "step status metrics for $buildId|$stepId", + labels = labels ) local[key]?.meters?.add(buildStepStatusGauge) } @@ -362,6 +458,16 @@ class MetricsUserService @Autowired constructor( metrics.data = newValue with(newValue) { when (eventType) { + CallBackEvent.BUILD_START -> { + /*去掉构建排队指标*/ + metrics.meters.find { it.id.name == MetricsUserConfig.gaugeBuildQueueKey }?.run { + metricsCacheService.removeCache(key) + } + metrics.meters.find { it.id.name == MetricsUserConfig.gaugeBuildStatusKey }?.run { + metricsCacheService.removeCache(key) + } + } + CallBackEvent.BUILD_END -> { metrics.meters.find { it.id.name == MetricsUserConfig.gaugeBuildStatusKey }?.run { registry.remove(this) @@ -372,12 +478,20 @@ class MetricsUserService @Autowired constructor( pipelineId = pipelineId, buildId = buildId, status = status, - description = "build status metrics for $buildId" + description = "build status metrics for $buildId", + labels = labels ) ) metricsCacheService.removeCache(key) } + CallBackEvent.BUILD_JOB_START -> { + /*去掉job排队指标*/ + metrics.meters.find { it.id.name == MetricsUserConfig.gaugeBuildJobQueueKey }?.run { + metricsCacheService.removeCache(key) + } + } + CallBackEvent.BUILD_JOB_END -> { metricsCacheService.removeCache(key) } @@ -394,7 +508,8 @@ class MetricsUserService @Autowired constructor( jobId = jobId!!, stepId = stepId!!, status = status, - description = "step status metrics for $buildId|$stepId" + description = "step status metrics for $buildId|$stepId", + labels = labels ) ) metricsCacheService.removeCache(key) @@ -406,12 +521,43 @@ class MetricsUserService @Autowired constructor( } } + private fun deserializeTag(labels: String?): List { + return labels?.split(";") + ?.mapNotNull { + val parts = it.split("=") + if (parts.size == 2) Tag.of(parts[0], parts[1]) else null + } ?: emptyList() + } + + private fun registerBuildQueueGauge( + key: String, + projectId: String, + pipelineId: String, + buildId: String, + description: String, + labels: String? + ): Meter { + return Gauge.builder( + MetricsUserConfig.gaugeBuildQueueKey, + local + ) { cache -> cache[key]?.let { computeStartTime(it) } ?: 0.0 } + .tags( + "projectId", projectId, + "pipeline_id", pipelineId, + "build_id", buildId + ) + .tags(deserializeTag(labels)) + .description(description) + .register(registry) + } + private fun registerBuildGauge( key: String, projectId: String, pipelineId: String, buildId: String, - description: String + description: String, + labels: String? ): Meter { return Gauge.builder( MetricsUserConfig.gaugeBuildKey, @@ -422,6 +568,7 @@ class MetricsUserService @Autowired constructor( "pipeline_id", pipelineId, "build_id", buildId ) + .tags(deserializeTag(labels)) .description(description) .register(registry) } @@ -431,7 +578,8 @@ class MetricsUserService @Autowired constructor( pipelineId: String, buildId: String, status: String, - description: String + description: String, + labels: String? ): Meter { return Gauge.builder( MetricsUserConfig.gaugeBuildStatusKey @@ -442,6 +590,31 @@ class MetricsUserService @Autowired constructor( "build_id", buildId, "status", status ) + .tags(deserializeTag(labels)) + .description(description) + .register(registry) + } + + private fun registerBuildJobQueueGauge( + key: String, + projectId: String, + pipelineId: String, + buildId: String, + jobId: String, + description: String, + labels: String? + ): Meter { + return Gauge.builder( + MetricsUserConfig.gaugeBuildJobQueueKey, + local + ) { cache -> cache[key]?.let { computeStartTime(it) } ?: 0.0 } + .tags( + "projectId", projectId, + "pipeline_id", pipelineId, + "build_id", buildId, + "job_id", jobId + ) + .tags(deserializeTag(labels)) .description(description) .register(registry) } @@ -452,7 +625,8 @@ class MetricsUserService @Autowired constructor( pipelineId: String, buildId: String, jobId: String, - description: String + description: String, + labels: String? ): Meter { return Gauge.builder( MetricsUserConfig.gaugeBuildJobKey, @@ -464,6 +638,31 @@ class MetricsUserService @Autowired constructor( "build_id", buildId, "job_id", jobId ) + .tags(deserializeTag(labels)) + .description(description) + .register(registry) + } + + private fun registerBuildAgentGauge( + key: String, + projectId: String, + pipelineId: String, + buildId: String, + jobId: String, + description: String, + labels: String? + ): Meter { + return Gauge.builder( + MetricsUserConfig.gaugeBuildAgentKey, + local + ) { cache -> cache[key]?.let { computeStartTime(it) } ?: 0.0 } + .tags( + "projectId", projectId, + "pipeline_id", pipelineId, + "build_id", buildId, + "job_id", jobId + ) + .tags(deserializeTag(labels)) .description(description) .register(registry) } @@ -476,7 +675,8 @@ class MetricsUserService @Autowired constructor( jobId: String, stepId: String, atomCode: String, - description: String + description: String, + labels: String? ): Meter { return Gauge.builder( MetricsUserConfig.gaugeBuildStepKey, @@ -490,6 +690,7 @@ class MetricsUserService @Autowired constructor( "step_id", stepId, "plugin_id", atomCode ) + .tags(deserializeTag(labels)) .description(description) .register(registry) } @@ -501,7 +702,8 @@ class MetricsUserService @Autowired constructor( jobId: String, stepId: String, status: String, - description: String + description: String, + labels: String? ): Meter { return Gauge.builder( MetricsUserConfig.gaugeBuildStepStatusKey @@ -514,6 +716,7 @@ class MetricsUserService @Autowired constructor( "step_id", stepId, "status", status ) + .tags(deserializeTag(labels)) .description(description) .register(registry) } diff --git a/src/backend/ci/core/openapi/api-openapi/src/main/kotlin/com/tencent/devops/openapi/api/apigw/v3/ApigwAuthGrantResourceV3.kt b/src/backend/ci/core/openapi/api-openapi/src/main/kotlin/com/tencent/devops/openapi/api/apigw/v3/ApigwAuthGrantResourceV3.kt deleted file mode 100644 index f4ed65c07ec..00000000000 --- a/src/backend/ci/core/openapi/api-openapi/src/main/kotlin/com/tencent/devops/openapi/api/apigw/v3/ApigwAuthGrantResourceV3.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.tencent.devops.openapi.api.apigw.v3 - -import com.tencent.devops.auth.pojo.dto.GrantInstanceDTO -import com.tencent.devops.common.api.auth.AUTH_HEADER_DEVOPS_APP_CODE -import com.tencent.devops.common.api.auth.AUTH_HEADER_USER_ID -import com.tencent.devops.common.api.auth.AUTH_HEADER_USER_ID_DEFAULT_VALUE -import com.tencent.devops.common.api.pojo.Result -import io.swagger.v3.oas.annotations.tags.Tag -import io.swagger.v3.oas.annotations.Operation -import io.swagger.v3.oas.annotations.Parameter -import javax.ws.rs.Consumes -import javax.ws.rs.HeaderParam -import javax.ws.rs.POST -import javax.ws.rs.Path -import javax.ws.rs.PathParam -import javax.ws.rs.Produces -import javax.ws.rs.core.MediaType - -@Tag(name = "OPENAPI_AUTH_V3", description = "OPENAPI-权限相关") -@Path("/{apigwType:apigw-user|apigw-app|apigw}/v3/auth") -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -@Suppress("ALL") -interface ApigwAuthGrantResourceV3 { - - @Operation(summary = "实例授权", tags = ["v3_app_permission_grant"]) - @POST - @Path("/projects/{projectId}/instance/grant") - fun grantInstancePermission( - @Parameter(description = "appCode", required = true) - @HeaderParam(AUTH_HEADER_DEVOPS_APP_CODE) - appCode: String?, - @Parameter(description = "apigw Type", required = true) - @PathParam("apigwType") - apigwType: String?, - @Parameter(description = "用户ID", required = true, example = AUTH_HEADER_USER_ID_DEFAULT_VALUE) - @HeaderParam(AUTH_HEADER_USER_ID) - userId: String, - @Parameter(description = "项目ID(项目英文名)", required = true) - @PathParam("projectId") - projectId: String, - grantInstance: GrantInstanceDTO - ): Result -} diff --git a/src/backend/ci/core/openapi/api-openapi/src/main/kotlin/com/tencent/devops/openapi/api/apigw/v4/ApigwAuthGrantResourceV4.kt b/src/backend/ci/core/openapi/api-openapi/src/main/kotlin/com/tencent/devops/openapi/api/apigw/v4/ApigwAuthGrantResourceV4.kt deleted file mode 100644 index d62c88a4b8d..00000000000 --- a/src/backend/ci/core/openapi/api-openapi/src/main/kotlin/com/tencent/devops/openapi/api/apigw/v4/ApigwAuthGrantResourceV4.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.tencent.devops.openapi.api.apigw.v4 - -import com.tencent.devops.auth.pojo.dto.GrantInstanceDTO -import com.tencent.devops.common.api.auth.AUTH_HEADER_DEVOPS_APP_CODE -import com.tencent.devops.common.api.auth.AUTH_HEADER_USER_ID -import com.tencent.devops.common.api.auth.AUTH_HEADER_USER_ID_DEFAULT_VALUE -import com.tencent.devops.common.api.pojo.Result -import io.swagger.v3.oas.annotations.tags.Tag -import io.swagger.v3.oas.annotations.Operation -import io.swagger.v3.oas.annotations.Parameter -import javax.ws.rs.Consumes -import javax.ws.rs.HeaderParam -import javax.ws.rs.POST -import javax.ws.rs.Path -import javax.ws.rs.PathParam -import javax.ws.rs.Produces -import javax.ws.rs.core.MediaType - -@Tag(name = "OPENAPI_AUTH_V4", description = "OPENAPI-权限相关") -@Path("/{apigwType:apigw-user|apigw-app|apigw}/v4/auth/projects/{projectId}") -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -@Suppress("ALL") -interface ApigwAuthGrantResourceV4 { - - @Operation(summary = "实例授权", tags = ["v4_app_permission_grant"]) - @POST - @Path("/instance_grant") - fun grantInstancePermission( - @Parameter(description = "appCode", required = true) - @HeaderParam(AUTH_HEADER_DEVOPS_APP_CODE) - appCode: String?, - @Parameter(description = "apigw Type", required = true) - @PathParam("apigwType") - apigwType: String?, - @Parameter(description = "用户ID", required = true, example = AUTH_HEADER_USER_ID_DEFAULT_VALUE) - @HeaderParam(AUTH_HEADER_USER_ID) - userId: String, - @Parameter(description = "项目ID(项目英文名)", required = true) - @PathParam("projectId") - projectId: String, - grantInstance: GrantInstanceDTO - ): Result -} diff --git a/src/backend/ci/core/openapi/api-openapi/src/main/kotlin/com/tencent/devops/openapi/api/apigw/v4/ApigwPipelineVersionResourceV4.kt b/src/backend/ci/core/openapi/api-openapi/src/main/kotlin/com/tencent/devops/openapi/api/apigw/v4/ApigwPipelineVersionResourceV4.kt new file mode 100644 index 00000000000..b7a7c6618a0 --- /dev/null +++ b/src/backend/ci/core/openapi/api-openapi/src/main/kotlin/com/tencent/devops/openapi/api/apigw/v4/ApigwPipelineVersionResourceV4.kt @@ -0,0 +1,450 @@ +/* + * 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.devops.openapi.api.apigw.v4 + +import com.tencent.devops.common.api.auth.AUTH_HEADER_DEVOPS_APP_CODE +import com.tencent.devops.common.api.auth.AUTH_HEADER_DEVOPS_APP_CODE_DEFAULT_VALUE +import com.tencent.devops.common.api.auth.AUTH_HEADER_DEVOPS_USER_ID +import com.tencent.devops.common.api.auth.AUTH_HEADER_DEVOPS_USER_ID_DEFAULT_VALUE +import com.tencent.devops.common.api.pojo.Page +import com.tencent.devops.common.api.pojo.Result +import com.tencent.devops.common.pipeline.PipelineVersionWithModel +import com.tencent.devops.common.pipeline.PipelineVersionWithModelRequest +import com.tencent.devops.common.pipeline.pojo.BuildNoUpdateReq +import com.tencent.devops.process.pojo.pipeline.DeployPipelineResult +import com.tencent.devops.common.pipeline.pojo.TemplateInstanceCreateRequest +import com.tencent.devops.common.pipeline.pojo.transfer.PreviewResponse +import com.tencent.devops.process.engine.pojo.PipelineVersionWithInfo +import com.tencent.devops.process.pojo.PipelineDetail +import com.tencent.devops.process.pojo.PipelineOperationDetail +import com.tencent.devops.process.pojo.PipelineVersionReleaseRequest +import com.tencent.devops.process.pojo.pipeline.PrefetchReleaseResult +import com.tencent.devops.process.pojo.setting.PipelineVersionSimple +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter +import io.swagger.v3.oas.annotations.tags.Tag +import javax.validation.Valid +import javax.ws.rs.Consumes +import javax.ws.rs.GET +import javax.ws.rs.HeaderParam +import javax.ws.rs.POST +import javax.ws.rs.Path +import javax.ws.rs.PathParam +import javax.ws.rs.Produces +import javax.ws.rs.QueryParam +import javax.ws.rs.core.MediaType +import javax.ws.rs.core.Response + +@Tag(name = "OPENAPI_PIPELINE_VERSION_V4", description = "OPENAPI-流水线版本管理资源") +@Path("/{apigwType:apigw-user|apigw-app|apigw}/v4/projects/{projectId}/version") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Suppress("ALL") +interface ApigwPipelineVersionResourceV4 { + + @Operation(summary = "获取流水线信息(含草稿)", tags = ["v4_app_pipeline_detail", "v4_user_pipeline_detail"]) + @GET + @Path("/pipeline_detail") + fun getPipelineVersionDetail( + @Parameter(description = "appCode", required = true, example = AUTH_HEADER_DEVOPS_APP_CODE_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_APP_CODE) + appCode: String?, + @Parameter(description = "apigw Type", required = true) + @PathParam("apigwType") + apigwType: String?, + @Parameter(description = "用户ID", required = true, example = AUTH_HEADER_DEVOPS_USER_ID_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "流水线ID", required = true) + @QueryParam("pipelineId") + pipelineId: String + ): Result + + @Operation( + summary = "将当前草稿发布为正式版本", + tags = ["v4_app_pipeline_release_prefetch", "v4_user_pipeline_release_prefetch"] + ) + @GET + @Path("/release_prefetch") + fun preFetchDraftVersion( + @Parameter(description = "appCode", required = true, example = AUTH_HEADER_DEVOPS_APP_CODE_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_APP_CODE) + appCode: String?, + @Parameter(description = "apigw Type", required = true) + @PathParam("apigwType") + apigwType: String?, + @Parameter(description = "用户ID", required = true, example = AUTH_HEADER_DEVOPS_USER_ID_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "流水线ID", required = true) + @QueryParam("pipelineId") + pipelineId: String, + @Parameter(description = "流水线编排版本", required = true) + @QueryParam("version") + version: Int + ): Result + + @Operation(summary = "将当前模板发布为正式版本", tags = ["v4_app_pipeline_release", "v4_user_pipeline_release"]) + @POST + @Path("/release_version") + fun releaseDraftVersion( + @Parameter(description = "appCode", required = true, example = AUTH_HEADER_DEVOPS_APP_CODE_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_APP_CODE) + appCode: String?, + @Parameter(description = "apigw Type", required = true) + @PathParam("apigwType") + apigwType: String?, + @Parameter(description = "用户ID", required = true, example = AUTH_HEADER_DEVOPS_USER_ID_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "流水线ID", required = true) + @QueryParam("pipelineId") + pipelineId: String, + @Parameter(description = "流水线编排版本", required = true) + @QueryParam("version") + version: Int, + @Parameter(description = "发布流水线版本请求", required = true) + request: PipelineVersionReleaseRequest + ): Result + + @Operation( + summary = "通过指定模板创建流水线", + tags = ["v4_app_pipeline_create_with_template", "v4_user_pipeline_create_with_template"] + ) + @POST + @Path("/create_with_template") + fun createPipelineFromTemplate( + @Parameter(description = "appCode", required = true, example = AUTH_HEADER_DEVOPS_APP_CODE_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_APP_CODE) + appCode: String?, + @Parameter(description = "apigw Type", required = true) + @PathParam("apigwType") + apigwType: String?, + @Parameter(description = "用户ID", required = true, example = AUTH_HEADER_DEVOPS_USER_ID_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "流水线模型实例请求", required = true) + request: TemplateInstanceCreateRequest + ): Result + + @Operation( + summary = "获取流水线指定版本的两种编排", + tags = ["v4_app_pipeline_get_version", "v4_user_pipeline_get_version"] + ) + @GET + @Path("/get_version") + fun getVersion( + @Parameter(description = "appCode", required = true, example = AUTH_HEADER_DEVOPS_APP_CODE_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_APP_CODE) + appCode: String?, + @Parameter(description = "apigw Type", required = true) + @PathParam("apigwType") + apigwType: String?, + @Parameter(description = "用户ID", required = true, example = AUTH_HEADER_DEVOPS_USER_ID_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "流水线ID", required = true) + @QueryParam("pipelineId") + pipelineId: String, + @Parameter(description = "流水线编排版本", required = true) + @QueryParam("version") + version: Int + ): Result + + @Operation(summary = "触发前配置", tags = ["v4_app_pipeline_preview_code", "v4_user_pipeline_preview_code"]) + @GET + @Path("/preview_code") + fun previewCode( + @Parameter(description = "appCode", required = true, example = AUTH_HEADER_DEVOPS_APP_CODE_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_APP_CODE) + appCode: String?, + @Parameter(description = "apigw Type", required = true) + @PathParam("apigwType") + apigwType: String?, + @Parameter(description = "用户ID", required = true, example = AUTH_HEADER_DEVOPS_USER_ID_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "流水线id", required = true) + @QueryParam("pipelineId") + pipelineId: String, + @Parameter(description = "流水线版本号", required = false) + @QueryParam("version") + version: Int? + ): Result + + @Operation( + summary = "保存或创建流水线编排草稿", + tags = ["v4_app_pipeline_save_draft", "v4_user_pipeline_save_draft"] + ) + @POST + @Path("/save_draft") + fun savePipelineDraft( + @Parameter(description = "appCode", required = true, example = AUTH_HEADER_DEVOPS_APP_CODE_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_APP_CODE) + appCode: String?, + @Parameter(description = "apigw Type", required = true) + @PathParam("apigwType") + apigwType: String?, + @Parameter(description = "用户ID", required = true, example = AUTH_HEADER_DEVOPS_USER_ID_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "流水线模型与设置", required = true) + @Valid + modelAndYaml: PipelineVersionWithModelRequest + ): Result + + @Operation( + summary = "获取流水线编排创建人列表(分页)", + tags = ["v4_app_pipeline_creator_list", "v4_user_pipeline_creator_list"] + ) + @GET + @Path("/creator_list") + fun versionCreatorList( + @Parameter(description = "appCode", required = true, example = AUTH_HEADER_DEVOPS_APP_CODE_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_APP_CODE) + appCode: String?, + @Parameter(description = "apigw Type", required = true) + @PathParam("apigwType") + apigwType: String?, + @Parameter(description = "用户ID", required = true, example = AUTH_HEADER_DEVOPS_USER_ID_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "流水线ID", required = true) + @QueryParam("pipelineId") + pipelineId: String, + @Parameter(description = "第几页", required = false, example = "1") + @QueryParam("page") + page: Int?, + @Parameter(description = "每页多少条", required = false, example = "20") + @QueryParam("pageSize") + pageSize: Int? + ): Result> + + @Operation( + summary = "流水线编排版本列表(搜索、分页)", + tags = ["v4_app_pipeline_version_list", "v4_user_pipeline_version_list"] + ) + @GET + @Path("/version_list") + fun versionList( + @Parameter(description = "appCode", required = true, example = AUTH_HEADER_DEVOPS_APP_CODE_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_APP_CODE) + appCode: String?, + @Parameter(description = "apigw Type", required = true) + @PathParam("apigwType") + apigwType: String?, + @Parameter(description = "用户ID", required = true, example = AUTH_HEADER_DEVOPS_USER_ID_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "流水线ID", required = true) + @QueryParam("pipelineId") + pipelineId: String, + @Parameter(description = "跳转定位的版本号", required = false) + @QueryParam("fromVersion") + fromVersion: Int? = null, + @Parameter(description = "搜索字段:版本名包含字符", required = false) + @QueryParam("versionName") + versionName: String? = null, + @Parameter(description = "搜索字段:创建人", required = false) + @QueryParam("creator") + creator: String? = null, + @Parameter(description = "搜索字段:变更说明", required = false) + @QueryParam("description") + description: String? = null, + @Parameter(description = "第几页", required = false, example = "1") + @QueryParam("page") + page: Int?, + @Parameter(description = "每页多少条", required = false, example = "5") + @QueryParam("pageSize") + pageSize: Int? + ): Result> + + @Operation( + summary = "获取流水线操作日志列表(分页)", + tags = ["v4_app_pipeline_operation_log", "v4_user_pipeline_operation_log"] + ) + @GET + @Path("/operation_log") + fun getPipelineOperationLogs( + @Parameter(description = "appCode", required = true, example = AUTH_HEADER_DEVOPS_APP_CODE_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_APP_CODE) + appCode: String?, + @Parameter(description = "apigw Type", required = true) + @PathParam("apigwType") + apigwType: String?, + @Parameter(description = "用户ID", required = true, example = AUTH_HEADER_DEVOPS_USER_ID_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "流水线ID", required = true) + @QueryParam("pipelineId") + pipelineId: String, + @Parameter(description = "搜索字段:创建人", required = false) + @QueryParam("creator") + creator: String? = null, + @Parameter(description = "第几页", required = false, example = "1") + @QueryParam("page") + page: Int?, + @Parameter(description = "每页多少条", required = false, example = "20") + @QueryParam("pageSize") + pageSize: Int? + ): Result> + + @Operation( + summary = "获取流水线操作人列表(分页)", + tags = ["v4_app_pipeline_operator_list", "v4_user_pipeline_operator_list"] + ) + @GET + @Path("/operator_list") + fun operatorList( + @Parameter(description = "appCode", required = true, example = AUTH_HEADER_DEVOPS_APP_CODE_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_APP_CODE) + appCode: String?, + @Parameter(description = "apigw Type", required = true) + @PathParam("apigwType") + apigwType: String?, + @Parameter(description = "用户ID", required = true, example = AUTH_HEADER_DEVOPS_USER_ID_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "流水线ID", required = true) + @QueryParam("pipelineId") + pipelineId: String + ): Result> + + @Operation( + summary = "回滚到指定的历史版本并覆盖草稿", + tags = ["v4_app_pipeline_rollback_draft", "v4_user_pipeline_rollback_draft"] + ) + @POST + @Path("/rollback_draft") + fun rollbackDraftFromVersion( + @Parameter(description = "appCode", required = true, example = AUTH_HEADER_DEVOPS_APP_CODE_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_APP_CODE) + appCode: String?, + @Parameter(description = "apigw Type", required = true) + @PathParam("apigwType") + apigwType: String?, + @Parameter(description = "用户ID", required = true, example = AUTH_HEADER_DEVOPS_USER_ID_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "流水线ID", required = true) + @QueryParam("pipelineId") + pipelineId: String, + @Parameter(description = "回回滚目标版本", required = true) + @QueryParam("version") + version: Int + ): Result + + @Operation(summary = "导出流水线模板", tags = ["v4_app_pipeline_export", "v4_user_pipeline_export"]) + @GET + @Path("/export") + @Produces(MediaType.APPLICATION_OCTET_STREAM) + fun exportPipeline( + @Parameter(description = "appCode", required = true, example = AUTH_HEADER_DEVOPS_APP_CODE_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_APP_CODE) + appCode: String?, + @Parameter(description = "apigw Type", required = true) + @PathParam("apigwType") + apigwType: String?, + @Parameter(description = "用户ID", required = true, example = AUTH_HEADER_DEVOPS_USER_ID_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "流水线Id", required = true) + @QueryParam("pipelineId") + pipelineId: String, + @Parameter(description = "导出的目标版本", required = false) + @QueryParam("version") + version: Int?, + @Parameter(description = "导出的数据类型", required = false) + @QueryParam("storageType") + storageType: String? + ): Response + + @Operation( + summary = "重置流水线推荐版本号", + tags = ["v4_app_pipeline_update_build_no", "v4_user_pipeline_update_build_no"] + ) + @POST + @Path("/update_build_no") + fun updateBuildNo( + @Parameter(description = "appCode", required = true, example = AUTH_HEADER_DEVOPS_APP_CODE_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_APP_CODE) + appCode: String?, + @Parameter(description = "apigw Type", required = true) + @PathParam("apigwType") + apigwType: String?, + @Parameter(description = "用户ID", required = true, example = AUTH_HEADER_DEVOPS_USER_ID_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "流水线ID", required = true) + @QueryParam("pipelineId") + pipelineId: String, + @Parameter(description = "流水线构建推荐版本号更新", required = true) + buildNo: BuildNoUpdateReq + ): Result +} diff --git a/src/backend/ci/core/openapi/api-openapi/src/main/kotlin/com/tencent/devops/openapi/api/apigw/v4/ApigwProjectResourceV4.kt b/src/backend/ci/core/openapi/api-openapi/src/main/kotlin/com/tencent/devops/openapi/api/apigw/v4/ApigwProjectResourceV4.kt index c8a24cbd6ed..94f308c1501 100644 --- a/src/backend/ci/core/openapi/api-openapi/src/main/kotlin/com/tencent/devops/openapi/api/apigw/v4/ApigwProjectResourceV4.kt +++ b/src/backend/ci/core/openapi/api-openapi/src/main/kotlin/com/tencent/devops/openapi/api/apigw/v4/ApigwProjectResourceV4.kt @@ -36,6 +36,7 @@ import com.tencent.devops.project.pojo.ProjectCreateUserInfo import com.tencent.devops.project.pojo.ProjectUpdateInfo import com.tencent.devops.project.pojo.ProjectVO import com.tencent.devops.project.pojo.Result +import com.tencent.devops.project.pojo.enums.PluginDetailsDisplayOrder import com.tencent.devops.project.pojo.enums.ProjectValidateType import io.swagger.v3.oas.annotations.tags.Tag import io.swagger.v3.oas.annotations.Operation @@ -231,4 +232,27 @@ interface ApigwProjectResourceV4 { @QueryParam("productId") productId: Int ): Result> + + @PUT + @Path("/{projectId}/update_plugin_details_display") + @Operation( + summary = "更新插件展示顺序", + tags = ["v4_app_update_plugin_details_display"] + ) + fun updatePluginDetailsDisplay( + @Parameter(description = "appCode", required = true, example = AUTH_HEADER_DEVOPS_APP_CODE_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_DEVOPS_APP_CODE) + appCode: String?, + @Parameter(description = "apigw Type", required = true) + @PathParam("apigwType") + apigwType: String?, + @Parameter(description = "userId") + @HeaderParam(AUTH_HEADER_DEVOPS_USER_ID) + userId: String?, + @Parameter(description = "projectId", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "插件展示顺序", required = true) + pluginDetailsDisplayOrder: List + ): Result } diff --git a/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/resources/apigw/v3/ApigwAuthGrantResourceV3Impl.kt b/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/resources/apigw/v3/ApigwAuthGrantResourceV3Impl.kt deleted file mode 100644 index e7ef674387c..00000000000 --- a/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/resources/apigw/v3/ApigwAuthGrantResourceV3Impl.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.tencent.devops.openapi.resources.apigw.v3 - -import com.tencent.devops.auth.api.service.ServicePermissionAuthResource -import com.tencent.devops.auth.pojo.dto.GrantInstanceDTO -import com.tencent.devops.common.api.pojo.Result -import com.tencent.devops.common.client.Client -import com.tencent.devops.common.client.ClientTokenService -import com.tencent.devops.common.web.RestResource -import com.tencent.devops.openapi.api.apigw.v3.ApigwAuthGrantResourceV3 -import com.tencent.devops.openapi.service.OpenapiPermissionService -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Autowired - -@RestResource -class ApigwAuthGrantResourceV3Impl @Autowired constructor( - private val client: Client, - private val tokenService: ClientTokenService, - private val openapiPermissionService: OpenapiPermissionService -) : ApigwAuthGrantResourceV3 { - override fun grantInstancePermission( - appCode: String?, - apigwType: String?, - userId: String, - projectId: String, - grantInstance: GrantInstanceDTO - ): Result { - logger.info("OPENAPI_AUTH_GRANT_V3|$userId|grant instance permission|$projectId|$grantInstance") - openapiPermissionService.validProjectManagerPermission(appCode, apigwType, userId, projectId) - return Result( - client.get(ServicePermissionAuthResource::class).grantInstancePermission( - userId = userId, - projectCode = projectId, - grantInstance = grantInstance, - token = tokenService.getSystemToken()!! - ).data ?: false - ) - } - - companion object { - val logger = LoggerFactory.getLogger(ApigwAuthGrantResourceV3Impl::class.java) - } -} diff --git a/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/resources/apigw/v4/ApigwAuthGrantResourceV4Impl.kt b/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/resources/apigw/v4/ApigwAuthGrantResourceV4Impl.kt deleted file mode 100644 index 305d39a8b7a..00000000000 --- a/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/resources/apigw/v4/ApigwAuthGrantResourceV4Impl.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.tencent.devops.openapi.resources.apigw.v4 - -import com.tencent.devops.auth.api.service.ServicePermissionAuthResource -import com.tencent.devops.auth.pojo.dto.GrantInstanceDTO -import com.tencent.devops.common.api.pojo.Result -import com.tencent.devops.common.client.Client -import com.tencent.devops.common.client.ClientTokenService -import com.tencent.devops.common.web.RestResource -import com.tencent.devops.openapi.api.apigw.v4.ApigwAuthGrantResourceV4 -import com.tencent.devops.openapi.service.OpenapiPermissionService -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Autowired - -@RestResource -class ApigwAuthGrantResourceV4Impl @Autowired constructor( - private val client: Client, - private val tokenService: ClientTokenService, - private val openapiPermissionService: OpenapiPermissionService -) : ApigwAuthGrantResourceV4 { - override fun grantInstancePermission( - appCode: String?, - apigwType: String?, - userId: String, - projectId: String, - grantInstance: GrantInstanceDTO - ): Result { - logger.info("OPENAPI_AUTH_GRANT_V4|$userId|grant instance permission|$projectId|$grantInstance") - openapiPermissionService.validProjectManagerPermission(appCode, apigwType, userId, projectId) - return Result( - client.get(ServicePermissionAuthResource::class).grantInstancePermission( - userId = userId, - projectCode = projectId, - grantInstance = grantInstance, - token = tokenService.getSystemToken()!! - ).data ?: false - ) - } - - companion object { - val logger = LoggerFactory.getLogger(ApigwAuthGrantResourceV4Impl::class.java) - } -} diff --git a/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/resources/apigw/v4/ApigwPipelineVersionResourceV4Impl.kt b/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/resources/apigw/v4/ApigwPipelineVersionResourceV4Impl.kt new file mode 100644 index 00000000000..df2223c71dc --- /dev/null +++ b/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/resources/apigw/v4/ApigwPipelineVersionResourceV4Impl.kt @@ -0,0 +1,311 @@ +/* + * 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.devops.openapi.resources.apigw.v4 + +import com.tencent.devops.common.api.pojo.Page +import com.tencent.devops.common.api.pojo.Result +import com.tencent.devops.common.client.Client +import com.tencent.devops.common.pipeline.PipelineVersionWithModel +import com.tencent.devops.common.pipeline.PipelineVersionWithModelRequest +import com.tencent.devops.common.pipeline.pojo.BuildNoUpdateReq +import com.tencent.devops.common.web.RestResource +import com.tencent.devops.openapi.utils.ApiGatewayUtil +import com.tencent.devops.process.pojo.pipeline.DeployPipelineResult +import com.tencent.devops.common.pipeline.pojo.TemplateInstanceCreateRequest +import com.tencent.devops.common.pipeline.pojo.transfer.PreviewResponse +import com.tencent.devops.openapi.api.apigw.v4.ApigwPipelineVersionResourceV4 +import com.tencent.devops.process.api.service.ServicePipelineVersionResource +import com.tencent.devops.process.engine.pojo.PipelineVersionWithInfo +import com.tencent.devops.process.pojo.PipelineDetail +import com.tencent.devops.process.pojo.PipelineOperationDetail +import com.tencent.devops.process.pojo.PipelineVersionReleaseRequest +import com.tencent.devops.process.pojo.pipeline.PrefetchReleaseResult +import com.tencent.devops.process.pojo.setting.PipelineVersionSimple +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired +import javax.ws.rs.core.Response + +@RestResource +class ApigwPipelineVersionResourceV4Impl @Autowired constructor( + private val client: Client, + private val apiGatewayUtil: ApiGatewayUtil +) : ApigwPipelineVersionResourceV4 { + + companion object { + private val logger = LoggerFactory.getLogger(ApigwPipelineVersionResourceV4Impl::class.java) + } + + override fun getPipelineVersionDetail( + appCode: String?, + apigwType: String?, + userId: String, + projectId: String, + pipelineId: String + ): Result { + logger.info("OPENAPI_PIPELINE_V4|$userId|getPipelineVersionDetail|$projectId|$pipelineId") + return client.get(ServicePipelineVersionResource::class).getPipelineVersionDetail( + userId = userId, + projectId = projectId, + pipelineId = pipelineId + ) + } + + override fun preFetchDraftVersion( + appCode: String?, + apigwType: String?, + userId: String, + projectId: String, + pipelineId: String, + version: Int + ): Result { + logger.info("OPENAPI_PIPELINE_V4|$userId|preFetchDraftVersion|$projectId|$pipelineId") + return client.get(ServicePipelineVersionResource::class).preFetchDraftVersion( + userId = userId, + projectId = projectId, + pipelineId = pipelineId, + version = version + ) + } + + override fun releaseDraftVersion( + appCode: String?, + apigwType: String?, + userId: String, + projectId: String, + pipelineId: String, + version: Int, + request: PipelineVersionReleaseRequest + ): Result { + logger.info("OPENAPI_PIPELINE_V4|$userId|releaseDraftVersion|$projectId|$pipelineId") + return client.get(ServicePipelineVersionResource::class).releaseDraftVersion( + userId = userId, + projectId = projectId, + pipelineId = pipelineId, + version = version, + request = request + ) + } + + override fun createPipelineFromTemplate( + appCode: String?, + apigwType: String?, + userId: String, + projectId: String, + request: TemplateInstanceCreateRequest + ): Result { + logger.info("OPENAPI_PIPELINE_V4|$userId|createPipelineFromTemplate|$projectId|request=$request") + return client.get(ServicePipelineVersionResource::class).createPipelineFromTemplate( + userId = userId, + projectId = projectId, + request = request + ) + } + + override fun getVersion( + appCode: String?, + apigwType: String?, + userId: String, + projectId: String, + pipelineId: String, + version: Int + ): Result { + logger.info("OPENAPI_PIPELINE_V4|$userId|getVersion|$projectId|$pipelineId") + return client.get(ServicePipelineVersionResource::class).getVersion( + userId = userId, + projectId = projectId, + pipelineId = pipelineId, + version = version + ) + } + + override fun previewCode( + appCode: String?, + apigwType: String?, + userId: String, + projectId: String, + pipelineId: String, + version: Int? + ): Result { + logger.info("OPENAPI_PIPELINE_V4|$userId|previewCode|$projectId|$pipelineId") + return client.get(ServicePipelineVersionResource::class).previewCode( + userId = userId, + projectId = projectId, + pipelineId = pipelineId, + version = version + ) + } + + override fun savePipelineDraft( + appCode: String?, + apigwType: String?, + userId: String, + projectId: String, + modelAndYaml: PipelineVersionWithModelRequest + ): Result { + logger.info("OPENAPI_PIPELINE_V4|$userId|savePipelineDraft|$projectId|$modelAndYaml") + return client.get(ServicePipelineVersionResource::class).savePipelineDraft( + userId = userId, + projectId = projectId, + modelAndYaml = modelAndYaml + ) + } + + override fun versionCreatorList( + appCode: String?, + apigwType: String?, + userId: String, + projectId: String, + pipelineId: String, + page: Int?, + pageSize: Int? + ): Result> { + logger.info("OPENAPI_PIPELINE_V4|$userId|versionCreatorList|$projectId|$pipelineId") + return client.get(ServicePipelineVersionResource::class).versionCreatorList( + userId = userId, + projectId = projectId, + pipelineId = pipelineId, + page = page, + pageSize = pageSize + ) + } + + override fun versionList( + appCode: String?, + apigwType: String?, + userId: String, + projectId: String, + pipelineId: String, + fromVersion: Int?, + versionName: String?, + creator: String?, + description: String?, + page: Int?, + pageSize: Int? + ): Result> { + logger.info("OPENAPI_PIPELINE_V4|$userId|versionList|$projectId|$pipelineId") + return client.get(ServicePipelineVersionResource::class).versionList( + userId = userId, + projectId = projectId, + pipelineId = pipelineId, + fromVersion = fromVersion, + versionName = versionName, + creator = creator, + description = description, + page = page, + pageSize = pageSize + ) + } + + override fun getPipelineOperationLogs( + appCode: String?, + apigwType: String?, + userId: String, + projectId: String, + pipelineId: String, + creator: String?, + page: Int?, + pageSize: Int? + ): Result> { + logger.info("OPENAPI_PIPELINE_V4|$userId|getPipelineOperationLogs|$projectId|$pipelineId") + return client.get(ServicePipelineVersionResource::class).getPipelineOperationLogs( + userId = userId, + projectId = projectId, + pipelineId = pipelineId, + creator = creator, + page = page, + pageSize = pageSize + ) + } + + override fun operatorList( + appCode: String?, + apigwType: String?, + userId: String, + projectId: String, + pipelineId: String + ): Result> { + logger.info("OPENAPI_PIPELINE_V4|$userId|operatorList|$projectId|$pipelineId") + return client.get(ServicePipelineVersionResource::class).operatorList( + userId = userId, + projectId = projectId, + pipelineId = pipelineId + ) + } + + override fun rollbackDraftFromVersion( + appCode: String?, + apigwType: String?, + userId: String, + projectId: String, + pipelineId: String, + version: Int + ): Result { + logger.info("OPENAPI_PIPELINE_V4|$userId|rollbackDraftFromVersion|$projectId|$pipelineId") + return client.get(ServicePipelineVersionResource::class).rollbackDraftFromVersion( + userId = userId, + projectId = projectId, + pipelineId = pipelineId, + version = version + ) + } + + override fun exportPipeline( + appCode: String?, + apigwType: String?, + userId: String, + projectId: String, + pipelineId: String, + version: Int?, + storageType: String? + ): Response { + logger.info("OPENAPI_PIPELINE_V4|$userId|exportPipeline|$projectId|$pipelineId") + return client.get(ServicePipelineVersionResource::class).exportPipeline( + userId = userId, + projectId = projectId, + pipelineId = pipelineId, + version = version, + storageType = storageType + ) + } + + override fun updateBuildNo( + appCode: String?, + apigwType: String?, + userId: String, + projectId: String, + pipelineId: String, + buildNo: BuildNoUpdateReq + ): Result { + logger.info("OPENAPI_PIPELINE_V4|$userId|updateBuildNo|$projectId|$pipelineId") + return client.get(ServicePipelineVersionResource::class).updateBuildNo( + userId = userId, + projectId = projectId, + pipelineId = pipelineId, + buildNo = buildNo + ) + } +} diff --git a/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/resources/apigw/v4/ApigwProjectResourceV4Impl.kt b/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/resources/apigw/v4/ApigwProjectResourceV4Impl.kt index a83c56ccd7b..237932a75c2 100644 --- a/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/resources/apigw/v4/ApigwProjectResourceV4Impl.kt +++ b/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/resources/apigw/v4/ApigwProjectResourceV4Impl.kt @@ -40,6 +40,7 @@ import com.tencent.devops.project.pojo.ProjectCreateUserInfo import com.tencent.devops.project.pojo.ProjectUpdateInfo import com.tencent.devops.project.pojo.ProjectVO import com.tencent.devops.project.pojo.Result +import com.tencent.devops.project.pojo.enums.PluginDetailsDisplayOrder import com.tencent.devops.project.pojo.enums.ProjectValidateType import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired @@ -185,4 +186,18 @@ class ApigwProjectResourceV4Impl @Autowired constructor( productId = productId ) } + + override fun updatePluginDetailsDisplay( + appCode: String?, + apigwType: String?, + userId: String?, + projectId: String, + pluginDetailsDisplayOrder: List + ): Result { + logger.info("updateProjectProductId v4 |$appCode|$userId|$projectId|$pluginDetailsDisplayOrder") + return client.get(ServiceProjectResource::class).updatePluginDetailsDisplay( + projectId = projectId, + pluginDetailsDisplayOrder = pluginDetailsDisplayOrder + ) + } } diff --git a/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt b/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt index e77b7e99249..b42ae3d808c 100644 --- a/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt +++ b/src/backend/ci/core/openapi/biz-openapi/src/main/kotlin/com/tencent/devops/openapi/service/doc/DocumentService.kt @@ -85,10 +85,6 @@ import io.swagger.v3.oas.models.media.StringSchema import io.swagger.v3.oas.models.parameters.Parameter import io.swagger.v3.oas.models.parameters.RequestBody import io.swagger.v3.oas.models.responses.ApiResponse -import org.apache.commons.lang3.StringUtils -import org.reflections.Reflections -import org.springframework.beans.factory.annotation.Value -import org.springframework.stereotype.Service import kotlin.jvm.internal.DefaultConstructorMarker import kotlin.reflect.KFunction import kotlin.reflect.KType @@ -96,6 +92,10 @@ import kotlin.reflect.full.memberProperties import kotlin.reflect.jvm.isAccessible import kotlin.reflect.jvm.javaConstructor import kotlin.reflect.jvm.javaType +import org.apache.commons.lang3.StringUtils +import org.reflections.Reflections +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Service import io.swagger.v3.oas.annotations.media.Schema as SchemaAnnotation @Service @@ -163,7 +163,9 @@ class DocumentService { getI18n(BK_PARAM_ILLUSTRATE), getI18n(BK_DEFAULT_VALUE) ), - rows = parseParameters(operation.parameters.filter { it.`in` == PATH_PARAM }), + rows = parseParameters( + operation.parameters?.filter { it.`in` == PATH_PARAM } ?: emptyList() + ), key = "path_parameter" ).checkLoadModel(onLoadModel) }, path + httpMethod + "path") @@ -179,7 +181,9 @@ class DocumentService { getI18n(BK_PARAM_ILLUSTRATE), getI18n(BK_DEFAULT_VALUE) ), - rows = parseParameters(operation.parameters.filter { it.`in` == QUERY_PARAM }), + rows = parseParameters( + operation.parameters?.filter { it.`in` == QUERY_PARAM } ?: emptyList() + ), "query_parameter" ).checkLoadModel(onLoadModel) }, path + httpMethod + "query") @@ -195,7 +199,9 @@ class DocumentService { getI18n(BK_PARAM_ILLUSTRATE), getI18n(BK_DEFAULT_VALUE) ), - rows = parseParameters(operation.parameters.filter { it.`in` == HEADER_PARAM }), + rows = parseParameters( + operation.parameters?.filter { it.`in` == HEADER_PARAM } ?: emptyList() + ), "header_parameter" ).checkLoadModel(onLoadModel) .setRow( @@ -676,16 +682,20 @@ class DocumentService { } } - private fun loadModelJson(model: Schema<*>?, loadJson: MutableMap) { + private fun loadModelJson( + model: Schema<*>?, + loadJson: MutableMap, + deep: MutableSet = mutableSetOf() + ) { if (model == null) return if (StringUtils.isNotBlank(model.`$ref`)) { val key = model.`$ref`.removePrefix("#/components/schemas/") - definitions[key]?.let { loadModelJson(it, loadJson) } + definitions[key]?.let { loadModelJson(it, loadJson, deep) } } when (model) { is ComposedSchema -> { model.allOf?.forEach { - loadModelJson(it, loadJson) + loadModelJson(it, loadJson, deep) } } @@ -694,18 +704,23 @@ class DocumentService { loadJson[model.discriminator.toString()] = "string" } model.properties?.forEach { (key, property) -> - loadJson[key] = loadPropertyJson(property) + loadJson[key] = loadPropertyJson(property, deep) } } } } - private fun loadPropertyJson(property: Schema<*>): Any { + private fun loadPropertyJson(property: Schema<*>, deep: MutableSet = mutableSetOf()): Any { if (StringUtils.isNotBlank(property.`$ref`)) { - val loadJson = mutableMapOf() - val key = property.`$ref`.removePrefix("#/components/schemas/") - definitions[key]?.let { loadModelJson(it, loadJson) } - return loadJson + if (property.`$ref` !in deep) { + deep.add(property.`$ref`) + val loadJson = mutableMapOf() + val key = property.`$ref`.removePrefix("#/components/schemas/") + definitions[key]?.let { loadModelJson(it, loadJson, deep) } + return loadJson + } else { + return property.`$ref` + } } return when (property) { // swagger无法获取到map的key类型 @@ -718,7 +733,7 @@ class DocumentService { } is ArraySchema -> { - listOf(loadPropertyJson(property.items)) + listOf(loadPropertyJson(property.items, deep)) } is StringSchema -> { diff --git a/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/api/service/ServicePipelineVersionResource.kt b/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/api/service/ServicePipelineVersionResource.kt index 37e61ec5438..5e0da198dba 100644 --- a/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/api/service/ServicePipelineVersionResource.kt +++ b/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/api/service/ServicePipelineVersionResource.kt @@ -33,6 +33,7 @@ import com.tencent.devops.common.api.pojo.Page import com.tencent.devops.common.api.pojo.Result import com.tencent.devops.common.pipeline.PipelineVersionWithModel import com.tencent.devops.common.pipeline.PipelineVersionWithModelRequest +import com.tencent.devops.common.pipeline.pojo.BuildNoUpdateReq import com.tencent.devops.common.pipeline.pojo.TemplateInstanceCreateRequest import com.tencent.devops.common.pipeline.pojo.transfer.PreviewResponse import com.tencent.devops.process.engine.pojo.PipelineVersionWithInfo @@ -314,4 +315,21 @@ interface ServicePipelineVersionResource { @QueryParam("storageType") storageType: String? ): Response + + @Operation(summary = "重置流水线推荐版本号") + @POST + @Path("/projects/{projectId}/pipelines/{pipelineId}/updateBuildNo") + fun updateBuildNo( + @Parameter(description = "用户ID", required = true, example = AUTH_HEADER_USER_ID_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "流水线ID", required = true) + @PathParam("pipelineId") + pipelineId: String, + @Parameter(description = "流水线构建推荐版本号更新", required = true) + buildNo: BuildNoUpdateReq + ): Result } diff --git a/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/constant/ProcessMessageCode.kt b/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/constant/ProcessMessageCode.kt index 591e64cdbc8..a7ca239c2de 100644 --- a/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/constant/ProcessMessageCode.kt +++ b/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/constant/ProcessMessageCode.kt @@ -361,6 +361,7 @@ object ProcessMessageCode { const val ERROR_PIPELINE_CONDITION_EXPRESSION_TOO_LONG = "2101253" // 自定义条件表达式{0}的长度超过{1}位 const val ERROR_PIPELINE_BUILD_START_PARAM_NO_EMPTY = "2101254" // 构建启动参数如果必填,不能为空 const val ERROR_REPEATEDLY_START_VM = "2101255" // 重复启动构建机,当前构建机的状态为:{0} + const val ERROR_PIPELINE_VARIABLES_OUT_OF_LENGTH = "2101256" // 流水线启动参数{0}超出4000长度限制 const val BK_SUCCESSFULLY_DISTRIBUTED = "bkSuccessfullyDistributed" // 跨项目构件分发成功,共分发了{0}个文件 const val BK_SUCCESSFULLY_FAILED = "bkSuccessfullyFailed" // 跨项目构件分发失败, diff --git a/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/pojo/setting/PipelineSettingVersion.kt b/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/pojo/setting/PipelineSettingVersion.kt index efcdadabd78..8f1dc7aa6ce 100644 --- a/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/pojo/setting/PipelineSettingVersion.kt +++ b/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/pojo/setting/PipelineSettingVersion.kt @@ -27,6 +27,7 @@ package com.tencent.devops.process.pojo.setting +import com.tencent.devops.common.api.pojo.PipelineAsCodeSettings import com.tencent.devops.common.pipeline.pojo.setting.PipelineRunLockType import com.tencent.devops.common.pipeline.pojo.setting.PipelineSetting import com.tencent.devops.common.pipeline.pojo.setting.Subscription @@ -73,7 +74,9 @@ data class PipelineSettingVersion( @get:Schema(title = "并发时,是否相同group取消正在执行的流水线", required = false) var concurrencyCancelInProgress: Boolean?, @get:Schema(title = "并发构建数量限制", required = false) - var maxConRunningQueueSize: Int? = null // MULTIPLE类型时,并发构建数量限制 + var maxConRunningQueueSize: Int? = null, // MULTIPLE类型时,并发构建数量限制 + @get:Schema(title = "YAML流水线特殊配置", required = false) + var pipelineAsCodeSettings: PipelineAsCodeSettings? = null ) { companion object { @@ -93,7 +96,8 @@ data class PipelineSettingVersion( buildNumRule = setting.buildNumRule, concurrencyCancelInProgress = setting.concurrencyCancelInProgress, concurrencyGroup = setting.concurrencyGroup, - maxConRunningQueueSize = setting.maxConRunningQueueSize + maxConRunningQueueSize = setting.maxConRunningQueueSize, + pipelineAsCodeSettings = setting.pipelineAsCodeSettings ) } } diff --git a/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/utils/Constants.kt b/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/utils/Constants.kt index db7dd3584b0..e682c48f61f 100644 --- a/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/utils/Constants.kt +++ b/src/backend/ci/core/process/api-process/src/main/kotlin/com/tencent/devops/process/utils/Constants.kt @@ -124,6 +124,7 @@ const val PIPELINE_ATOM_VERSION = "BK_CI_ATOM_VERSION" // "流水线插件版本 const val PIPELINE_TASK_NAME = "BK_CI_TASK_NAME" // "流水线任务名称(步骤名称)" const val PIPELINE_STEP_ID = "BK_CI_STEP_ID" // "用户自定义ID(上下文标识)" const val PIPELINE_ATOM_TIMEOUT = "BK_CI_ATOM_TIMEOUT" // "流水线插件超时时间" +const val PIPELINE_DIALECT = "BK_CI_PIPELINE_DIALECT" // 流水线语法风格 /** * 自定义触发材料 */ diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/PipelineSettingDao.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/PipelineSettingDao.kt index a27fe282702..cf09bf1e3fb 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/PipelineSettingDao.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/PipelineSettingDao.kt @@ -92,7 +92,8 @@ class PipelineSettingDao { CLEAN_VARIABLES_WHEN_RETRY, SUCCESS_SUBSCRIPTION, FAILURE_SUBSCRIPTION, - VERSION + VERSION, + PIPELINE_AS_CODE_SETTINGS ).values( setting.projectId, setting.pipelineName, @@ -126,7 +127,8 @@ class PipelineSettingDao { setting.cleanVariablesWhenRetry, JsonUtil.toJson(successSubscriptionList, false), JsonUtil.toJson(failSubscriptionList, false), - setting.version + setting.version, + setting.pipelineAsCodeSettings?.let { JsonUtil.toJson(it, false) } ).onDuplicateKeyUpdate() .set(NAME, setting.pipelineName) .set(DESC, setting.desc) diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/PipelineSettingVersionDao.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/PipelineSettingVersionDao.kt index 0991b83670a..c72ad643261 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/PipelineSettingVersionDao.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/PipelineSettingVersionDao.kt @@ -29,6 +29,7 @@ package com.tencent.devops.process.dao import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.module.kotlin.readValue +import com.tencent.devops.common.api.pojo.PipelineAsCodeSettings import com.tencent.devops.common.api.util.DateTimeUtil import com.tencent.devops.common.api.util.JsonUtil import com.tencent.devops.common.pipeline.pojo.setting.PipelineRunLockType @@ -114,6 +115,9 @@ class PipelineSettingVersionDao { .set(SUCCESS_SUBSCRIPTION, JsonUtil.toJson(successSubscriptionList, false)) .set(FAILURE_SUBSCRIPTION, JsonUtil.toJson(failSubscriptionList, false)) .set(MAX_CON_RUNNING_QUEUE_SIZE, setting.maxConRunningQueueSize ?: -1) + .set(PIPELINE_AS_CODE_SETTINGS, setting.pipelineAsCodeSettings?.let { self -> + JsonUtil.toJson(self, false) + }) .execute() } } @@ -223,7 +227,10 @@ class PipelineSettingVersionDao { buildNumRule = t.buildNumRule, concurrencyCancelInProgress = t.concurrencyCancelInProgress, concurrencyGroup = t.concurrencyGroup, - maxConRunningQueueSize = t.maxConRunningQueueSize + maxConRunningQueueSize = t.maxConRunningQueueSize, + pipelineAsCodeSettings = t.pipelineAsCodeSettings?.let { self -> + JsonUtil.to(self, PipelineAsCodeSettings::class.java) + } ) } } diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/label/PipelineViewGroupDao.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/label/PipelineViewGroupDao.kt index 4a98bf75e86..111f875e6d9 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/label/PipelineViewGroupDao.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/dao/label/PipelineViewGroupDao.kt @@ -221,13 +221,15 @@ class PipelineViewGroupDao { fun countByViewId( dslContext: DSLContext, projectId: String, - viewIds: Collection + viewIds: Collection, + filterPipelineIds: List? = null ): Map { with(TPipelineViewGroup.T_PIPELINE_VIEW_GROUP) { return dslContext.select(VIEW_ID, count()) .from(this) .where(PROJECT_ID.eq(projectId)) .and(VIEW_ID.`in`(viewIds)) + .let { if (filterPipelineIds != null) it.and(PIPELINE_ID.`in`(filterPipelineIds)) else it } .groupBy(VIEW_ID) .fetch().map { it.value1() to it.value2() }.toMap() } diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/dao/PipelineInfoDao.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/dao/PipelineInfoDao.kt index ec7e213bb72..c52dd85fe16 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/dao/PipelineInfoDao.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/dao/PipelineInfoDao.kt @@ -729,7 +729,8 @@ class PipelineInfoDao { projectId: String, excludePipelineIds: List, channelCode: ChannelCode? = null, - includeDelete: Boolean = false + includeDelete: Boolean = false, + filterPipelineIds: List? = null ): Int { with(T_PIPELINE_INFO) { return dslContext.selectCount() @@ -738,6 +739,7 @@ class PipelineInfoDao { .and(PIPELINE_ID.notIn(excludePipelineIds)) .let { if (channelCode == null) it else it.and(CHANNEL.eq(channelCode.name)) } .let { if (includeDelete) it else it.and(DELETE.eq(false)) } + .let { if (filterPipelineIds != null) it.and(PIPELINE_ID.`in`(filterPipelineIds)) else it } .fetchOne()?.value1() ?: 0 } } diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/extend/DefaultModelCheckPlugin.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/extend/DefaultModelCheckPlugin.kt index 90112cc805b..c99c41322ae 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/extend/DefaultModelCheckPlugin.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/extend/DefaultModelCheckPlugin.kt @@ -37,6 +37,7 @@ import com.tencent.devops.common.pipeline.container.NormalContainer import com.tencent.devops.common.pipeline.container.Stage import com.tencent.devops.common.pipeline.container.TriggerContainer import com.tencent.devops.common.pipeline.container.VMBuildContainer +import com.tencent.devops.common.pipeline.dialect.IPipelineDialect import com.tencent.devops.common.pipeline.enums.JobRunCondition import com.tencent.devops.common.pipeline.enums.StageRunCondition import com.tencent.devops.common.pipeline.extend.ModelCheckPlugin @@ -90,7 +91,8 @@ open class DefaultModelCheckPlugin constructor( projectId: String?, userId: String, isTemplate: Boolean, - oauthUser: String? + oauthUser: String?, + pipelineDialect: IPipelineDialect? ): Int { var metaSize = 0 // 检查流水线名称 @@ -122,7 +124,10 @@ open class DefaultModelCheckPlugin constructor( val trigger = stages.getOrNull(0) ?: throw ErrorCodeException(errorCode = ProcessMessageCode.ERROR_PIPELINE_MODEL_NEED_JOB) // 检查触发容器 - val paramsMap = checkTriggerContainer(trigger) + val paramsMap = checkTriggerContainer( + trigger = trigger, + supportChineseVarName = pipelineDialect?.supportChineseVarName() + ) val contextMap = PipelineVarUtil.fillVariableMap(paramsMap.mapValues { it.value.defaultValue.toString() }) val elementCnt = mutableMapOf() val containerCnt = mutableMapOf() @@ -474,7 +479,10 @@ open class DefaultModelCheckPlugin constructor( } } - open fun checkTriggerContainer(trigger: Stage): Map { + open fun checkTriggerContainer( + trigger: Stage, + supportChineseVarName: Boolean? + ): Map { if (trigger.containers.size != 1) { logger.warn("The trigger stage contain more than one container (${trigger.containers.size})") throw ErrorCodeException( @@ -484,7 +492,11 @@ open class DefaultModelCheckPlugin constructor( val triggerContainer = (trigger.containers.getOrNull(0) ?: throw ErrorCodeException( errorCode = ProcessMessageCode.ERROR_PIPELINE_MODEL_NEED_JOB )) as TriggerContainer - return PipelineUtils.checkPipelineParams(triggerContainer.params) + return if (supportChineseVarName != false) { + triggerContainer.params.associateBy { it.id } + } else { + PipelineUtils.checkPipelineParams(triggerContainer.params) + } } companion object { diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineContainerService.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineContainerService.kt index 47d96a19d01..b9243b239aa 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineContainerService.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineContainerService.kt @@ -627,6 +627,10 @@ class PipelineContainerService @Autowired constructor( startTime = null endTime = null executeCount = context.executeCount + /*重试时重置构建机互斥组名称,以便变量更改时能生效*/ + controlOption.agentReuseMutex?.runtimeAgentOrEnvId = null + /*重试时重置互斥组名称,以便变量更改时能生效*/ + controlOption.mutexGroup?.runtimeMutexGroup = null updateExistsContainer.add(Pair(this, container)) } return@findHistoryContainer diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRepositoryService.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRepositoryService.kt index 5968fda12d2..6309eff2f9b 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRepositoryService.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRepositoryService.kt @@ -48,6 +48,7 @@ import com.tencent.devops.common.pipeline.container.NormalContainer import com.tencent.devops.common.pipeline.container.Stage import com.tencent.devops.common.pipeline.container.TriggerContainer import com.tencent.devops.common.pipeline.container.VMBuildContainer +import com.tencent.devops.common.pipeline.dialect.IPipelineDialect import com.tencent.devops.common.pipeline.enums.BranchVersionAction import com.tencent.devops.common.pipeline.enums.ChannelCode import com.tencent.devops.common.pipeline.enums.PipelineInstanceTypeEnum @@ -233,7 +234,10 @@ class PipelineRepositoryService constructor( versionStatus: VersionStatus? = VersionStatus.RELEASED, branchName: String? = null, description: String? = null, - yamlInfo: PipelineYamlVo? = null + yamlInfo: PipelineYamlVo? = null, + inheritedDialectSetting: Boolean? = null, + pipelineDialectSetting: String? = null, + pipelineDialect: IPipelineDialect? = null ): DeployPipelineResult { // 生成流水线ID,新流水线以p-开头,以区分以前旧数据 @@ -247,7 +251,8 @@ class PipelineRepositoryService constructor( create = create, versionStatus = versionStatus, channelCode = channelCode, - yamlInfo = yamlInfo + yamlInfo = yamlInfo, + pipelineDialect = pipelineDialect ) val triggerContainer = model.getTriggerContainer() val buildNo = triggerContainer.buildNo?.apply { @@ -313,7 +318,9 @@ class PipelineRepositoryService constructor( versionStatus = versionStatus, branchName = branchName, description = description, - baseVersion = baseVersion + baseVersion = baseVersion, + inheritedDialectSetting = inheritedDialectSetting, + pipelineDialectSetting = pipelineDialectSetting ) } operationLogService.addOperationLog( @@ -342,13 +349,15 @@ class PipelineRepositoryService constructor( create: Boolean = true, versionStatus: VersionStatus? = VersionStatus.RELEASED, channelCode: ChannelCode, - yamlInfo: PipelineYamlVo? = null + yamlInfo: PipelineYamlVo? = null, + pipelineDialect: IPipelineDialect? = null ): List { val metaSize = modelCheckPlugin.checkModelIntegrity( model = model, projectId = projectId, userId = userId, - oauthUser = getPipelineOauthUser(projectId, pipelineId) + oauthUser = getPipelineOauthUser(projectId, pipelineId), + pipelineDialect = pipelineDialect ) // 去重id val distinctIdSet = HashSet(metaSize, 1F /* loadFactor */) @@ -637,7 +646,9 @@ class PipelineRepositoryService constructor( templateId: String? = null, versionStatus: VersionStatus? = VersionStatus.RELEASED, branchName: String?, - description: String? + description: String?, + inheritedDialectSetting: Boolean? = true, + pipelineDialectSetting: String? = null ): DeployPipelineResult { // #8161 如果只有一个草稿版本的创建操作,流水线状态也为仅有草稿 val modelVersion = 1 @@ -703,7 +714,13 @@ class PipelineRepositoryService constructor( content = NotifyTemplateUtils.getCommonShutdownFailureContent() ).takeIf { failType.isNotEmpty() } PipelineSetting.defaultSetting( - projectId, pipelineId, model.name, maxPipelineResNum, failSubscription + projectId = projectId, + pipelineId = pipelineId, + pipelineName = model.name, + maxPipelineResNum = maxPipelineResNum, + failSubscription = failSubscription, + inheritedDialectSetting = inheritedDialectSetting, + pipelineDialectSetting = pipelineDialectSetting ) } @@ -743,7 +760,10 @@ class PipelineRepositoryService constructor( } setting.labels = labels } - setting.pipelineAsCodeSettings = PipelineAsCodeSettings() + setting.pipelineAsCodeSettings = PipelineAsCodeSettings.initDialect( + inheritedDialect = inheritedDialectSetting, + pipelineDialect = pipelineDialectSetting + ) newSetting = setting } // 如果不需要覆盖模板内容,则直接保存传值或默认值 @@ -1677,11 +1697,13 @@ class PipelineRepositoryService constructor( fun getSettingByPipelineVersion( projectId: String, pipelineId: String, - pipelineVersion: Int + pipelineVersion: Int? ): PipelineSetting? { - val resource = pipelineResourceVersionDao.getPipelineVersionSimple( - dslContext, projectId, pipelineId, pipelineVersion - ) + val resource = pipelineVersion?.let { + pipelineResourceVersionDao.getPipelineVersionSimple( + dslContext, projectId, pipelineId, pipelineVersion + ) + } return resource?.settingVersion?.let { pipelineSettingVersionService.getPipelineSetting( projectId = projectId, @@ -1739,6 +1761,7 @@ class PipelineRepositoryService constructor( isTemplate: Boolean ): PipelineName { var oldName: String = setting.pipelineName + setting.pipelineAsCodeSettings?.resetDialect() (context ?: dslContext).transaction { t -> val transactionContext = DSL.using(t) val old = pipelineSettingDao.getSetting( diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRuntimeExtService.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRuntimeExtService.kt index f46ee1ba102..31c5c6aec6f 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRuntimeExtService.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/PipelineRuntimeExtService.kt @@ -126,13 +126,19 @@ class PipelineRuntimeExtService @Autowired constructor( return null } - fun existQueue(projectId: String, pipelineId: String, buildId: String, buildStatus: BuildStatus): Boolean { + fun changeBuildStatus( + projectId: String, + pipelineId: String, + buildId: String, + oldBuildStatus: BuildStatus, + newBuildStatus: BuildStatus + ): Boolean { return pipelineBuildDao.updateStatus( dslContext = dslContext, projectId = projectId, buildId = buildId, - oldBuildStatus = buildStatus, - newBuildStatus = BuildStatus.UNEXEC + oldBuildStatus = oldBuildStatus, + newBuildStatus = newBuildStatus ) } } diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/vmbuild/EngineVMBuildService.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/vmbuild/EngineVMBuildService.kt index fabf2abe690..f154334063b 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/vmbuild/EngineVMBuildService.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/service/vmbuild/EngineVMBuildService.kt @@ -41,12 +41,17 @@ import com.tencent.devops.common.api.util.timestampmilli import com.tencent.devops.common.client.Client import com.tencent.devops.common.event.dispatcher.pipeline.PipelineEventDispatcher import com.tencent.devops.common.event.enums.ActionType +import com.tencent.devops.common.event.enums.PipelineBuildStatusBroadCastEventType import com.tencent.devops.common.event.pojo.pipeline.PipelineBuildStatusBroadCastEvent import com.tencent.devops.common.log.utils.BuildLogPrinter import com.tencent.devops.common.pipeline.EnvReplacementParser +import com.tencent.devops.common.pipeline.Model import com.tencent.devops.common.pipeline.NameAndValue +import com.tencent.devops.common.pipeline.container.Container import com.tencent.devops.common.pipeline.container.NormalContainer +import com.tencent.devops.common.pipeline.container.Stage import com.tencent.devops.common.pipeline.container.VMBuildContainer +import com.tencent.devops.common.pipeline.dialect.PipelineDialectUtil import com.tencent.devops.common.pipeline.enums.BuildFormPropertyType import com.tencent.devops.common.pipeline.enums.BuildStatus import com.tencent.devops.common.pipeline.enums.BuildTaskStatus @@ -71,6 +76,7 @@ import com.tencent.devops.process.engine.control.BuildingHeartBeatUtils import com.tencent.devops.process.engine.control.ControlUtils import com.tencent.devops.process.engine.control.lock.ContainerIdLock import com.tencent.devops.process.engine.pojo.BuildInfo +import com.tencent.devops.process.engine.pojo.PipelineBuildContainer import com.tencent.devops.process.engine.pojo.PipelineBuildTask import com.tencent.devops.process.engine.pojo.UpdateTaskInfo import com.tencent.devops.process.engine.pojo.builds.CompleteTask @@ -98,6 +104,7 @@ import com.tencent.devops.process.service.PipelineAsCodeService import com.tencent.devops.process.service.PipelineContextService import com.tencent.devops.process.util.TaskUtils import com.tencent.devops.process.utils.PIPELINE_BUILD_REMARK +import com.tencent.devops.process.utils.PIPELINE_DIALECT import com.tencent.devops.process.utils.PIPELINE_ELEMENT_ID import com.tencent.devops.process.utils.PIPELINE_VMSEQ_ID import com.tencent.devops.process.utils.PipelineVarUtil @@ -193,8 +200,9 @@ class EngineVMBuildService @Autowired(required = false) constructor( val variables = buildVariableService.getAllVariable(projectId, buildInfo.pipelineId, buildId) val variablesWithType = buildVariableService.getAllVariableWithType(projectId, buildId).toMutableList() val model = containerBuildDetailService.getBuildModel(projectId, buildId) + // TODO 没有升级的worker还需要用到这个变量,下一版删除 val asCodeSettings = pipelineAsCodeService.getPipelineAsCodeSettings( - projectId, buildInfo.pipelineId, buildId, buildInfo + projectId = projectId, pipelineId = buildInfo.pipelineId ) Preconditions.checkNotNull(model, NotFoundException("Build Model ($buildId) is not exist")) @@ -224,72 +232,20 @@ class EngineVMBuildService @Autowired(required = false) constructor( params = arrayOf(c.startVMStatus ?: "") ) ) - val containerAppResource = client.get(ServiceContainerAppResource::class) - // #4518 填充构建机环境变量、构建上下文、获取超时时间 - val (containerEnv, context, timeoutMills) = when (c) { - is VMBuildContainer -> { - val envList = mutableListOf() - val tm = transMinuteTimeoutToMills(container.controlOption.jobControlOption.timeout) - val contextMap = variables.plus( - pipelineContextService.buildContext( - projectId = projectId, pipelineId = pipelineId, buildId = buildId, - stageId = s.id!!, containerId = c.id!!, taskId = null, - variables = variables, model = model, executeCount = buildInfo.executeCount - ) - ).toMutableMap() - fillContainerContext(contextMap, c.customEnv, c.matrixContext, asCodeSettings?.enable) - val asCodeEnabled = asCodeSettings?.enable == true - val contextPair = if (asCodeEnabled) { - EnvReplacementParser.getCustomExecutionContextByMap(contextMap) - } else null - c.buildEnv?.forEach { env -> - containerAppResource.getBuildEnv( - name = env.key, - version = EnvReplacementParser.parse( - value = env.value, - contextMap = contextMap, - onlyExpression = asCodeEnabled, - contextPair = contextPair - ), - os = c.baseOS.name.lowercase() - ).data?.let { self -> envList.add(self) } - } - - // 设置Job环境变量customEnv到variablesWithType和variables中 - // TODO 此处应收敛到variablesWithType或variables的其中一个 - val customBuildParameters = mutableListOf() - c.customEnv?.forEach { nameAndValue -> - val value = EnvReplacementParser.parse( - value = nameAndValue.value, - contextMap = contextMap, - onlyExpression = asCodeEnabled, - contextPair = contextPair - ) - val key = nameAndValue.key ?: return@forEach - contextMap[key] = value - customBuildParameters.add( - BuildParameters( - key = key, - value = value, - valueType = BuildFormPropertyType.STRING, - readOnly = true - ) - ) - } - variablesWithType.addAll(customBuildParameters) - Triple(envList, contextMap, tm) - } - - is NormalContainer -> { - val tm = transMinuteTimeoutToMills(container.controlOption.jobControlOption.timeout) - val contextMap = pipelineContextService.getAllBuildContext(variables).toMutableMap() - fillContainerContext(contextMap, null, c.matrixContext, asCodeSettings?.enable) - Triple(mutableListOf(), contextMap, tm) - } - - else -> throw OperationException("vmName($vmName) is an illegal container type: $c") - } + val (containerEnv, context, timeoutMills) = getContainerContext( + container = c, + buildContainer = container, + variables = variables, + projectId = projectId, + pipelineId = pipelineId, + buildId = buildId, + stage = s, + model = model, + buildInfo = buildInfo, + variablesWithType = variablesWithType, + vmName = vmName + ) buildingHeartBeatUtils.addHeartBeat(buildId, vmSeqId, System.currentTimeMillis()) // # 2365 将心跳监听事件 构建机主动上报成功状态时才触发 buildingHeartBeatUtils.dispatchHeartbeatEvent(buildInfo = buildInfo, containerId = vmSeqId) @@ -325,6 +281,129 @@ class EngineVMBuildService @Autowired(required = false) constructor( throw NotFoundException("Fail to find the vm build container: j($vmSeqId) vmName($vmName)") } + private fun getContainerContext( + container: Container, + buildContainer: PipelineBuildContainer, + variables: Map, + projectId: String, + pipelineId: String, + buildId: String, + stage: Stage, + model: Model?, + buildInfo: BuildInfo, + variablesWithType: MutableList, + vmName: String + ): Triple, MutableMap, Long> { + return when (container) { + is VMBuildContainer -> { + getVMBuildContainerContext( + container = container, + buildContainer = buildContainer, + variables = variables, + projectId = projectId, + pipelineId = pipelineId, + buildId = buildId, + stage = stage, + model = model, + buildInfo = buildInfo, + variablesWithType = variablesWithType + ) + } + + is NormalContainer -> { + val tm = transMinuteTimeoutToMills(buildContainer.controlOption.jobControlOption.timeout) + val contextMap = pipelineContextService.getAllBuildContext(variables).toMutableMap() + fillContainerContext(contextMap, null, container.matrixContext) + Triple(mutableListOf(), contextMap, tm) + } + + else -> throw OperationException("vmName($vmName) is an illegal container type: $container") + } + } + + private fun getVMBuildContainerContext( + container: VMBuildContainer, + buildContainer: PipelineBuildContainer, + variables: Map, + projectId: String, + pipelineId: String, + buildId: String, + stage: Stage, + model: Model?, + buildInfo: BuildInfo, + variablesWithType: MutableList + ): Triple, MutableMap, Long> { + val containerAppResource = client.get(ServiceContainerAppResource::class) + val envList = mutableListOf() + val tm = transMinuteTimeoutToMills(buildContainer.controlOption.jobControlOption.timeout) + val contextMap = variables.plus( + pipelineContextService.buildContext( + projectId = projectId, pipelineId = pipelineId, buildId = buildId, + stageId = stage.id!!, containerId = container.id!!, taskId = null, + variables = variables, model = model, executeCount = buildInfo.executeCount + ) + ).toMutableMap() + val dialect = PipelineDialectUtil.getPipelineDialect(variables[PIPELINE_DIALECT]) + fillContainerContext(contextMap, container.customEnv, container.matrixContext) + val contextPair by lazy { + EnvReplacementParser.getCustomExecutionContextByMap(contextMap) + } + container.buildEnv?.forEach { env -> + containerAppResource.getBuildEnv( + name = env.key, + version = EnvReplacementParser.parse( + value = env.value, + contextMap = contextMap, + onlyExpression = dialect.supportUseExpression(), + contextPair = contextPair + ), + os = container.baseOS.name.lowercase() + ).data?.let { self -> envList.add(self) } + } + + // 设置Job环境变量customEnv到variablesWithType和variables中 + // TODO 此处应收敛到variablesWithType或variables的其中一个 + val customBuildParameters = mutableListOf() + // 兼容历史数据 + container.customBuildEnv?.forEach { (k, v) -> + val value = EnvReplacementParser.parse( + value = v, + contextMap = contextMap, + onlyExpression = dialect.supportUseExpression(), + contextPair = contextPair + ) + contextMap[k] = value + customBuildParameters.add( + BuildParameters( + key = k, + value = value, + valueType = BuildFormPropertyType.STRING, + readOnly = true + ) + ) + } + container.customEnv?.forEach { nameAndValue -> + val key = nameAndValue.key ?: return@forEach + val value = EnvReplacementParser.parse( + value = nameAndValue.value, + contextMap = contextMap, + onlyExpression = dialect.supportUseExpression(), + contextPair = contextPair + ) + contextMap[key] = value + customBuildParameters.add( + BuildParameters( + key = key, + value = value, + valueType = BuildFormPropertyType.STRING, + readOnly = true + ) + ) + } + variablesWithType.addAll(customBuildParameters) + return Triple(envList, contextMap, tm) + } + /** * 对[customBuildEnv]的占位符进行替换, * 再追加env.前缀的构建机容器的上下文[context], @@ -333,14 +412,22 @@ class EngineVMBuildService @Autowired(required = false) constructor( private fun fillContainerContext( context: MutableMap, customBuildEnv: List?, - matrixContext: Map?, - asCodeEnabled: Boolean? + matrixContext: Map? ) { + val contextPair by lazy { + EnvReplacementParser.getCustomExecutionContextByMap(context) + } + val dialect = PipelineDialectUtil.getPipelineDialect(context[PIPELINE_DIALECT]) customBuildEnv?.let { context.putAll( - customBuildEnv.map { - "$ENV_CONTEXT_KEY_PREFIX${it.key}" to EnvReplacementParser.parse(it.value, context, asCodeEnabled) - }.toMap() + customBuildEnv.associate { + "$ENV_CONTEXT_KEY_PREFIX${it.key}" to EnvReplacementParser.parse( + value = it.value, + contextMap = context, + onlyExpression = dialect.supportUseExpression(), + contextPair = contextPair + ) + } ) } @@ -518,12 +605,7 @@ class EngineVMBuildService @Autowired(required = false) constructor( val task = allTasks.firstOrNull() ?: return BuildTask(buildId, vmSeqId, BuildTaskStatus.WAIT, buildInfo.executeCount) - return claim( - task = task, buildId = buildId, userId = task.starter, vmSeqId = vmSeqId, - asCodeEnabled = pipelineAsCodeService.asCodeEnabled( - task.projectId, task.pipelineId, buildId, buildInfo - ) == true - ) + return claim(task = task, buildId = buildId, userId = task.starter, vmSeqId = vmSeqId) } finally { containerIdLock.unlock() } @@ -533,8 +615,7 @@ class EngineVMBuildService @Autowired(required = false) constructor( task: PipelineBuildTask, buildId: String, userId: String, - vmSeqId: String, - asCodeEnabled: Boolean + vmSeqId: String ): BuildTask { LOG.info("ENGINE|$buildId|BC_ING|${task.projectId}|j($vmSeqId)|[${task.taskId}-${task.taskName}]") return when { @@ -573,7 +654,8 @@ class EngineVMBuildService @Autowired(required = false) constructor( atomCode = task.atomCode, executeCount = task.executeCount, actionType = ActionType.REFRESH, - buildStatus = task.status.name + buildStatus = task.status.name, + type = PipelineBuildStatusBroadCastEventType.BUILD_TASK_PAUSE ) ) BuildTask(buildId, vmSeqId, BuildTaskStatus.END, task.executeCount) @@ -627,7 +709,8 @@ class EngineVMBuildService @Autowired(required = false) constructor( userId = task.starter, buildId = buildId, taskId = task.taskId, actionType = ActionType.START, containerHashId = task.containerHashId, jobId = task.jobId, stageId = task.stageId, stepId = task.stepId, atomCode = task.atomCode, executeCount = task.executeCount, - buildStatus = BuildStatus.RUNNING.name + buildStatus = BuildStatus.RUNNING.name, + type = PipelineBuildStatusBroadCastEventType.BUILD_TASK_START ) ) val signToken = UUIDUtil.generate() @@ -642,6 +725,7 @@ class EngineVMBuildService @Autowired(required = false) constructor( expiredInSecond = transMinuteTimeoutToSec(task.additionalOptions?.timeout?.toInt()) ) } + val dialect = PipelineDialectUtil.getPipelineDialect(buildVariable[PIPELINE_DIALECT]) BuildTask( buildId = buildId, vmSeqId = vmSeqId, @@ -653,12 +737,14 @@ class EngineVMBuildService @Autowired(required = false) constructor( executeCount = task.executeCount, type = task.taskType, params = task.taskParams.map { - // 在pipeline as code模式下,此处直接保持原文传给worker - val obj = if (asCodeEnabled) { + // 表达式在worker端替换 + val obj = if (!dialect.supportUseExpression()) { + ObjectReplaceEnvVarUtil.replaceEnvVar( + it.value, buildVariable + ) + } else { it.value - } else ObjectReplaceEnvVarUtil.replaceEnvVar( - it.value, buildVariable - ) + } it.key to JsonUtil.toJson(obj, formatted = false) }.filter { !it.first.startsWith("@type") @@ -822,7 +908,7 @@ class EngineVMBuildService @Autowired(required = false) constructor( buildId = buildId, taskId = result.taskId, actionType = ActionType.END, containerHashId = task.containerHashId, jobId = task.jobId, stageId = task.stageId, stepId = task.stepId, atomCode = task.atomCode, executeCount = task.executeCount, - buildStatus = task.status.name + buildStatus = task.status.name, type = PipelineBuildStatusBroadCastEventType.BUILD_TASK_END ) ) } diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/utils/PipelineUtils.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/utils/PipelineUtils.kt index 176a3d38add..23925a1a9e0 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/utils/PipelineUtils.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/engine/utils/PipelineUtils.kt @@ -68,7 +68,8 @@ object PipelineUtils { logger.warn("Pipeline's start params[${param.id}] is illegal") throw OperationException( message = I18nUtil.getCodeLanMessage( - ProcessMessageCode.ERROR_PIPELINE_PARAMS_NAME_ERROR + ProcessMessageCode.ERROR_PIPELINE_PARAMS_NAME_ERROR, + params = arrayOf(param.id) ) ) } diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/BuildVariableService.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/BuildVariableService.kt index 5685895238d..0106da13cca 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/BuildVariableService.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/BuildVariableService.kt @@ -29,12 +29,14 @@ package com.tencent.devops.process.service import com.tencent.devops.common.api.util.TemplateFastReplaceUtils import com.tencent.devops.common.api.util.Watcher +import com.tencent.devops.common.pipeline.dialect.PipelineDialectUtil import com.tencent.devops.common.pipeline.enums.BuildFormPropertyType import com.tencent.devops.common.pipeline.pojo.BuildParameters import com.tencent.devops.common.redis.RedisOperation import com.tencent.devops.common.service.utils.LogUtils import com.tencent.devops.process.engine.control.lock.PipelineBuildVarLock import com.tencent.devops.process.engine.dao.PipelineBuildVarDao +import com.tencent.devops.process.utils.PIPELINE_DIALECT import com.tencent.devops.process.utils.PIPELINE_RETRY_COUNT import com.tencent.devops.process.utils.PipelineVarUtil import org.apache.commons.lang3.math.NumberUtils @@ -102,7 +104,8 @@ class BuildVariableService @Autowired constructor( buildId = buildId, keys = keys ) - return if (pipelineAsCodeService.asCodeEnabled(projectId, pipelineId, buildId, null) == true) { + val dialect = PipelineDialectUtil.getPipelineDialect(dataMap[PIPELINE_DIALECT]) + return if (dialect.supportUseExpression()) { dataMap } else { PipelineVarUtil.mixOldVarAndNewVar(dataMap) diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/PipelineAsCodeService.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/PipelineAsCodeService.kt index 0d123f51335..3c0987dd6a3 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/PipelineAsCodeService.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/PipelineAsCodeService.kt @@ -27,10 +27,11 @@ package com.tencent.devops.process.service -import com.tencent.devops.process.dao.PipelineSettingDao import com.tencent.devops.common.api.pojo.PipelineAsCodeSettings -import com.tencent.devops.process.engine.dao.PipelineBuildDao -import com.tencent.devops.process.engine.pojo.BuildInfo +import com.tencent.devops.common.pipeline.dialect.IPipelineDialect +import com.tencent.devops.common.pipeline.dialect.PipelineDialectType +import com.tencent.devops.common.pipeline.dialect.PipelineDialectUtil +import com.tencent.devops.process.dao.PipelineSettingDao import org.jooq.DSLContext import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Service @@ -39,31 +40,95 @@ import org.springframework.stereotype.Service class PipelineAsCodeService @Autowired constructor( private val dslContext: DSLContext, private val pipelineSettingDao: PipelineSettingDao, - private val pipelineBuildDao: PipelineBuildDao + private val projectCacheService: ProjectCacheService ) { fun asCodeEnabled( projectId: String, - pipelineId: String, - buildId: String, - buildInfo: BuildInfo? + pipelineId: String ): Boolean? { - return getPipelineAsCodeSettings(projectId, pipelineId, buildId, buildInfo)?.enable + return getPipelineAsCodeSettings(projectId, pipelineId)?.enable } + fun getPipelineAsCodeSettings(projectId: String, pipelineId: String): PipelineAsCodeSettings? { + val settings = pipelineSettingDao.getPipelineAsCodeSettings( + dslContext = dslContext, projectId = projectId, pipelineId = pipelineId + ) + return getPipelineAsCodeSettings(projectId = projectId, asCodeSettings = settings) + } + + /** + * 前端或者构建机请求时,构造PipelineAsCodeSettings,保证dialect一定有值 + * + * 1. 如果asCodeSettings为空,则使用项目方言 + * 2. 如果继承项目方言,则查询项目方言 + */ fun getPipelineAsCodeSettings( projectId: String, - pipelineId: String, - buildId: String, - buildInfo: BuildInfo? + asCodeSettings: PipelineAsCodeSettings? ): PipelineAsCodeSettings? { - val settings = pipelineSettingDao.getPipelineAsCodeSettings( - dslContext = dslContext, projectId = projectId, pipelineId = pipelineId + return when { + asCodeSettings == null -> { + val projectDialect = + projectCacheService.getProjectDialect(projectId) ?: PipelineDialectType.CLASSIC.name + PipelineAsCodeSettings(inheritedDialect = true, projectDialect = projectDialect) + } + asCodeSettings.inheritedDialect != false -> { + val projectDialect = + projectCacheService.getProjectDialect(projectId) ?: PipelineDialectType.CLASSIC.name + asCodeSettings.copy(projectDialect = projectDialect) + } + else -> + asCodeSettings + } + } + + /** + * 获取项目级方言 + */ + fun getProjectDialect(projectId: String): IPipelineDialect { + val projectDialect = + projectCacheService.getProjectDialect(projectId) ?: PipelineDialectType.CLASSIC.name + return PipelineDialectType.valueOf(projectDialect).dialect + } + + fun getPipelineDialect(projectId: String, pipelineId: String): IPipelineDialect { + val asCodeSettings = getPipelineAsCodeSettings(projectId = projectId, pipelineId = pipelineId) + return getPipelineDialect(projectId = projectId, asCodeSettings = asCodeSettings) + } + + /** + * 获取流水线方言,根据流水线设置 + */ + fun getPipelineDialect(projectId: String, asCodeSettings: PipelineAsCodeSettings?): IPipelineDialect { + return PipelineDialectUtil.getPipelineDialect( + getPipelineAsCodeSettings( + projectId = projectId, + asCodeSettings = asCodeSettings + ) ) -// val info = buildInfo ?: pipelineBuildDao.getBuildInfo( -// dslContext, projectId, pipelineId, buildId -// ) -// return settings?.copy(enable = info?.yamlVersion == YamlVersion.V3_0.tag) - return settings + } + + /** + * 获取流水线方言,根据流水线设置或者方言设置 + */ + fun getPipelineDialect( + projectId: String, + asCodeSettings: PipelineAsCodeSettings?, + inheritedDialectSetting: Boolean?, + pipelineDialectSetting: String? + ): IPipelineDialect { + val projectDialect = projectCacheService.getProjectDialect(projectId = projectId) + return if (asCodeSettings != null) { + PipelineDialectUtil.getPipelineDialect( + asCodeSettings.copy(projectDialect = projectDialect) + ) + } else { + PipelineDialectUtil.getPipelineDialect( + inheritedDialect = inheritedDialectSetting, + projectDialect = projectDialect, + pipelineDialect = pipelineDialectSetting + ) + } } } diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/ProjectCacheService.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/ProjectCacheService.kt index af82c466377..a52e95608c6 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/ProjectCacheService.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/ProjectCacheService.kt @@ -68,6 +68,10 @@ class ProjectCacheService @Autowired constructor(private val client: Client) { } } + fun getProjectDialect(projectId: String): String? { + return getProject(projectId = projectId)?.properties?.pipelineDialect + } + private fun getProjectInner(projectId: String): ProjectVO { return client.get(ServiceProjectResource::class).get(projectId).data ?: throw NotFoundException("Fail to find the project info of project($projectId)") diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/pipeline/PipelineBuildService.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/pipeline/PipelineBuildService.kt index 55b84711fe5..cffab2ac0c1 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/pipeline/PipelineBuildService.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/pipeline/PipelineBuildService.kt @@ -36,6 +36,8 @@ import com.tencent.devops.common.audit.ActionAuditContent import com.tencent.devops.common.auth.api.ActionId import com.tencent.devops.common.auth.api.ResourceTypeId import com.tencent.devops.common.pipeline.Model +import com.tencent.devops.common.pipeline.dialect.IPipelineDialect +import com.tencent.devops.common.pipeline.dialect.PipelineDialectUtil import com.tencent.devops.common.pipeline.enums.BuildFormPropertyType import com.tencent.devops.common.pipeline.enums.ChannelCode import com.tencent.devops.common.pipeline.enums.StartType @@ -54,6 +56,7 @@ import com.tencent.devops.process.engine.service.PipelineRepositoryService import com.tencent.devops.process.engine.service.PipelineRuntimeService import com.tencent.devops.process.pojo.BuildId import com.tencent.devops.process.pojo.app.StartBuildContext +import com.tencent.devops.process.service.PipelineAsCodeService import com.tencent.devops.process.service.ProjectCacheService import com.tencent.devops.process.util.BuildMsgUtils import com.tencent.devops.process.utils.BK_CI_AUTHORIZER @@ -64,6 +67,7 @@ import com.tencent.devops.process.utils.PIPELINE_BUILD_ID import com.tencent.devops.process.utils.PIPELINE_BUILD_MSG import com.tencent.devops.process.utils.PIPELINE_BUILD_URL import com.tencent.devops.process.utils.PIPELINE_CREATE_USER +import com.tencent.devops.process.utils.PIPELINE_DIALECT import com.tencent.devops.process.utils.PIPELINE_ID import com.tencent.devops.process.utils.PIPELINE_NAME import com.tencent.devops.process.utils.PIPELINE_RETRY_BUILD_ID @@ -81,6 +85,7 @@ import com.tencent.devops.process.utils.PIPELINE_START_USER_ID import com.tencent.devops.process.utils.PIPELINE_START_USER_NAME import com.tencent.devops.process.utils.PIPELINE_START_WEBHOOK_USER_ID import com.tencent.devops.process.utils.PIPELINE_UPDATE_USER +import com.tencent.devops.process.utils.PIPELINE_VARIABLES_STRING_LENGTH_MAX import com.tencent.devops.process.utils.PIPELINE_VERSION import com.tencent.devops.process.utils.PROJECT_NAME import com.tencent.devops.process.utils.PROJECT_NAME_CHINESE @@ -99,7 +104,8 @@ class PipelineBuildService( private val projectCacheService: ProjectCacheService, private val pipelineUrlBean: PipelineUrlBean, private val simpleRateLimiter: SimpleRateLimiter, - private val buildIdGenerator: BuildIdGenerator + private val buildIdGenerator: BuildIdGenerator, + private val pipelineAsCodeService: PipelineAsCodeService ) { companion object { private val NO_LIMIT_CHANNEL = listOf(ChannelCode.CODECC) @@ -160,7 +166,12 @@ class PipelineBuildService( ) } ?: pipelineRepositoryService.getSetting(pipeline.projectId, pipeline.pipelineId) } else { - pipelineRepositoryService.getSetting(pipeline.projectId, pipeline.pipelineId) + // webhook、重试可以指定流水线版本 + pipelineRepositoryService.getSettingByPipelineVersion( + projectId = pipeline.projectId, + pipelineId = pipeline.pipelineId, + pipelineVersion = signPipelineVersion + ) } val bucketSize = setting!!.maxConRunningQueueSize val lockKey = "PipelineRateLimit:${pipeline.pipelineId}" @@ -177,6 +188,12 @@ class PipelineBuildService( ) } } + val asCodeSettings = pipelineAsCodeService.getPipelineAsCodeSettings( + projectId = pipeline.projectId, + asCodeSettings = setting.pipelineAsCodeSettings + ) + val pipelineDialectType = + PipelineDialectUtil.getPipelineDialectType(channelCode = channelCode, asCodeSettings = asCodeSettings) // 如果指定了版本号,则设置指定的版本号 pipeline.version = signPipelineVersion ?: pipeline.version @@ -212,7 +229,8 @@ class PipelineBuildService( ) } else { null - } + }, + pipelineDialectType = pipelineDialectType.name ) val context = StartBuildContext.init( @@ -232,6 +250,11 @@ class PipelineBuildService( versionName = versionName, yamlVersion = yamlVersion ) + // 校验流水线启动变量长度 + checkBuildParameterLength( + pipelineDialect = pipelineDialectType.dialect, + buildParameters = context.buildParameters + ) val interceptResult = pipelineInterceptorChain.filter( InterceptData( @@ -275,7 +298,8 @@ class PipelineBuildService( channelCode: ChannelCode, isMobile: Boolean, debug: Boolean? = false, - pipelineAuthorizer: String? = null + pipelineAuthorizer: String? = null, + pipelineDialectType: String ) { val userName = when (startType) { StartType.PIPELINE -> pipelineParamMap[PIPELINE_START_PIPELINE_USER_ID]?.value @@ -366,6 +390,7 @@ class PipelineBuildService( ), readOnly = true ) + pipelineParamMap[PIPELINE_DIALECT] = BuildParameters(PIPELINE_DIALECT, pipelineDialectType, readOnly = true) // 自定义触发源材料信息 startValues?.get(BK_CI_MATERIAL_ID)?.let { pipelineParamMap[BK_CI_MATERIAL_ID] = BuildParameters( @@ -405,4 +430,19 @@ class PipelineBuildService( } // return originStartParams } + + private fun checkBuildParameterLength( + pipelineDialect: IPipelineDialect, + buildParameters: List + ) { + val longVarNames = buildParameters.filter { + it.value.toString().length >= PIPELINE_VARIABLES_STRING_LENGTH_MAX + }.map { it.key } + if (longVarNames.isNotEmpty() && !pipelineDialect.supportLongVarValue()) { + throw ErrorCodeException( + errorCode = ProcessMessageCode.ERROR_PIPELINE_VARIABLES_OUT_OF_LENGTH, + params = arrayOf(longVarNames.toString()) + ) + } + } } diff --git a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/pipeline/PipelineSettingVersionService.kt b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/pipeline/PipelineSettingVersionService.kt index bdc28ca5c69..18a7eefe58c 100644 --- a/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/pipeline/PipelineSettingVersionService.kt +++ b/src/backend/ci/core/process/biz-base/src/main/kotlin/com/tencent/devops/process/service/pipeline/PipelineSettingVersionService.kt @@ -38,6 +38,7 @@ import com.tencent.devops.process.dao.PipelineSettingDao import com.tencent.devops.process.dao.PipelineSettingVersionDao import com.tencent.devops.process.pojo.PipelineDetailInfo import com.tencent.devops.process.pojo.setting.PipelineSettingVersion +import com.tencent.devops.process.service.PipelineAsCodeService import com.tencent.devops.process.service.label.PipelineGroupService import com.tencent.devops.process.utils.PipelineVersionUtils import org.jooq.DSLContext @@ -51,7 +52,8 @@ class PipelineSettingVersionService @Autowired constructor( private val dslContext: DSLContext, private val pipelineGroupService: PipelineGroupService, private val pipelineSettingDao: PipelineSettingDao, - private val pipelineSettingVersionDao: PipelineSettingVersionDao + private val pipelineSettingVersionDao: PipelineSettingVersionDao, + private val pipelineAsCodeService: PipelineAsCodeService ) { /** @@ -133,6 +135,7 @@ class PipelineSettingVersionService @Autowired constructor( } else { null } + settingInfo.pipelineAsCodeSettings = ve.pipelineAsCodeSettings } // 来自前端的请求中,版本中的可能还不是正式生效的,如果和正式配置中有差异则重新获取名称 if (settingInfo.labels.isNotEmpty() && settingInfo.labels != labels && userId != null) { @@ -144,6 +147,10 @@ class PipelineSettingVersionService @Autowired constructor( } settingInfo.labelNames = labelNames } + settingInfo.pipelineAsCodeSettings = pipelineAsCodeService.getPipelineAsCodeSettings( + projectId = projectId, + settingInfo.pipelineAsCodeSettings + ) } return settingInfo diff --git a/src/backend/ci/core/process/biz-base/src/test/kotlin/com/tencent/devops/process/engine/extend/DefaultModelCheckPluginTest.kt b/src/backend/ci/core/process/biz-base/src/test/kotlin/com/tencent/devops/process/engine/extend/DefaultModelCheckPluginTest.kt index 06e4178fae4..ca38042d7c0 100644 --- a/src/backend/ci/core/process/biz-base/src/test/kotlin/com/tencent/devops/process/engine/extend/DefaultModelCheckPluginTest.kt +++ b/src/backend/ci/core/process/biz-base/src/test/kotlin/com/tencent/devops/process/engine/extend/DefaultModelCheckPluginTest.kt @@ -199,7 +199,7 @@ class DefaultModelCheckPluginTest : TestBase() { fun checkTriggerContainer() { // trigger val triggerStage = genStages(1, 1, 1) - checkPlugin.checkTriggerContainer(triggerStage[0]) + checkPlugin.checkTriggerContainer(triggerStage[0], false) } @Test diff --git a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/atom/TaskAtomService.kt b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/atom/TaskAtomService.kt index 7ffbce57255..ca877cc41e7 100644 --- a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/atom/TaskAtomService.kt +++ b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/atom/TaskAtomService.kt @@ -36,6 +36,7 @@ import com.tencent.devops.common.api.util.timestampmilli import com.tencent.devops.common.client.Client import com.tencent.devops.common.event.dispatcher.pipeline.PipelineEventDispatcher import com.tencent.devops.common.event.enums.ActionType +import com.tencent.devops.common.event.enums.PipelineBuildStatusBroadCastEventType import com.tencent.devops.common.event.pojo.pipeline.PipelineBuildStatusBroadCastEvent import com.tencent.devops.common.log.utils.BuildLogPrinter import com.tencent.devops.common.pipeline.enums.BuildStatus @@ -160,7 +161,12 @@ class TaskAtomService @Autowired(required = false) constructor( stepId = task.stepId, atomCode = task.atomCode, executeCount = task.executeCount, - buildStatus = task.status.name + buildStatus = task.status.name, + type = when (actionType) { + ActionType.START -> PipelineBuildStatusBroadCastEventType.BUILD_TASK_START + ActionType.END -> PipelineBuildStatusBroadCastEventType.BUILD_TASK_END + else -> null + } ) ) } diff --git a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/atom/vm/DispatchVMStartupTaskAtom.kt b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/atom/vm/DispatchVMStartupTaskAtom.kt index 595867b53bf..3aedb903f5f 100644 --- a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/atom/vm/DispatchVMStartupTaskAtom.kt +++ b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/atom/vm/DispatchVMStartupTaskAtom.kt @@ -42,6 +42,7 @@ import com.tencent.devops.common.pipeline.EnvReplacementParser import com.tencent.devops.common.pipeline.NameAndValue import com.tencent.devops.common.pipeline.container.Container import com.tencent.devops.common.pipeline.container.VMBuildContainer +import com.tencent.devops.common.pipeline.dialect.PipelineDialectUtil import com.tencent.devops.common.pipeline.enums.BuildStatus import com.tencent.devops.common.pipeline.type.agent.ThirdPartyAgentEnvDispatchType import com.tencent.devops.common.pipeline.type.agent.ThirdPartyAgentIDDispatchType @@ -65,8 +66,8 @@ import com.tencent.devops.process.engine.service.detail.ContainerBuildDetailServ import com.tencent.devops.process.engine.service.record.ContainerBuildRecordService import com.tencent.devops.process.pojo.mq.PipelineAgentShutdownEvent import com.tencent.devops.process.pojo.mq.PipelineAgentStartupEvent -import com.tencent.devops.process.service.PipelineAsCodeService import com.tencent.devops.process.service.PipelineContextService +import com.tencent.devops.process.utils.PIPELINE_DIALECT import com.tencent.devops.process.utils.BK_CI_AUTHORIZER import com.tencent.devops.store.api.container.ServiceContainerAppResource import org.slf4j.LoggerFactory @@ -92,7 +93,6 @@ class DispatchVMStartupTaskAtom @Autowired constructor( private val pipelineEventDispatcher: SampleEventDispatcher, private val buildLogPrinter: BuildLogPrinter, private val dispatchTypeBuilder: DispatchTypeBuilder, - private val pipelineAsCodeService: PipelineAsCodeService, private val pipelineContextService: PipelineContextService, private val pipelineTaskService: PipelineTaskService ) : IAtomTask { @@ -296,14 +296,11 @@ class DispatchVMStartupTaskAtom @Autowired constructor( ): Boolean { param.buildEnv?.let { buildEnv -> val asCode by lazy { - val asCodeSettings = pipelineAsCodeService.getPipelineAsCodeSettings( - task.projectId, task.pipelineId, task.buildId, null - ) - val asCodeEnabled = asCodeSettings?.enable == true - val contextPair = if (asCodeEnabled) { + val dialect = PipelineDialectUtil.getPipelineDialect(variables[PIPELINE_DIALECT]) + val contextPair = if (dialect.supportUseExpression()) { EnvReplacementParser.getCustomExecutionContextByMap(variables) } else null - Pair(asCodeEnabled, contextPair) + Pair(dialect, contextPair) } buildEnv.forEach { env -> if (!env.value.startsWith("$")) { @@ -312,7 +309,7 @@ class DispatchVMStartupTaskAtom @Autowired constructor( val version = EnvReplacementParser.parse( value = env.value, contextMap = variables, - onlyExpression = asCode.first, + onlyExpression = asCode.first.supportUseExpression(), contextPair = asCode.second ) val res = client.get(ServiceContainerAppResource::class).getBuildEnv( diff --git a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/BuildEndControl.kt b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/BuildEndControl.kt index 8673e56f61b..0606c9ff87a 100644 --- a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/BuildEndControl.kt +++ b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/BuildEndControl.kt @@ -53,6 +53,7 @@ import com.tencent.devops.common.redis.RedisOperation import com.tencent.devops.common.service.prometheus.BkTimed import com.tencent.devops.common.service.utils.LogUtils import com.tencent.devops.common.event.dispatcher.pipeline.PipelineEventDispatcher +import com.tencent.devops.common.event.enums.PipelineBuildStatusBroadCastEventType import com.tencent.devops.common.websocket.enum.RefreshType import com.tencent.devops.process.constant.ProcessMessageCode import com.tencent.devops.process.engine.common.VMUtils @@ -258,7 +259,8 @@ class BuildEndControl @Autowired constructor( buildId = buildId, actionType = ActionType.END, buildStatus = buildStatus.name, - executeCount = buildInfo.executeCount + executeCount = buildInfo.executeCount, + type = PipelineBuildStatusBroadCastEventType.BUILD_END ), PipelineBuildWebSocketPushEvent( source = "pauseTask", diff --git a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/BuildMonitorControl.kt b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/BuildMonitorControl.kt index 25e42006e2d..7a584631959 100644 --- a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/BuildMonitorControl.kt +++ b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/BuildMonitorControl.kt @@ -89,6 +89,7 @@ class BuildMonitorControl @Autowired constructor( ) { companion object { + const val START_EVENT_SOURCE = "start_monitor" private const val TAG = "startVM-0" private const val JOB_ID = "0" private val LOG = LoggerFactory.getLogger(BuildMonitorControl::class.java) @@ -425,11 +426,12 @@ class BuildMonitorControl @Autowired constructor( private fun monitorQueueBuild(event: PipelineBuildMonitorEvent, buildInfo: BuildInfo): Boolean { // 判断是否超时 if (pipelineSettingService.isQueueTimeout(event.projectId, event.pipelineId, buildInfo.queueTime)) { - val exitQueue = pipelineRuntimeExtService.existQueue( + val exitQueue = pipelineRuntimeExtService.changeBuildStatus( projectId = event.projectId, pipelineId = event.pipelineId, buildId = event.buildId, - buildStatus = buildInfo.status + oldBuildStatus = buildInfo.status, + newBuildStatus = BuildStatus.UNEXEC ) LOG.info("ENGINE|${event.buildId}|BUILD_QUEUE_MONITOR_TIMEOUT|queue timeout|exitQueue=$exitQueue") val errorInfo = I18nUtil.generateResponseDataObject( @@ -492,7 +494,7 @@ class BuildMonitorControl @Autowired constructor( val triggerContainer = model.getTriggerContainer() pipelineEventDispatcher.dispatch( PipelineBuildStartEvent( - source = "start_monitor", + source = START_EVENT_SOURCE, projectId = buildInfo.projectId, pipelineId = buildInfo.pipelineId, userId = buildInfo.startUser, diff --git a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/BuildStartControl.kt b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/BuildStartControl.kt index 405c00fb995..fe5ce51f694 100644 --- a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/BuildStartControl.kt +++ b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/BuildStartControl.kt @@ -31,7 +31,9 @@ import com.tencent.devops.common.api.enums.RepositoryConfig import com.tencent.devops.common.api.util.EnvUtils import com.tencent.devops.common.api.util.Watcher import com.tencent.devops.common.api.util.timestampmilli +import com.tencent.devops.common.event.dispatcher.pipeline.PipelineEventDispatcher import com.tencent.devops.common.event.enums.ActionType +import com.tencent.devops.common.event.enums.PipelineBuildStatusBroadCastEventType import com.tencent.devops.common.event.pojo.pipeline.PipelineBuildStartBroadCastEvent import com.tencent.devops.common.event.pojo.pipeline.PipelineBuildStatusBroadCastEvent import com.tencent.devops.common.log.utils.BuildLogPrinter @@ -55,7 +57,6 @@ import com.tencent.devops.common.pipeline.pojo.time.BuildTimestampType import com.tencent.devops.common.pipeline.utils.PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX import com.tencent.devops.common.pipeline.utils.RepositoryConfigUtils import com.tencent.devops.common.redis.RedisOperation -import com.tencent.devops.common.event.dispatcher.pipeline.PipelineEventDispatcher import com.tencent.devops.common.service.prometheus.BkTimed import com.tencent.devops.common.service.utils.LogUtils import com.tencent.devops.common.web.utils.I18nUtil @@ -94,11 +95,11 @@ import com.tencent.devops.process.utils.PIPELINE_TIME_START import com.tencent.devops.process.utils.PipelineVarUtil import io.micrometer.core.instrument.Counter import io.micrometer.core.instrument.MeterRegistry +import java.time.LocalDateTime +import kotlin.math.max import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Service -import java.time.LocalDateTime -import kotlin.math.max /** * 构建控制器 @@ -231,23 +232,51 @@ class BuildStartControl @Autowired constructor( val setting = pipelineRepositoryService.getSetting(projectId, pipelineId) if (setting?.runLockType == PipelineRunLockType.MULTIPLE) { - canStart = checkRunningCountWithLimit( - buildInfo = buildInfo, - setting = setting, - executeCount = executeCount, - limitCount = setting.maxConRunningQueueSize ?: PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX - ) + if (buildInfo.status != BuildStatus.QUEUE_CACHE) { + // 并发时不按排队队列领取,直接更改状态为队列待处理 + canStart = pipelineRuntimeExtService.changeBuildStatus( + projectId = projectId, + pipelineId = pipelineId, + buildId = buildId, + oldBuildStatus = BuildStatus.QUEUE, + newBuildStatus = BuildStatus.QUEUE_CACHE + ) + } + if (canStart) { + canStart = checkRunningCountWithLimit( + setting = setting, + executeCount = executeCount, + limitCount = setting.maxConRunningQueueSize ?: PIPELINE_SETTING_MAX_CON_QUEUE_SIZE_MAX + ) + } else { + buildLogPrinter.addLine( + message = "Waiting build #${buildInfo.buildNum - 1}", + buildId = buildId, tag = TAG, containerHashId = JOB_ID, executeCount = executeCount, + jobId = null, stepId = TAG + ) + } } // #4074 LOCK 不会进入到这里,在启动API已经拦截 if (setting?.runLockType == PipelineRunLockType.SINGLE || setting?.runLockType == PipelineRunLockType.SINGLE_LOCK ) { - canStart = checkRunningCountWithLimit( - buildInfo = buildInfo, - setting = setting, - executeCount = executeCount, - limitCount = 1 - ) + if (buildInfo.status != BuildStatus.QUEUE_CACHE) { + // #4074 锁定当前构建是队列中第一个排队待执行的 + canStart = pipelineRuntimeExtService.queueCanPend2Start(projectId, pipelineId, buildId = buildId) + } + if (canStart) { + canStart = checkRunningCountWithLimit( + setting = setting, + executeCount = executeCount, + limitCount = 1 + ) + } else { + buildLogPrinter.addLine( + message = "Waiting build #${buildInfo.buildNum - 1}", + buildId = buildId, tag = TAG, containerHashId = JOB_ID, executeCount = executeCount, + jobId = null, stepId = TAG + ) + } } if (setting?.runLockType == PipelineRunLockType.GROUP_LOCK) { @@ -279,6 +308,8 @@ class BuildStartControl @Autowired constructor( ) ) broadcastStartEvent(buildInfo) + } else { + broadcastQueueEvent() } } finally { pipelineBuildLock.unlock() @@ -385,47 +416,32 @@ class BuildStartControl @Autowired constructor( } private fun PipelineBuildStartEvent.checkRunningCountWithLimit( - buildInfo: BuildInfo, setting: PipelineSetting, executeCount: Int, limitCount: Int ): Boolean { - // #4074 锁定当前构建是队列中第一个排队待执行的 - var checkStart = true - if (buildInfo.status != BuildStatus.QUEUE_CACHE) { - checkStart = pipelineRuntimeExtService.queueCanPend2Start(projectId, pipelineId, buildId = buildId) - } - if (checkStart) { - val runningCount = pipelineRuntimeService.getRunningBuildCount(projectId, pipelineId) - - if (runningCount >= limitCount) { - // 需要重新入队等待 - pipelineRuntimeService.updateBuildInfoStatus2Queue( - projectId = projectId, buildId = buildId, oldStatus = BuildStatus.QUEUE_CACHE, - showMsg = I18nUtil.getCodeLanMessage( - messageCode = BUILD_QUEUE_FOR_SINGLE, - defaultMessage = "QUEUE: The current build is queued" - ) + val runningCount = pipelineRuntimeService.getRunningBuildCount(projectId, pipelineId) + if (runningCount >= limitCount) { + // 需要重新入队等待 + pipelineRuntimeService.updateBuildInfoStatus2Queue( + projectId = projectId, buildId = buildId, oldStatus = BuildStatus.QUEUE_CACHE, + showMsg = I18nUtil.getCodeLanMessage( + messageCode = BUILD_QUEUE_FOR_SINGLE, + defaultMessage = "QUEUE: The current build is queued" ) + ) - buildLogPrinter.addLine( - message = I18nUtil.getCodeLanMessage( - messageCode = ProcessMessageCode.BK_BUILD_QUEUE_WAIT, - params = arrayOf(setting.runLockType.name, runningCount.toString()) - ), - buildId = buildId, tag = TAG, containerHashId = JOB_ID, executeCount = executeCount, - jobId = null, stepId = TAG - ) - checkStart = false - } - } else { buildLogPrinter.addLine( - message = "Waiting build #${buildInfo.buildNum - 1}", + message = I18nUtil.getCodeLanMessage( + messageCode = ProcessMessageCode.BK_BUILD_QUEUE_WAIT, + params = arrayOf(setting.runLockType.name, runningCount.toString()) + ), buildId = buildId, tag = TAG, containerHashId = JOB_ID, executeCount = executeCount, jobId = null, stepId = TAG ) + return false } - return checkStart + return true } /** @@ -502,7 +518,29 @@ class BuildStartControl @Autowired constructor( buildId = buildId, actionType = ActionType.START, executeCount = executeCount, - buildStatus = BuildStatus.RUNNING.name + buildStatus = BuildStatus.RUNNING.name, + type = PipelineBuildStatusBroadCastEventType.BUILD_START + ) + ) + } + + private fun PipelineBuildStartEvent.broadcastQueueEvent() { + /*暂不重复发送排队的PipelineBuildStatusBroadCastEvent,仅首次的时候发送即可。*/ + if (this.source == BuildMonitorControl.START_EVENT_SOURCE) { + return + } + pipelineEventDispatcher.dispatch( + // build 排队 + PipelineBuildStatusBroadCastEvent( + source = source, + projectId = projectId, + pipelineId = pipelineId, + userId = userId, + buildId = buildId, + actionType = ActionType.START, + executeCount = executeCount, + buildStatus = BuildStatus.QUEUE.name, + type = PipelineBuildStatusBroadCastEventType.BUILD_QUEUE ) ) } diff --git a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/ContainerControl.kt b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/ContainerControl.kt index 6d8740c287d..281eb2cd48d 100644 --- a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/ContainerControl.kt +++ b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/ContainerControl.kt @@ -177,7 +177,7 @@ class ContainerControl @Autowired constructor( stageId = stageId, onlyMatrixGroup = true ) - val pipelineAsCodeEnabled = pipelineAsCodeService.asCodeEnabled(projectId, pipelineId, buildId, buildInfo) + val pipelineAsCodeEnabled = pipelineAsCodeService.asCodeEnabled(projectId, pipelineId) val context = ContainerContext( buildStatus = this.status, // 初始状态为容器状态,中间流转会切换状态,并最终赋值给该容器状态 diff --git a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/StageControl.kt b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/StageControl.kt index e68e4456530..ee96cfd4070 100644 --- a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/StageControl.kt +++ b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/StageControl.kt @@ -140,7 +140,7 @@ class StageControl @Autowired constructor( containsMatrix = false ) val executeCount = buildVariableService.getBuildExecuteCount(projectId, pipelineId, buildId) - val pipelineAsCodeEnabled = pipelineAsCodeService.asCodeEnabled(projectId, pipelineId, buildId, buildInfo) + val pipelineAsCodeEnabled = pipelineAsCodeService.asCodeEnabled(projectId, pipelineId) // #10082 过滤Agent复用互斥的endJob信息 val mutexJobs = containers.filter { it.controlOption.agentReuseMutex?.endJob == true && diff --git a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/container/impl/ContainerCmdLoop.kt b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/container/impl/ContainerCmdLoop.kt index ecdc00716cc..65379380419 100644 --- a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/container/impl/ContainerCmdLoop.kt +++ b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/container/impl/ContainerCmdLoop.kt @@ -28,6 +28,10 @@ package com.tencent.devops.process.engine.control.command.container.impl import com.tencent.devops.common.event.dispatcher.pipeline.PipelineEventDispatcher +import com.tencent.devops.common.event.enums.ActionType +import com.tencent.devops.common.event.enums.PipelineBuildStatusBroadCastEventType +import com.tencent.devops.common.event.pojo.pipeline.PipelineBuildStatusBroadCastEvent +import com.tencent.devops.common.pipeline.enums.BuildStatus import com.tencent.devops.process.engine.control.command.CmdFlowState import com.tencent.devops.process.engine.control.command.container.ContainerCmd import com.tencent.devops.process.engine.control.command.container.ContainerContext @@ -61,6 +65,24 @@ class ContainerCmdLoop( commandContext.latestSummary == "agent_reuse_mutex_print" ) { commandContext.cmdFlowState = CmdFlowState.FINALLY + with(commandContext.container) { + pipelineEventDispatcher.dispatch( + PipelineBuildStatusBroadCastEvent( + source = "container-queue-loop-$containerId", projectId = projectId, + pipelineId = pipelineId, userId = commandContext.event.userId, + buildId = buildId, taskId = null, actionType = ActionType.START, + containerHashId = containerHashId, jobId = jobId, stageId = null, + stepId = null, atomCode = null, executeCount = executeCount, + buildStatus = BuildStatus.QUEUE.name, + type = PipelineBuildStatusBroadCastEventType.BUILD_JOB_QUEUE, + labels = mapOf( + "latestSummary" to commandContext.latestSummary, + "mutexGroup" to (controlOption.mutexGroup?.runtimeMutexGroup ?: ""), + "agentReuseMutex" to (controlOption.agentReuseMutex?.runtimeAgentOrEnvId ?: "") + ) + ) + ) + } } } } diff --git a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/container/impl/InitializeMatrixGroupStageCmd.kt b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/container/impl/InitializeMatrixGroupStageCmd.kt index 6e0b4dee045..4ca56c8c632 100644 --- a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/container/impl/InitializeMatrixGroupStageCmd.kt +++ b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/container/impl/InitializeMatrixGroupStageCmd.kt @@ -37,6 +37,7 @@ import com.tencent.devops.common.pipeline.EnvReplacementParser import com.tencent.devops.common.pipeline.NameAndValue import com.tencent.devops.common.pipeline.container.NormalContainer import com.tencent.devops.common.pipeline.container.VMBuildContainer +import com.tencent.devops.common.pipeline.dialect.PipelineDialectUtil import com.tencent.devops.common.pipeline.enums.BuildStatus import com.tencent.devops.common.pipeline.matrix.MatrixConfig import com.tencent.devops.common.pipeline.option.JobControlOption @@ -69,6 +70,7 @@ import com.tencent.devops.process.pojo.TemplateAcrossInfoType import com.tencent.devops.process.pojo.pipeline.record.BuildRecordContainer import com.tencent.devops.process.pojo.pipeline.record.BuildRecordTask import com.tencent.devops.process.service.PipelineBuildTemplateAcrossInfoService +import com.tencent.devops.process.utils.PIPELINE_DIALECT import com.tencent.devops.process.utils.PIPELINE_MATRIX_CON_RUNNING_SIZE_MAX import com.tencent.devops.process.utils.PIPELINE_MATRIX_MAX_CON_RUNNING_SIZE_DEFAULT import com.tencent.devops.process.utils.PIPELINE_STAGE_CONTAINERS_COUNT_MAX @@ -177,7 +179,6 @@ class InitializeMatrixGroupStageCmd( val event = commandContext.event val variables = commandContext.variables - val asCodeEnabled = commandContext.pipelineAsCodeEnabled ?: false val modelStage = containerBuildDetailService.getBuildModel( projectId = parentContainer.projectId, buildId = parentContainer.buildId @@ -197,6 +198,7 @@ class InitializeMatrixGroupStageCmd( containerId = parentContainer.containerId, executeCount = parentContainer.executeCount ) + val dialect = PipelineDialectUtil.getPipelineDialect(variables[PIPELINE_DIALECT]) // #4518 待生成的分裂后container表和task表记录 val buildContainerList = mutableListOf() val buildTaskList = mutableListOf() @@ -215,8 +217,8 @@ class InitializeMatrixGroupStageCmd( LOG.info( "ENGINE|${event.buildId}|${event.source}|INIT_MATRIX_CONTAINER|${event.stageId}|" + - "matrixGroupId=$matrixGroupId|containerHashId=${modelContainer.containerHashId}" + - "|context=$context|asCodeEnabled=$asCodeEnabled" + "matrixGroupId=$matrixGroupId|containerHashId=${modelContainer.containerHashId}" + + "|context=$context|dialect=${variables[PIPELINE_DIALECT]}" ) val matrixOption: MatrixControlOption @@ -238,7 +240,7 @@ class InitializeMatrixGroupStageCmd( dependOnContainerId2JobIds = null ) matrixOption = checkAndFetchOption(modelContainer.matrixControlOption) - matrixConfig = matrixOption.convertMatrixConfig(variables, asCodeEnabled) + matrixConfig = matrixOption.convertMatrixConfig(variables) contextCaseList = matrixConfig.getAllCombinations() if (contextCaseList.size > MATRIX_CASE_MAX_COUNT) { @@ -255,10 +257,9 @@ class InitializeMatrixGroupStageCmd( if (!it.key.isNullOrBlank()) customBuildEnv[it.key!!] = it.value ?: "" } val allContext = customBuildEnv.plus(contextCase) - val contextPair = if (asCodeEnabled) { + val contextPair = if (dialect.supportUseExpression()) { EnvReplacementParser.getCustomExecutionContextByMap(allContext) } else null - // 对自定义构建环境的做特殊解析 // customDispatchType决定customBaseOS是否计算,请勿填充默认值 val parsedInfo = matrixOption.customDispatchInfo?.let { self -> @@ -276,9 +277,17 @@ class InitializeMatrixGroupStageCmd( val mutexGroup = modelContainer.mutexGroup?.let { self -> self.copy( mutexGroupName = EnvReplacementParser.parse( - self.mutexGroupName, allContext, asCodeEnabled, contextPair + value = self.mutexGroupName, + contextMap = allContext, + onlyExpression = dialect.supportUseExpression(), + contextPair = contextPair ), - linkTip = EnvReplacementParser.parse(self.linkTip, allContext, asCodeEnabled, contextPair) + linkTip = EnvReplacementParser.parse( + value = self.linkTip, + contextMap = allContext, + onlyExpression = dialect.supportUseExpression(), + contextPair = contextPair + ) ) } val newSeq = context.containerSeq++ @@ -290,7 +299,12 @@ class InitializeMatrixGroupStageCmd( modelContainer.elements, context.executeCount, postParentIdMap, matrixTaskIds ) val newContainer = VMBuildContainer( - name = EnvReplacementParser.parse(modelContainer.name, allContext, asCodeEnabled, contextPair), + name = EnvReplacementParser.parse( + value = modelContainer.name, + contextMap = allContext, + onlyExpression = dialect.supportUseExpression(), + contextPair = contextPair + ), id = newSeq.toString(), containerId = newSeq.toString(), containerHashId = modelContainerIdGenerator.getNextId(), @@ -315,13 +329,28 @@ class InitializeMatrixGroupStageCmd( }, buildEnv = buildEnv ?: modelContainer.buildEnv, thirdPartyAgentId = modelContainer.thirdPartyAgentId?.let { self -> - EnvReplacementParser.parse(self, allContext, asCodeEnabled, contextPair) + EnvReplacementParser.parse( + value = self, + contextMap = allContext, + onlyExpression = dialect.supportUseExpression(), + contextPair = contextPair + ) }, thirdPartyAgentEnvId = modelContainer.thirdPartyAgentEnvId?.let { self -> - EnvReplacementParser.parse(self, allContext, asCodeEnabled, contextPair) + EnvReplacementParser.parse( + value = self, + contextMap = allContext, + onlyExpression = dialect.supportUseExpression(), + contextPair = contextPair + ) }, thirdPartyWorkspace = modelContainer.thirdPartyWorkspace?.let { self -> - EnvReplacementParser.parse(self, allContext, asCodeEnabled, contextPair) + EnvReplacementParser.parse( + value = self, + contextMap = allContext, + onlyExpression = dialect.supportUseExpression(), + contextPair = contextPair + ) } ) newContainer.jobId?.let { matrixJobIds.add(it) } @@ -397,7 +426,7 @@ class InitializeMatrixGroupStageCmd( dependOnContainerId2JobIds = null ) matrixOption = checkAndFetchOption(modelContainer.matrixControlOption) - matrixConfig = matrixOption.convertMatrixConfig(variables, asCodeEnabled) + matrixConfig = matrixOption.convertMatrixConfig(variables) contextCaseList = matrixConfig.getAllCombinations() contextCaseList.forEach { contextCase -> @@ -411,22 +440,30 @@ class InitializeMatrixGroupStageCmd( val statusElements = generateMatrixElements( modelContainer.elements, context.executeCount, postParentIdMap, matrixTaskIds ) - val replacement = if (asCodeEnabled) { - EnvReplacementParser.getCustomExecutionContextByMap(contextCase) - } else null + val replacement = EnvReplacementParser.getCustomExecutionContextByMap(contextCase) val mutexGroup = modelContainer.mutexGroup?.let { self -> self.copy( mutexGroupName = EnvReplacementParser.parse( value = self.mutexGroupName, contextMap = contextCase, - onlyExpression = asCodeEnabled, + onlyExpression = dialect.supportUseExpression(), contextPair = replacement ), - linkTip = EnvReplacementParser.parse(self.linkTip, contextCase, asCodeEnabled, replacement) + linkTip = EnvReplacementParser.parse( + value = self.linkTip, + contextMap = contextCase, + onlyExpression = dialect.supportUseExpression(), + contextPair = replacement + ) ) } val newContainer = NormalContainer( - name = EnvReplacementParser.parse(modelContainer.name, contextCase, asCodeEnabled, replacement), + name = EnvReplacementParser.parse( + value = modelContainer.name, + contextMap = contextCase, + onlyExpression = dialect.supportUseExpression(), + contextPair = replacement + ), id = newSeq.toString(), containerId = newSeq.toString(), containerHashId = modelContainerIdGenerator.getNextId(), diff --git a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/container/impl/StartActionTaskContainerCmd.kt b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/container/impl/StartActionTaskContainerCmd.kt index 0b68d8da031..177f45ca6fe 100644 --- a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/container/impl/StartActionTaskContainerCmd.kt +++ b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/container/impl/StartActionTaskContainerCmd.kt @@ -30,6 +30,7 @@ package com.tencent.devops.process.engine.control.command.container.impl import com.tencent.devops.common.api.pojo.ErrorType import com.tencent.devops.common.event.dispatcher.pipeline.PipelineEventDispatcher import com.tencent.devops.common.event.enums.ActionType +import com.tencent.devops.common.event.enums.PipelineBuildStatusBroadCastEventType import com.tencent.devops.common.event.pojo.pipeline.PipelineBuildStatusBroadCastEvent import com.tencent.devops.common.expression.ExpressionParseException import com.tencent.devops.common.log.utils.BuildLogPrinter @@ -135,7 +136,8 @@ class StartActionTaskContainerCmd( jobId = jobId, stepId = null, executeCount = executeCount, - buildStatus = commandContext.buildStatus.name + buildStatus = commandContext.buildStatus.name, + type = PipelineBuildStatusBroadCastEventType.BUILD_JOB_START ) ) } diff --git a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/container/impl/UpdateStateContainerCmdFinally.kt b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/container/impl/UpdateStateContainerCmdFinally.kt index 440ddd189ea..33f32cf58e7 100644 --- a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/container/impl/UpdateStateContainerCmdFinally.kt +++ b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/container/impl/UpdateStateContainerCmdFinally.kt @@ -29,6 +29,7 @@ package com.tencent.devops.process.engine.control.command.container.impl import com.tencent.devops.common.event.dispatcher.pipeline.PipelineEventDispatcher import com.tencent.devops.common.event.enums.ActionType +import com.tencent.devops.common.event.enums.PipelineBuildStatusBroadCastEventType import com.tencent.devops.common.event.pojo.pipeline.PipelineBuildStatusBroadCastEvent import com.tencent.devops.common.log.utils.BuildLogPrinter import com.tencent.devops.common.pipeline.enums.BuildStatus @@ -114,7 +115,8 @@ class UpdateStateContainerCmdFinally( jobId = commandContext.container.jobId, stepId = null, executeCount = executeCount, - buildStatus = commandContext.buildStatus.name + buildStatus = commandContext.buildStatus.name, + type = PipelineBuildStatusBroadCastEventType.BUILD_JOB_END ) ) } @@ -243,7 +245,8 @@ class UpdateStateContainerCmdFinally( jobId = commandContext.container.jobId, stepId = null, executeCount = executeCount, - buildStatus = commandContext.buildStatus.name + buildStatus = commandContext.buildStatus.name, + type = PipelineBuildStatusBroadCastEventType.BUILD_JOB_END ) ) diff --git a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/stage/impl/CheckPauseReviewStageCmd.kt b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/stage/impl/CheckPauseReviewStageCmd.kt index 5c6660e8245..9dd229e668b 100644 --- a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/stage/impl/CheckPauseReviewStageCmd.kt +++ b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/stage/impl/CheckPauseReviewStageCmd.kt @@ -29,6 +29,7 @@ package com.tencent.devops.process.engine.control.command.stage.impl import com.tencent.devops.common.auth.api.AuthProjectApi import com.tencent.devops.common.auth.code.PipelineAuthServiceCode +import com.tencent.devops.common.pipeline.dialect.PipelineDialectUtil import com.tencent.devops.common.pipeline.enums.BuildStatus import com.tencent.devops.common.pipeline.pojo.StagePauseCheck import com.tencent.devops.process.engine.common.BS_MANUAL_START_STAGE @@ -42,6 +43,7 @@ import com.tencent.devops.process.engine.pojo.event.PipelineBuildStageEvent import com.tencent.devops.process.engine.service.PipelineStageService import com.tencent.devops.process.service.BuildVariableService import com.tencent.devops.process.utils.PIPELINE_BUILD_NUM +import com.tencent.devops.process.utils.PIPELINE_DIALECT import com.tencent.devops.process.utils.PIPELINE_NAME import com.tencent.devops.process.utils.PIPELINE_START_USER_NAME import org.slf4j.LoggerFactory @@ -96,7 +98,8 @@ class CheckPauseReviewStageCmd( // #3742 进入暂停状态则刷新完状态后直接返回,等待手动触发 LOG.info("ENGINE|${event.buildId}|${event.source}|STAGE_PAUSE|${event.stageId}") - stage.checkIn?.parseReviewVariables(commandContext.variables, commandContext.pipelineAsCodeEnabled) + val dialect = PipelineDialectUtil.getPipelineDialect(commandContext.variables[PIPELINE_DIALECT]) + stage.checkIn?.parseReviewVariables(commandContext.variables, dialect = dialect) if (stage.checkIn != null) { checkReviewGroup(event.projectId, stage.checkIn!!) } diff --git a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/stage/impl/StartContainerStageCmd.kt b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/stage/impl/StartContainerStageCmd.kt index ecd9a680b1e..08f0ee581cc 100644 --- a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/stage/impl/StartContainerStageCmd.kt +++ b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/stage/impl/StartContainerStageCmd.kt @@ -29,6 +29,7 @@ package com.tencent.devops.process.engine.control.command.stage.impl import com.tencent.devops.common.event.dispatcher.pipeline.PipelineEventDispatcher import com.tencent.devops.common.event.enums.ActionType +import com.tencent.devops.common.event.enums.PipelineBuildStatusBroadCastEventType import com.tencent.devops.common.event.pojo.pipeline.PipelineBuildStatusBroadCastEvent import com.tencent.devops.common.pipeline.container.AgentReuseMutex import com.tencent.devops.common.pipeline.enums.BuildStatus @@ -120,7 +121,8 @@ class StartContainerStageCmd( actionType = ActionType.START, stageId = commandContext.stage.stageId, executeCount = commandContext.executeCount, - buildStatus = commandContext.buildStatus.name + buildStatus = commandContext.buildStatus.name, + type = PipelineBuildStatusBroadCastEventType.BUILD_STAGE_START ) ) } diff --git a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/stage/impl/UpdateStateForStageCmdFinally.kt b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/stage/impl/UpdateStateForStageCmdFinally.kt index 8e9959ced81..d647384cc92 100644 --- a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/stage/impl/UpdateStateForStageCmdFinally.kt +++ b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/control/command/stage/impl/UpdateStateForStageCmdFinally.kt @@ -29,6 +29,7 @@ package com.tencent.devops.process.engine.control.command.stage.impl import com.tencent.devops.common.event.dispatcher.pipeline.PipelineEventDispatcher import com.tencent.devops.common.event.enums.ActionType +import com.tencent.devops.common.event.enums.PipelineBuildStatusBroadCastEventType import com.tencent.devops.common.event.pojo.pipeline.PipelineBuildStatusBroadCastEvent import com.tencent.devops.common.log.utils.BuildLogPrinter import com.tencent.devops.common.pipeline.enums.BuildStatus @@ -116,7 +117,8 @@ class UpdateStateForStageCmdFinally( PipelineBuildStatusBroadCastEvent( source = "UpdateStateForStageCmdFinally", projectId = stage.projectId, pipelineId = stage.pipelineId, userId = event.userId, buildId = stage.buildId, stageId = stage.stageId, actionType = ActionType.END, - buildStatus = commandContext.buildStatus.name, executeCount = stage.executeCount + buildStatus = commandContext.buildStatus.name, executeCount = stage.executeCount, + type = PipelineBuildStatusBroadCastEventType.BUILD_STAGE_END ) ) } diff --git a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/listener/run/PipelineTaskPauseListener.kt b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/listener/run/PipelineTaskPauseListener.kt index 9c7915f9bd1..49d35a713cf 100644 --- a/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/listener/run/PipelineTaskPauseListener.kt +++ b/src/backend/ci/core/process/biz-engine/src/main/kotlin/com/tencent/devops/process/engine/listener/run/PipelineTaskPauseListener.kt @@ -36,6 +36,7 @@ import com.tencent.devops.common.pipeline.enums.BuildStatus import com.tencent.devops.common.pipeline.pojo.element.Element import com.tencent.devops.common.redis.RedisOperation import com.tencent.devops.common.event.dispatcher.pipeline.PipelineEventDispatcher +import com.tencent.devops.common.event.enums.PipelineBuildStatusBroadCastEventType import com.tencent.devops.process.engine.common.BS_ATOM_STATUS_REFRESH_DELAY_MILLS import com.tencent.devops.process.engine.common.BS_MANUAL_STOP_PAUSE_ATOM import com.tencent.devops.process.engine.common.VMUtils @@ -232,7 +233,8 @@ class PipelineTaskPauseListener @Autowired constructor( stepId = task.stepId, atomCode = task.atomCode, executeCount = task.executeCount, - buildStatus = BuildStatus.CANCELED.name + buildStatus = BuildStatus.CANCELED.name, + type = PipelineBuildStatusBroadCastEventType.BUILD_TASK_END ) ) } diff --git a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/api/ServicePipelineVersionResourceImpl.kt b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/api/ServicePipelineVersionResourceImpl.kt index a328b4b778b..757e4ac7204 100644 --- a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/api/ServicePipelineVersionResourceImpl.kt +++ b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/api/ServicePipelineVersionResourceImpl.kt @@ -37,6 +37,7 @@ import com.tencent.devops.common.auth.api.AuthResourceType import com.tencent.devops.common.pipeline.PipelineVersionWithModel import com.tencent.devops.common.pipeline.PipelineVersionWithModelRequest import com.tencent.devops.common.pipeline.enums.PipelineStorageType +import com.tencent.devops.common.pipeline.pojo.BuildNoUpdateReq import com.tencent.devops.common.pipeline.pojo.TemplateInstanceCreateRequest import com.tencent.devops.common.web.RestResource import com.tencent.devops.common.web.utils.I18nUtil @@ -451,6 +452,21 @@ class ServicePipelineVersionResourceImpl @Autowired constructor( ) } + override fun updateBuildNo( + userId: String, + projectId: String, + pipelineId: String, + buildNo: BuildNoUpdateReq + ): Result { + pipelineInfoFacadeService.updateBuildNo( + userId = userId, + projectId = projectId, + pipelineId = pipelineId, + buildNo = buildNo + ) + return Result(true) + } + private fun checkParam(userId: String, projectId: String) { if (userId.isBlank()) { throw ParamBlankException("Invalid userId") diff --git a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/api/UserPipelineResourceImpl.kt b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/api/UserPipelineResourceImpl.kt index 196a5097fa5..9f6aedadf5c 100644 --- a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/api/UserPipelineResourceImpl.kt +++ b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/api/UserPipelineResourceImpl.kt @@ -39,7 +39,6 @@ import com.tencent.devops.common.api.util.MessageUtil import com.tencent.devops.common.auth.api.ActionId import com.tencent.devops.common.auth.api.AuthPermission import com.tencent.devops.common.auth.api.AuthResourceType -import com.tencent.devops.common.client.Client import com.tencent.devops.common.pipeline.Model import com.tencent.devops.common.pipeline.enums.ChannelCode import com.tencent.devops.common.pipeline.enums.VersionStatus @@ -101,8 +100,7 @@ class UserPipelineResourceImpl @Autowired constructor( private val auditService: AuditService, private val pipelineVersionFacadeService: PipelineVersionFacadeService, private val pipelineRuleService: PipelineRuleService, - private val pipelineRecentUseService: PipelineRecentUseService, - private val client: Client + private val pipelineRecentUseService: PipelineRecentUseService ) : UserPipelineResource { override fun hasCreatePermission(userId: String, projectId: String): Result { diff --git a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/AbstractPipelinePermissionService.kt b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/AbstractPipelinePermissionService.kt index 50b91946016..0ddfb5f80d6 100644 --- a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/AbstractPipelinePermissionService.kt +++ b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/AbstractPipelinePermissionService.kt @@ -240,4 +240,8 @@ abstract class AbstractPipelinePermissionService constructor( serviceCode = pipelineAuthServiceCode ) } + + override fun isControlPipelineListPermission(projectId: String): Boolean { + return true + } } diff --git a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/PipelinePermissionService.kt b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/PipelinePermissionService.kt index 228b238c62b..51425474d52 100644 --- a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/PipelinePermissionService.kt +++ b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/PipelinePermissionService.kt @@ -154,4 +154,10 @@ interface PipelinePermissionService { userId: String, projectId: String ): Boolean + + /** + * 判断该项目是否进行列表权限控制 + * @param projectId projectId + */ + fun isControlPipelineListPermission(projectId: String): Boolean } diff --git a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/RbacPipelinePermissionService.kt b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/RbacPipelinePermissionService.kt index 20f089fd897..2c6d78df27a 100644 --- a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/RbacPipelinePermissionService.kt +++ b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/RbacPipelinePermissionService.kt @@ -37,20 +37,24 @@ import com.tencent.devops.common.auth.api.AuthResourceType import com.tencent.devops.common.auth.api.pojo.AuthResourceInstance import com.tencent.devops.common.auth.api.pojo.BkAuthGroup import com.tencent.devops.common.auth.code.PipelineAuthServiceCode +import com.tencent.devops.common.client.Client import com.tencent.devops.process.engine.dao.PipelineInfoDao -import com.tencent.devops.process.service.view.PipelineViewGroupService +import com.tencent.devops.process.service.view.PipelineViewGroupCommonService +import com.tencent.devops.project.api.service.ServiceProjectResource import org.jooq.DSLContext import org.slf4j.LoggerFactory +import javax.ws.rs.NotFoundException @Suppress("LongParameterList") -class RbacPipelinePermissionService constructor( +class RbacPipelinePermissionService( val authPermissionApi: AuthPermissionApi, val authProjectApi: AuthProjectApi, val pipelineAuthServiceCode: PipelineAuthServiceCode, val dslContext: DSLContext, val pipelineInfoDao: PipelineInfoDao, - val pipelineViewGroupService: PipelineViewGroupService, - val authResourceApi: AuthResourceApi + val pipelineViewGroupCommonService: PipelineViewGroupCommonService, + val authResourceApi: AuthResourceApi, + val client: Client ) : PipelinePermissionService { override fun checkPipelinePermission( @@ -89,7 +93,7 @@ class RbacPipelinePermissionService constructor( } finally { logger.info( "It take(${System.currentTimeMillis() - startEpoch})ms to check pipeline permission|" + - "$userId|$projectId|$pipelineId|$permission|$authResourceType" + "$userId|$projectId|$pipelineId|$permission|$authResourceType" ) } } @@ -105,7 +109,7 @@ class RbacPipelinePermissionService constructor( resourceCode = projectId ) parents.add(projectInstance) - pipelineViewGroupService.listViewIdsByPipelineId(projectId, pipelineId).forEach { viewId -> + pipelineViewGroupCommonService.listViewIdsByPipelineId(projectId, pipelineId).forEach { viewId -> parents.add( AuthResourceInstance( resourceType = AuthResourceType.PIPELINE_GROUP.value, @@ -125,7 +129,7 @@ class RbacPipelinePermissionService constructor( projectId: String, pipelineIds: List ): List { - val listViewIdsMap = pipelineViewGroupService.listViewIdsMap( + val listViewIdsMap = pipelineViewGroupCommonService.listViewIdsMap( projectId = projectId, pipelineIds = pipelineIds ) @@ -198,10 +202,11 @@ class RbacPipelinePermissionService constructor( // 如果有项目下所有该资源权限,返回项目下流水线列表 instanceMap[AuthResourceType.PROJECT.value]?.contains(projectId) == true -> getAllAuthPipelineIds(projectId = projectId) + else -> { // 获取有权限流水线组下的流水线 val authViewPipelineIds = instanceMap[AuthResourceType.PIPELINE_GROUP.value]?.let { authViewIds -> - pipelineViewGroupService.listPipelineIdsByViewIds(projectId, authViewIds) + pipelineViewGroupCommonService.listPipelineIdsByViewIds(projectId, authViewIds) } ?: emptyList() // 获取有权限的流水线列表 val authPipelineIds = instanceMap[AuthResourceType.PIPELINE_DEFAULT.value] ?: emptyList() @@ -295,6 +300,12 @@ class RbacPipelinePermissionService constructor( return authProjectApi.checkProjectManager(userId, pipelineAuthServiceCode, projectId) } + override fun isControlPipelineListPermission(projectId: String): Boolean { + val projectInfo = client.get(ServiceProjectResource::class).get(englishName = projectId).data + ?: throw NotFoundException("Fail to find the project info of project($projectId)") + return projectInfo.properties?.pipelineListPermissionControl == true + } + companion object { private val resourceType = AuthResourceType.PIPELINE_DEFAULT private val logger = LoggerFactory.getLogger(RbacPipelinePermissionService::class.java) diff --git a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/StreamPipelinePermissionServiceImpl.kt b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/StreamPipelinePermissionServiceImpl.kt index 3c6123ba6b9..dc3dd010f9f 100644 --- a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/StreamPipelinePermissionServiceImpl.kt +++ b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/StreamPipelinePermissionServiceImpl.kt @@ -157,6 +157,8 @@ class StreamPipelinePermissionServiceImpl @Autowired constructor( ).data ?: false } + override fun isControlPipelineListPermission(projectId: String) = false + private fun getProjectAllInstance(projectId: String): List { return pipelineInfoDao.searchByProject(dslContext, projectId)?.map { it.pipelineId } ?: emptyList() } diff --git a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/config/PipelinePermConfiguration.kt b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/config/PipelinePermConfiguration.kt index 1cb8988b4e2..92460f6d6c6 100644 --- a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/config/PipelinePermConfiguration.kt +++ b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/permission/config/PipelinePermConfiguration.kt @@ -38,7 +38,7 @@ import com.tencent.devops.process.permission.MockPipelinePermissionService import com.tencent.devops.process.permission.PipelinePermissionService import com.tencent.devops.process.permission.RbacPipelinePermissionService import com.tencent.devops.process.permission.StreamPipelinePermissionServiceImpl -import com.tencent.devops.process.service.view.PipelineViewGroupService +import com.tencent.devops.process.service.view.PipelineViewGroupCommonService import org.jooq.DSLContext import org.springframework.boot.autoconfigure.AutoConfigureOrder import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty @@ -107,15 +107,17 @@ class PipelinePermConfiguration { pipelineAuthServiceCode: PipelineAuthServiceCode, dslContext: DSLContext, pipelineInfoDao: PipelineInfoDao, - pipelineViewGroupService: PipelineViewGroupService, - authResourceApi: AuthResourceApi + pipelineViewGroupCommonService: PipelineViewGroupCommonService, + authResourceApi: AuthResourceApi, + client: Client ): PipelinePermissionService = RbacPipelinePermissionService( authPermissionApi = authPermissionApi, authProjectApi = authProjectApi, pipelineAuthServiceCode = pipelineAuthServiceCode, dslContext = dslContext, pipelineInfoDao = pipelineInfoDao, - pipelineViewGroupService = pipelineViewGroupService, - authResourceApi = authResourceApi + pipelineViewGroupCommonService = pipelineViewGroupCommonService, + authResourceApi = authResourceApi, + client = client ) } diff --git a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/PipelineInfoFacadeService.kt b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/PipelineInfoFacadeService.kt index f5ad4172a56..b7a5ba7c12a 100644 --- a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/PipelineInfoFacadeService.kt +++ b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/PipelineInfoFacadeService.kt @@ -111,12 +111,12 @@ import org.springframework.beans.factory.annotation.Value import org.springframework.dao.DuplicateKeyException import org.springframework.stereotype.Service import java.net.URLEncoder +import java.time.LocalDateTime import java.util.LinkedList import java.util.concurrent.TimeUnit import javax.ws.rs.core.MediaType import javax.ws.rs.core.Response import javax.ws.rs.core.StreamingOutput -import java.time.LocalDateTime @Suppress("ALL") @Service @@ -140,7 +140,8 @@ class PipelineInfoFacadeService @Autowired constructor( private val transferService: PipelineTransferYamlService, private val yamlFacadeService: PipelineYamlFacadeService, private val operationLogService: PipelineOperationLogService, - private val pipelineAuthorizationService: PipelineAuthorizationService + private val pipelineAuthorizationService: PipelineAuthorizationService, + private val pipelineAsCodeService: PipelineAsCodeService ) { @Value("\${process.deletedPipelineStoreDays:30}") @@ -309,7 +310,9 @@ class PipelineInfoFacadeService @Autowired constructor( useLabelSettings: Boolean? = false, useConcurrencyGroup: Boolean? = false, description: String? = null, - yamlInfo: PipelineYamlVo? = null + yamlInfo: PipelineYamlVo? = null, + inheritedDialectSetting: Boolean? = true, + pipelineDialectSetting: String? = null ): DeployPipelineResult { val watcher = Watcher(id = "createPipeline|$projectId|$userId|$channelCode|$checkPermission|$instanceType|$fixPipelineId") @@ -429,6 +432,12 @@ class PipelineInfoFacadeService @Autowired constructor( } watcher.start("deployPipeline") + val pipelineDialect = pipelineAsCodeService.getPipelineDialect( + projectId = projectId, + asCodeSettings = setting?.pipelineAsCodeSettings, + inheritedDialectSetting = inheritedDialectSetting, + pipelineDialectSetting = pipelineDialectSetting + ) val result = pipelineRepositoryService.deployPipeline( model = instance, setting = setting, @@ -446,7 +455,10 @@ class PipelineInfoFacadeService @Autowired constructor( description = description, yaml = yaml, baseVersion = null, - yamlInfo = yamlInfo + yamlInfo = yamlInfo, + inheritedDialectSetting = inheritedDialectSetting, + pipelineDialectSetting = pipelineDialectSetting, + pipelineDialect = pipelineDialect ) pipelineId = result.pipelineId watcher.stop() @@ -599,12 +611,14 @@ class PipelineInfoFacadeService @Autowired constructor( } ?: newResource.model.name.ifBlank { yamlFileName } + val pipelineAsCodeSettings = + newResource.setting.pipelineAsCodeSettings?.copy(enable = true) ?: PipelineAsCodeSettings(enable = true) // 通过PAC模式创建或保存的流水线均打开PAC // 修正创建时的流水线名和增加PAC开关参数 val newSetting = newResource.setting.copy( projectId = projectId, pipelineName = pipelineName, - pipelineAsCodeSettings = PipelineAsCodeSettings(enable = true) + pipelineAsCodeSettings = pipelineAsCodeSettings ) return createPipeline( userId = userId, @@ -651,13 +665,15 @@ class PipelineInfoFacadeService @Autowired constructor( // 通过PAC模式创建或保存的流水线均打开PAC // 修正创建时的流水线名和增加PAC开关参数 val pipelineName = newResource.model.name.ifBlank { yamlFileName } + val pipelineAsCodeSettings = + newResource.setting.pipelineAsCodeSettings?.copy(enable = true) ?: PipelineAsCodeSettings(enable = true) val savedSetting = pipelineSettingFacadeService.saveSetting( userId = userId, projectId = projectId, pipelineId = pipelineId, setting = newResource.setting.copy( pipelineName = pipelineName, - pipelineAsCodeSettings = PipelineAsCodeSettings(enable = true) + pipelineAsCodeSettings = pipelineAsCodeSettings ), checkPermission = false, versionStatus = versionStatus, @@ -1109,6 +1125,11 @@ class PipelineInfoFacadeService @Autowired constructor( ) modelCheckPlugin.beforeDeleteElementInExistsModel(existModel, model, param) } + val pipelineSetting = savedSetting ?: pipelineSettingFacadeService.getSettingInfo(projectId, pipelineId) + val pipelineDialect = pipelineAsCodeService.getPipelineDialect( + projectId = projectId, + pipelineSetting?.pipelineAsCodeSettings + ) val deployResult = pipelineRepositoryService.deployPipeline( model = model, projectId = projectId, @@ -1123,7 +1144,8 @@ class PipelineInfoFacadeService @Autowired constructor( description = description, yaml = yaml, baseVersion = baseVersion, - yamlInfo = yamlInfo + yamlInfo = yamlInfo, + pipelineDialect = pipelineDialect ) // 审计 ActionAuditContext.current() diff --git a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/PipelineListFacadeService.kt b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/PipelineListFacadeService.kt index e00bfae4d21..0b0d59657da 100644 --- a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/PipelineListFacadeService.kt +++ b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/PipelineListFacadeService.kt @@ -152,6 +152,14 @@ class PipelineListFacadeService @Autowired constructor( companion object { private val logger = LoggerFactory.getLogger(PipelineListFacadeService::class.java) + private val DEFAULT_VIEW_IDS = listOf( + PIPELINE_VIEW_FAVORITE_PIPELINES, + PIPELINE_VIEW_MY_PIPELINES, + PIPELINE_VIEW_ALL_PIPELINES, + PIPELINE_VIEW_MY_LIST_PIPELINES, + PIPELINE_VIEW_UNCLASSIFIED, + PIPELINE_VIEW_RECENT_USE + ) } fun sortPipelines(pipelines: MutableList, sortType: PipelineSortType) { @@ -505,34 +513,147 @@ class PipelineListFacadeService @Autowired constructor( val watcher = Watcher(id = "listViewPipelines|$projectId|$userId") watcher.start("perm_r_perm") val authPipelines = pipelinePermissionService.getResourceByPermission( - userId = userId, projectId = projectId, permission = AuthPermission.LIST + userId = userId, + projectId = projectId, + permission = AuthPermission.LIST ) watcher.stop() watcher.start("s_r_summary") try { - val favorPipelines = pipelineGroupService.getFavorPipelines(userId = userId, projectId = projectId) + // 记录用户最近使用过的视图 + pipelineViewService.addUsingView( + userId = userId, + projectId = projectId, + viewId = viewId + ) + val favorPipelines = pipelineGroupService.getFavorPipelines( + userId = userId, + projectId = projectId + ) + // 组装搜索条件 val pipelineFilterParamList = pipelineListQueryParamService.generatePipelineFilterParams( projectId = projectId, filterByPipelineName = filterByPipelineName, filterByCreator = filterByCreator, filterByLabels = filterByLabels ) - - val pipelineIds = mutableSetOf() - val viewIdList = listOf( - PIPELINE_VIEW_FAVORITE_PIPELINES, - PIPELINE_VIEW_MY_PIPELINES, - PIPELINE_VIEW_ALL_PIPELINES, - PIPELINE_VIEW_MY_LIST_PIPELINES, - PIPELINE_VIEW_UNCLASSIFIED, - PIPELINE_VIEW_RECENT_USE + // 根据视图类型获取流水线 + val pipelineIdsFilterByView = listPipelineIdsByViewId( + userId = userId, + projectId = projectId, + viewId = viewId, + filterByViewIds = filterByViewIds ) - val includeDelete = showDelete && (PIPELINE_VIEW_RECENT_USE == viewId || !viewIdList.contains(viewId)) + val includeDelete = showDelete && (PIPELINE_VIEW_RECENT_USE == viewId || !DEFAULT_VIEW_IDS.contains(viewId)) + // 查询有权限查看的流水线总数 + val totalAvailablePipelineSize = pipelineBuildSummaryDao.listPipelineInfoBuildSummaryCount( + dslContext = dslContext, + projectId = projectId, + channelCode = channelCode, + pipelineIds = pipelineIdsFilterByView, + favorPipelines = favorPipelines, + authPipelines = authPipelines, + viewId = viewId, + pipelineFilterParamList = pipelineFilterParamList, + permissionFlag = true, + includeDelete = includeDelete, + userId = userId + ) + val pipelineList = mutableListOf() + val isPipelineListPermissionControl = pipelinePermissionService.isControlPipelineListPermission(projectId) + if (includeDelete) { + handlePipelineQueryList( + pipelineList = pipelineList, + projectId = projectId, + channelCode = channelCode, + sortType = sortType, + pipelineIds = pipelineIdsFilterByView, + favorPipelines = favorPipelines, + authPipelines = authPipelines, + viewId = viewId, + pipelineFilterParamList = pipelineFilterParamList, + permissionFlag = true, + page = page, + pageSize = pageSize, + includeDelete = true, + collation = collation, + userId = userId, + queryByWeb = queryByWeb + ) + } else if (!isPipelineListPermissionControl) { + // 无列表权限控制下的流水线获取方法 + listPipelineWithoutPermission( + userId = userId, + projectId = projectId, + sortType = sortType, + channelCode = channelCode, + viewId = viewId, + filterInvalid = filterInvalid, + collation = collation, + queryByWeb = queryByWeb, + totalAvailablePipelineSize = totalAvailablePipelineSize, + pipelineList = pipelineList, + filterPipelineIds = pipelineIdsFilterByView, + favorPipelines = favorPipelines, + authPipelines = authPipelines, + pipelineFilterParamList = pipelineFilterParamList, + includeDelete = includeDelete, + page = page, + pageSize = pageSize + ) + } else { + // 列表权限控制下的流水线获取方法 + handlePipelineQueryList( + pipelineList = pipelineList, + projectId = projectId, + channelCode = channelCode, + sortType = sortType, + pipelineIds = pipelineIdsFilterByView, + favorPipelines = favorPipelines, + authPipelines = authPipelines, + viewId = viewId, + pipelineFilterParamList = pipelineFilterParamList, + permissionFlag = true, + page = page, + pageSize = pageSize, + includeDelete = includeDelete, + collation = collation, + userId = userId, + queryByWeb = queryByWeb + ) + } + watcher.stop() + return PipelineViewPipelinePage( + page = page ?: 1, + pageSize = pageSize ?: totalAvailablePipelineSize.toInt(), + count = totalAvailablePipelineSize, + records = fillPipelinePermissions( + userId = userId, + projectId = projectId, + pipelineList = pipelineList + ) + ) + } finally { + LogUtils.printCostTimeWE(watcher = watcher) + processJmxApi.execute(ProcessJmxApi.LIST_NEW_PIPELINES, watcher.totalTimeMillis) + } + } - if (!viewIdList.contains(viewId)) { // 已分组的视图 + private fun listPipelineIdsByViewId( + userId: String, + projectId: String, + viewId: String, + filterByViewIds: String? + ): Set { + val pipelineIds = mutableSetOf() + when { + // 非默认预置流水线组 + !DEFAULT_VIEW_IDS.contains(viewId) -> { pipelineIds.addAll(pipelineViewGroupService.listPipelineIdsByViewId(projectId, viewId)) - } else if (viewId == PIPELINE_VIEW_UNCLASSIFIED) { // 非分组的视图 + } + // 流水线视图未分组 + viewId == PIPELINE_VIEW_UNCLASSIFIED -> { val allPipelineIds = pipelineInfoDao.listPipelineIdByProject(dslContext, projectId).toMutableSet() pipelineIds.addAll( allPipelineIds.subtract(pipelineViewGroupService.getClassifiedPipelineIds(projectId).toSet()) @@ -541,219 +662,196 @@ class PipelineListFacadeService @Autowired constructor( if (pipelineIds.isEmpty()) { pipelineIds.add("##NONE##") } - } else if (viewId == PIPELINE_VIEW_RECENT_USE) { // 最近访问 - pipelineIds.addAll(pipelineRecentUseService.listPipelineIds(userId, projectId)) } - // 剔除掉filterByViewIds - if (filterByViewIds != null) { - val pipelineIdsByFilterViewIds = - pipelineViewGroupService.listPipelineIdsByViewIds(projectId, filterByViewIds.split(",")).toSet() - if (pipelineIds.isEmpty()) { - pipelineIds.addAll(pipelineIdsByFilterViewIds) - } else { - pipelineIds.retainAll(pipelineIdsByFilterViewIds) - } + // 最近使用过的流水线组 + viewId == PIPELINE_VIEW_RECENT_USE -> { + pipelineIds.addAll(pipelineRecentUseService.listPipelineIds(userId, projectId)) } + } - pipelineViewService.addUsingView(userId = userId, projectId = projectId, viewId = viewId) + // 剔除掉filterByViewIds + if (filterByViewIds != null) { + val pipelineIdsByFilterViewIds = + pipelineViewGroupService.listPipelineIdsByViewIds(projectId, filterByViewIds.split(",")).toSet() + if (pipelineIds.isEmpty()) { + pipelineIds.addAll(pipelineIdsByFilterViewIds) + } else { + pipelineIds.retainAll(pipelineIdsByFilterViewIds) + } + } + return pipelineIds + } - // 查询有权限查看的流水线总数 - val totalAvailablePipelineSize = pipelineBuildSummaryDao.listPipelineInfoBuildSummaryCount( + /** + * 该方法之所以复杂,是为了达到将有权限列表的流水线展示在前面, + * 无列表权限的流水线挪在最后的目的。 + * 这种分页方法,需要对分页的各种情况的临界值进行处理。 + * */ + private fun listPipelineWithoutPermission( + userId: String, + projectId: String, + sortType: PipelineSortType, + channelCode: ChannelCode, + viewId: String, + filterInvalid: Boolean = false, + collation: PipelineCollation = PipelineCollation.DEFAULT, + queryByWeb: Boolean = false, + totalAvailablePipelineSize: Long, + pipelineList: MutableList, + filterPipelineIds: Collection? = null, + favorPipelines: List = emptyList(), + authPipelines: List = emptyList(), + pipelineFilterParamList: List? = null, + includeDelete: Boolean?, + page: Int?, + pageSize: Int? + ) { + // 查询无权限查看的流水线总数 + val totalInvalidPipelineSize = + if (filterInvalid) 0 else pipelineBuildSummaryDao.listPipelineInfoBuildSummaryCount( dslContext = dslContext, projectId = projectId, channelCode = channelCode, - pipelineIds = pipelineIds, + pipelineIds = filterPipelineIds, + viewId = viewId, favorPipelines = favorPipelines, authPipelines = authPipelines, - viewId = viewId, pipelineFilterParamList = pipelineFilterParamList, - permissionFlag = true, + permissionFlag = false, includeDelete = includeDelete, userId = userId ) - // 查询无权限查看的流水线总数 - val totalInvalidPipelineSize = - if (filterInvalid) 0 else pipelineBuildSummaryDao.listPipelineInfoBuildSummaryCount( - dslContext = dslContext, + if ((null != page && null != pageSize) && !(page == 1 && pageSize == -1)) { + // 判断可用的流水线是否已到最后一页 + val totalAvailablePipelinePage = PageUtil.calTotalPage(pageSize, totalAvailablePipelineSize) + if (page < totalAvailablePipelinePage) { + // 当前页未到可用流水线最后一页,不需要处理临界点(最后一页)的情况 + handlePipelineQueryList( + pipelineList = pipelineList, projectId = projectId, channelCode = channelCode, - pipelineIds = pipelineIds, - viewId = viewId, + sortType = sortType, + pipelineIds = filterPipelineIds, favorPipelines = favorPipelines, authPipelines = authPipelines, + viewId = viewId, pipelineFilterParamList = pipelineFilterParamList, - permissionFlag = false, + permissionFlag = true, + page = page, + pageSize = pageSize, includeDelete = includeDelete, - userId = userId + collation = collation, + userId = userId, + queryByWeb = queryByWeb ) - val pipelineList = mutableListOf() - val totalSize = totalAvailablePipelineSize + totalInvalidPipelineSize - if (includeDelete) { + } else if (page == totalAvailablePipelinePage && totalAvailablePipelineSize > 0) { + // 查询可用流水线最后一页不满页的数量 + val lastPageRemainNum = pageSize - totalAvailablePipelineSize % pageSize handlePipelineQueryList( pipelineList = pipelineList, projectId = projectId, channelCode = channelCode, sortType = sortType, - pipelineIds = pipelineIds, + pipelineIds = filterPipelineIds, favorPipelines = favorPipelines, authPipelines = authPipelines, viewId = viewId, pipelineFilterParamList = pipelineFilterParamList, - permissionFlag = null, + permissionFlag = true, page = page, pageSize = pageSize, - includeDelete = true, + includeDelete = includeDelete, collation = collation, userId = userId, queryByWeb = queryByWeb ) - } else if ((null != page && null != pageSize) && !(page == 1 && pageSize == -1)) { - // 判断可用的流水线是否已到最后一页 - val totalAvailablePipelinePage = PageUtil.calTotalPage(pageSize, totalAvailablePipelineSize) - if (page < totalAvailablePipelinePage) { - // 当前页未到可用流水线最后一页,不需要处理临界点(最后一页)的情况 + // 可用流水线最后一页不满页的数量需用不可用的流水线填充 + if (lastPageRemainNum > 0 && totalInvalidPipelineSize > 0) { handlePipelineQueryList( pipelineList = pipelineList, projectId = projectId, channelCode = channelCode, sortType = sortType, - pipelineIds = pipelineIds, - favorPipelines = favorPipelines, - authPipelines = authPipelines, - viewId = viewId, - pipelineFilterParamList = pipelineFilterParamList, - permissionFlag = true, - page = page, - pageSize = pageSize, - includeDelete = includeDelete, - collation = collation, - userId = userId, - queryByWeb = queryByWeb - ) - } else if (page == totalAvailablePipelinePage && totalAvailablePipelineSize > 0) { - // 查询可用流水线最后一页不满页的数量 - val lastPageRemainNum = pageSize - totalAvailablePipelineSize % pageSize - handlePipelineQueryList( - pipelineList = pipelineList, - projectId = projectId, - channelCode = channelCode, - sortType = sortType, - pipelineIds = pipelineIds, - favorPipelines = favorPipelines, - authPipelines = authPipelines, - viewId = viewId, - pipelineFilterParamList = pipelineFilterParamList, - permissionFlag = true, - page = page, - pageSize = pageSize, - includeDelete = includeDelete, - collation = collation, - userId = userId, - queryByWeb = queryByWeb - ) - // 可用流水线最后一页不满页的数量需用不可用的流水线填充 - if (lastPageRemainNum > 0 && totalInvalidPipelineSize > 0) { - handlePipelineQueryList( - pipelineList = pipelineList, - projectId = projectId, - channelCode = channelCode, - sortType = sortType, - pipelineIds = pipelineIds, - favorPipelines = favorPipelines, - authPipelines = authPipelines, - viewId = viewId, - pipelineFilterParamList = pipelineFilterParamList, - permissionFlag = false, - page = 1, - pageSize = lastPageRemainNum.toInt(), - includeDelete = includeDelete, - collation = collation, - userId = userId, - queryByWeb = queryByWeb - ) - } - } else if (totalInvalidPipelineSize > 0) { - // 当前页大于可用流水线最后一页,需要排除掉可用流水线最后一页不满页的数量用不可用的流水线填充的情况 - val lastPageRemainNum = - if (totalAvailablePipelineSize > 0) pageSize - totalAvailablePipelineSize % pageSize else 0 - handlePipelineQueryList( - pipelineList = pipelineList, - projectId = projectId, - channelCode = channelCode, - sortType = sortType, - pipelineIds = pipelineIds, + pipelineIds = filterPipelineIds, favorPipelines = favorPipelines, authPipelines = authPipelines, viewId = viewId, pipelineFilterParamList = pipelineFilterParamList, permissionFlag = false, - page = page - totalAvailablePipelinePage, - pageSize = pageSize, - pageOffsetNum = lastPageRemainNum.toInt(), + page = 1, + pageSize = lastPageRemainNum.toInt(), includeDelete = includeDelete, collation = collation, userId = userId, queryByWeb = queryByWeb ) } - } else { - // 不分页查询 + } else if (totalInvalidPipelineSize > 0) { + // 当前页大于可用流水线最后一页,需要排除掉可用流水线最后一页不满页的数量用不可用的流水线填充的情况 + val lastPageRemainNum = + if (totalAvailablePipelineSize > 0) pageSize - totalAvailablePipelineSize % pageSize else 0 handlePipelineQueryList( pipelineList = pipelineList, projectId = projectId, channelCode = channelCode, sortType = sortType, - pipelineIds = pipelineIds, + pipelineIds = filterPipelineIds, favorPipelines = favorPipelines, authPipelines = authPipelines, viewId = viewId, pipelineFilterParamList = pipelineFilterParamList, - permissionFlag = true, - page = page, + permissionFlag = false, + page = page - totalAvailablePipelinePage, pageSize = pageSize, + pageOffsetNum = lastPageRemainNum.toInt(), includeDelete = includeDelete, collation = collation, userId = userId, queryByWeb = queryByWeb ) - - if (filterInvalid) { - handlePipelineQueryList( - pipelineList = pipelineList, - projectId = projectId, - channelCode = channelCode, - sortType = sortType, - pipelineIds = pipelineIds, - favorPipelines = favorPipelines, - authPipelines = authPipelines, - viewId = viewId, - pipelineFilterParamList = pipelineFilterParamList, - permissionFlag = false, - page = page, - pageSize = pageSize, - includeDelete = includeDelete, - collation = collation, - userId = userId, - queryByWeb = queryByWeb - ) - } } - watcher.stop() + } else { + // 不分页查询 + handlePipelineQueryList( + pipelineList = pipelineList, + projectId = projectId, + channelCode = channelCode, + sortType = sortType, + pipelineIds = filterPipelineIds, + favorPipelines = favorPipelines, + authPipelines = authPipelines, + viewId = viewId, + pipelineFilterParamList = pipelineFilterParamList, + permissionFlag = true, + page = page, + pageSize = pageSize, + includeDelete = includeDelete, + collation = collation, + userId = userId, + queryByWeb = queryByWeb + ) - return PipelineViewPipelinePage( - page = page ?: 1, - pageSize = pageSize ?: totalSize.toInt(), - count = totalSize, - records = fillPipelinePermissions( - userId = userId, + if (filterInvalid) { + handlePipelineQueryList( + pipelineList = pipelineList, projectId = projectId, - pipelineList = pipelineList + channelCode = channelCode, + sortType = sortType, + pipelineIds = filterPipelineIds, + favorPipelines = favorPipelines, + authPipelines = authPipelines, + viewId = viewId, + pipelineFilterParamList = pipelineFilterParamList, + permissionFlag = false, + page = page, + pageSize = pageSize, + includeDelete = includeDelete, + collation = collation, + userId = userId, + queryByWeb = queryByWeb ) - ) - } finally { - LogUtils.printCostTimeWE(watcher = watcher) - processJmxApi.execute(ProcessJmxApi.LIST_NEW_PIPELINES, watcher.totalTimeMillis) + } } } @@ -858,6 +956,8 @@ class PipelineListFacadeService @Autowired constructor( val authPipelines = pipelinePermissionService.getResourceByPermission( userId = userId, projectId = projectId, permission = AuthPermission.LIST ) + val isControlPipelineListPermission = pipelinePermissionService.isControlPipelineListPermission(projectId) + val permissionFlag = if (isControlPipelineListPermission) true else null val favorPipelines = pipelineGroupService.getFavorPipelines(userId = userId, projectId = projectId) val recentUsePipelines = pipelineRecentUseService.listPipelineIds(userId, projectId) val totalCount = pipelineBuildSummaryDao.listPipelineInfoBuildSummaryCount( @@ -868,7 +968,8 @@ class PipelineListFacadeService @Autowired constructor( favorPipelines = favorPipelines, viewId = PIPELINE_VIEW_ALL_PIPELINES, includeDelete = false, - userId = userId + userId = userId, + permissionFlag = permissionFlag ).toInt() val myFavoriteCount = pipelineBuildSummaryDao.listPipelineInfoBuildSummaryCount( dslContext = dslContext, @@ -878,7 +979,8 @@ class PipelineListFacadeService @Autowired constructor( favorPipelines = favorPipelines, viewId = PIPELINE_VIEW_FAVORITE_PIPELINES, includeDelete = false, - userId = userId + userId = userId, + permissionFlag = permissionFlag ).toInt() val myPipelineCount = pipelineBuildSummaryDao.listPipelineInfoBuildSummaryCount( dslContext = dslContext, @@ -888,7 +990,8 @@ class PipelineListFacadeService @Autowired constructor( favorPipelines = favorPipelines, viewId = PIPELINE_VIEW_MY_PIPELINES, includeDelete = false, - userId = userId + userId = userId, + permissionFlag = permissionFlag ).toInt() val recentUseCount = pipelineBuildSummaryDao.listPipelineInfoBuildSummaryCount( dslContext = dslContext, @@ -899,7 +1002,8 @@ class PipelineListFacadeService @Autowired constructor( viewId = PIPELINE_VIEW_RECENT_USE, includeDelete = false, userId = userId, - pipelineIds = recentUsePipelines + pipelineIds = recentUsePipelines, + permissionFlag = permissionFlag ).toInt() val recycleCount = pipelineInfoDao.countPipeline( dslContext = dslContext, diff --git a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/PipelineVersionFacadeService.kt b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/PipelineVersionFacadeService.kt index b9de79ed072..39ab5330a15 100644 --- a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/PipelineVersionFacadeService.kt +++ b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/PipelineVersionFacadeService.kt @@ -105,7 +105,8 @@ class PipelineVersionFacadeService @Autowired constructor( private val pipelineViewGroupService: PipelineViewGroupService, private val pipelineBuildSummaryDao: PipelineBuildSummaryDao, private val pipelineBuildDao: PipelineBuildDao, - private val buildLogPrinter: BuildLogPrinter + private val buildLogPrinter: BuildLogPrinter, + private val pipelineAsCodeService: PipelineAsCodeService ) { companion object { @@ -319,7 +320,11 @@ class PipelineVersionFacadeService @Autowired constructor( create = false, versionStatus = VersionStatus.RELEASED, channelCode = pipeline.channelCode, - yamlInfo = request.yamlInfo + yamlInfo = request.yamlInfo, + pipelineDialect = pipelineAsCodeService.getPipelineDialect( + projectId = projectId, + asCodeSettings = originSetting.pipelineAsCodeSettings + ) ) val originYaml = pipelineYamlFacadeService.getPipelineYamlInfo(projectId, pipelineId, version) // 如果不匹配已有状态则报错,需要用户重新刷新页面 @@ -329,7 +334,7 @@ class PipelineVersionFacadeService @Autowired constructor( // 根据项目PAC状态进行接口调用 val enabled = originYaml != null || request.enablePac val targetSettings = originSetting.copy( - pipelineAsCodeSettings = PipelineAsCodeSettings(enabled) + pipelineAsCodeSettings = originSetting.pipelineAsCodeSettings?.copy(enable = enabled) ) val (versionStatus, branchName) = if ( enabled && request.targetAction == CodeTargetAction.CHECKOUT_BRANCH_AND_REQUEST_MERGE @@ -570,7 +575,9 @@ class PipelineVersionFacadeService @Autowired constructor( versionStatus = VersionStatus.COMMITTING, useSubscriptionSettings = request.useSubscriptionSettings, useLabelSettings = request.useLabelSettings, - useConcurrencyGroup = request.useConcurrencyGroup + useConcurrencyGroup = request.useConcurrencyGroup, + inheritedDialectSetting = request.inheritedDialect, + pipelineDialectSetting = request.pipelineDialect ) } @@ -644,7 +651,9 @@ class PipelineVersionFacadeService @Autowired constructor( baseVersion = resource.baseVersion, baseVersionName = baseResource?.versionName, yamlSupported = yamlSupported, - yamlInvalidMsg = msg + yamlInvalidMsg = msg, + updater = resource.updater ?: resource.creator, + updateTime = resource.updateTime?.timestampmilli() ) } diff --git a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/SubPipelineRepositoryService.kt b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/SubPipelineRepositoryService.kt index faf07365aad..0082bd70a6e 100644 --- a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/SubPipelineRepositoryService.kt +++ b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/SubPipelineRepositoryService.kt @@ -6,6 +6,7 @@ import com.tencent.devops.common.api.util.EnvUtils import com.tencent.devops.common.auth.api.AuthPermission import com.tencent.devops.common.pipeline.Model import com.tencent.devops.common.pipeline.container.Stage +import com.tencent.devops.common.pipeline.container.TriggerContainer import com.tencent.devops.common.pipeline.pojo.element.Element import com.tencent.devops.common.pipeline.pojo.element.SubPipelineCallElement import com.tencent.devops.common.pipeline.pojo.element.atom.ElementCheckResult @@ -15,7 +16,6 @@ import com.tencent.devops.common.pipeline.pojo.element.market.MarketBuildLessAto import com.tencent.devops.common.web.utils.I18nUtil import com.tencent.devops.process.constant.ProcessMessageCode import com.tencent.devops.process.engine.dao.PipelineResourceDao -import com.tencent.devops.process.engine.extend.DefaultModelCheckPlugin import com.tencent.devops.process.engine.service.PipelineRepositoryService import com.tencent.devops.process.permission.PipelinePermissionService import com.tencent.devops.process.utils.PipelineVarUtil @@ -32,7 +32,6 @@ class SubPipelineRepositoryService @Autowired constructor( private val objectMapper: ObjectMapper, private val pipelineResDao: PipelineResourceDao, private val pipelineRepositoryService: PipelineRepositoryService, - private val defaultModelCheckPlugin: DefaultModelCheckPlugin, private val pipelinePermissionService: PipelinePermissionService ) { @@ -294,11 +293,11 @@ class SubPipelineRepositoryService @Autowired constructor( } private fun getContextMap(stages: List): Map { - val trigger = stages.getOrNull(0) - ?: throw ErrorCodeException(errorCode = ProcessMessageCode.ERROR_PIPELINE_MODEL_NEED_JOB) - // 检查触发容器 - val paramsMap = defaultModelCheckPlugin.checkTriggerContainer(trigger) - return PipelineVarUtil.fillVariableMap(paramsMap.mapValues { it.value.defaultValue.toString() }) + val triggerContainer = stages[0].containers[0] as TriggerContainer + val variables = triggerContainer.params.associate { param -> + param.id to param.defaultValue.toString() + } + return PipelineVarUtil.fillVariableMap(variables) } companion object { @@ -306,4 +305,4 @@ class SubPipelineRepositoryService @Autowired constructor( private val PIPELINE_ID_PATTERN = Pattern.compile("(p-)?[a-f\\d]{32}") private const val SUB_PIPELINE_EXEC_ATOM_CODE = "SubPipelineExec" } -} \ No newline at end of file +} diff --git a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/template/TemplateFacadeService.kt b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/template/TemplateFacadeService.kt index 781f002957c..e1e5fca0e24 100644 --- a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/template/TemplateFacadeService.kt +++ b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/template/TemplateFacadeService.kt @@ -122,6 +122,7 @@ import com.tencent.devops.process.pojo.template.TemplateType import com.tencent.devops.process.pojo.template.TemplateVersion import com.tencent.devops.process.pojo.template.TemplateWithPermission import com.tencent.devops.process.service.ParamFacadeService +import com.tencent.devops.process.service.PipelineAsCodeService import com.tencent.devops.process.service.PipelineInfoFacadeService import com.tencent.devops.process.service.PipelineRemoteAuthService import com.tencent.devops.process.service.StageTagService @@ -180,7 +181,8 @@ class TemplateFacadeService @Autowired constructor( private val modelCheckPlugin: ModelCheckPlugin, private val pipelineSettingFacadeService: PipelineSettingFacadeService, private val templateCommonService: TemplateCommonService, - private val templateSettingService: TemplateSettingService + private val templateSettingService: TemplateSettingService, + private val pipelineAsCodeService: PipelineAsCodeService ) { @Value("\${template.maxSyncInstanceNum:10}") @@ -2321,11 +2323,14 @@ class TemplateFacadeService @Autowired constructor( errorCode = ProcessMessageCode.TEMPLATE_NAME_CAN_NOT_NULL ) } + // 模版先都统一使用项目配置 + val projectDialect = projectId?.let { pipelineAsCodeService.getProjectDialect(projectId = it) } modelCheckPlugin.checkModelIntegrity( model = template, projectId = projectId, userId = userId, - isTemplate = true + isTemplate = true, + pipelineDialect = projectDialect ) checkPipelineParam(template) } diff --git a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/template/TemplateSettingService.kt b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/template/TemplateSettingService.kt index 90c7630a1bd..844587c51f6 100644 --- a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/template/TemplateSettingService.kt +++ b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/template/TemplateSettingService.kt @@ -6,9 +6,9 @@ import com.tencent.devops.common.pipeline.extend.ModelCheckPlugin import com.tencent.devops.common.pipeline.pojo.setting.PipelineSetting import com.tencent.devops.process.constant.ProcessMessageCode import com.tencent.devops.process.engine.dao.template.TemplateDao -import com.tencent.devops.process.engine.service.PipelineInfoExtService import com.tencent.devops.process.engine.service.PipelineRepositoryService import com.tencent.devops.process.permission.template.PipelineTemplatePermissionService +import com.tencent.devops.process.service.PipelineAsCodeService import com.tencent.devops.process.service.label.PipelineGroupService import com.tencent.devops.process.service.pipeline.PipelineSettingVersionService import org.jooq.DSLContext @@ -25,12 +25,12 @@ class TemplateSettingService @Autowired constructor( private val dslContext: DSLContext, private val pipelineGroupService: PipelineGroupService, private val pipelineRepositoryService: PipelineRepositoryService, - private val pipelineInfoExtService: PipelineInfoExtService, private val templateCommonService: TemplateCommonService, private val templateDao: TemplateDao, private val modelCheckPlugin: ModelCheckPlugin, private val pipelineSettingVersionService: PipelineSettingVersionService, - private val pipelineTemplatePermissionService: PipelineTemplatePermissionService + private val pipelineTemplatePermissionService: PipelineTemplatePermissionService, + private val pipelineAsCodeService: PipelineAsCodeService ) { fun updateTemplateSetting( projectId: String, @@ -144,6 +144,11 @@ class TemplateSettingService @Autowired constructor( labels.addAll(it.labels) } setting.labels = labels + val asCodeSettings = pipelineAsCodeService.getPipelineAsCodeSettings( + projectId = projectId, + asCodeSettings = setting.pipelineAsCodeSettings + ) + setting.pipelineAsCodeSettings = asCodeSettings return setting } diff --git a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/GroupPermissionService.kt b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/view/PipelineViewGroupCommonService.kt similarity index 52% rename from src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/GroupPermissionService.kt rename to src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/view/PipelineViewGroupCommonService.kt index 726d3724796..733fcf588f2 100644 --- a/src/backend/ci/core/auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/service/GroupPermissionService.kt +++ b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/view/PipelineViewGroupCommonService.kt @@ -25,25 +25,43 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.tencent.devops.auth.service +package com.tencent.devops.process.service.view -import com.tencent.devops.auth.dao.AuthGroupPermissionDao +import com.tencent.devops.common.api.util.HashUtil +import com.tencent.devops.process.dao.label.PipelineViewGroupDao import org.jooq.DSLContext import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Service @Service -class GroupPermissionService @Autowired constructor( - val dslContext: DSLContext, - val groupPermissionDao: AuthGroupPermissionDao +class PipelineViewGroupCommonService @Autowired constructor( + private val pipelineViewGroupDao: PipelineViewGroupDao, + private val dslContext: DSLContext ) { - - fun getPermissionByGroupCode(groupCode: String): List? { - val permissionRecord = groupPermissionDao.getByGroupCode(dslContext, groupCode) - var permissionList = mutableListOf() - if (permissionRecord != null) { - permissionList = permissionRecord.map { it.authAction } + fun listPipelineIdsByViewIds(projectId: String, viewIdsEncode: List): List { + val viewIds = viewIdsEncode.map { HashUtil.decodeIdToLong(it) } + val pipelineIds = mutableListOf() + val viewGroups = pipelineViewGroupDao.listByViewIds(dslContext, projectId, viewIds) + if (viewGroups.isEmpty()) { + pipelineIds.addAll(emptyList()) + } else { + pipelineIds.addAll(viewGroups.map { it.pipelineId }.toList()) + } + if (pipelineIds.isEmpty()) { + pipelineIds.add("##NONE##") // 特殊标志,避免有些判空逻辑导致过滤器没有执行 } - return permissionList + return pipelineIds + } + + fun listViewIdsByPipelineId(projectId: String, pipelineId: String): Set { + return pipelineViewGroupDao.listByPipelineId(dslContext, projectId, pipelineId).map { it.viewId }.toSet() + } + + fun listViewIdsMap(projectId: String, pipelineIds: List): Map> { + return pipelineViewGroupDao.listByPipelineIds( + dslContext = dslContext, + projectId = projectId, + pipelineIds = pipelineIds + ).groupBy({ it.pipelineId }, { it.viewId }) } } diff --git a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/view/PipelineViewGroupService.kt b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/view/PipelineViewGroupService.kt index 95954fe05e8..f9292c49997 100644 --- a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/view/PipelineViewGroupService.kt +++ b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/service/view/PipelineViewGroupService.kt @@ -41,6 +41,7 @@ import com.tencent.devops.common.api.util.Watcher import com.tencent.devops.common.api.util.timestamp import com.tencent.devops.common.audit.ActionAuditContent import com.tencent.devops.common.auth.api.ActionId +import com.tencent.devops.common.auth.api.AuthPermission import com.tencent.devops.common.auth.api.ResourceTypeId import com.tencent.devops.common.client.Client import com.tencent.devops.common.client.ClientTokenService @@ -58,6 +59,7 @@ import com.tencent.devops.process.dao.label.PipelineViewTopDao import com.tencent.devops.process.engine.dao.PipelineInfoDao import com.tencent.devops.process.engine.dao.PipelineYamlViewDao import com.tencent.devops.process.enums.OperationLogType +import com.tencent.devops.process.permission.PipelinePermissionService import com.tencent.devops.process.pojo.classify.PipelineNewView import com.tencent.devops.process.pojo.classify.PipelineNewViewSummary import com.tencent.devops.process.pojo.classify.PipelineViewBulkAdd @@ -95,7 +97,9 @@ class PipelineViewGroupService @Autowired constructor( private val client: Client, private val clientTokenService: ClientTokenService, private val operationLogService: PipelineOperationLogService, - private val pipelineYamlViewDao: PipelineYamlViewDao + private val pipelineYamlViewDao: PipelineYamlViewDao, + private val pipelinePermissionService: PipelinePermissionService, + private val pipelineViewGroupCommonService: PipelineViewGroupCommonService ) { private val allPipelineInfoCache = Caffeine.newBuilder() .maximumSize(10) @@ -326,18 +330,7 @@ class PipelineViewGroupService @Autowired constructor( } fun listPipelineIdsByViewIds(projectId: String, viewIdsEncode: List): List { - val viewIds = viewIdsEncode.map { HashUtil.decodeIdToLong(it) } - val pipelineIds = mutableListOf() - val viewGroups = pipelineViewGroupDao.listByViewIds(dslContext, projectId, viewIds) - if (viewGroups.isEmpty()) { - pipelineIds.addAll(emptyList()) - } else { - pipelineIds.addAll(viewGroups.map { it.pipelineId }.toList()) - } - if (pipelineIds.isEmpty()) { - pipelineIds.add("##NONE##") // 特殊标志,避免有些判空逻辑导致过滤器没有执行 - } - return pipelineIds + return pipelineViewGroupCommonService.listPipelineIdsByViewIds(projectId, viewIdsEncode) } fun listPipelineIdsByViewId(projectId: String, viewIdEncode: String): List { @@ -837,7 +830,20 @@ class PipelineViewGroupService @Autowired constructor( fun listView(userId: String, projectId: String, projected: Boolean?, viewType: Int?): List { val views = pipelineViewDao.list(dslContext, userId, projectId, projected, viewType) - val countByViewId = pipelineViewGroupDao.countByViewId(dslContext, projectId, views.map { it.id }) + val isControlPipelineListPermission = pipelinePermissionService.isControlPipelineListPermission(projectId) + val authPipelines = if (isControlPipelineListPermission) { + pipelinePermissionService.getResourceByPermission( + userId = userId, projectId = projectId, permission = AuthPermission.LIST + ) + } else { + null + } + val countByViewId = pipelineViewGroupDao.countByViewId( + dslContext = dslContext, + projectId = projectId, + viewIds = views.map { it.id }, + filterPipelineIds = authPipelines + ) val yamlViews = pipelineYamlViewDao.listViewIds(dslContext, projectId) // 确保数据都初始化一下 views.filter { it.viewType == PipelineViewType.DYNAMIC } @@ -846,20 +852,26 @@ class PipelineViewGroupService @Autowired constructor( if (projected != false) { val classifiedPipelineIds = getClassifiedPipelineIds(projectId) val unclassifiedCount = - pipelineInfoDao.countExcludePipelineIds(dslContext, projectId, classifiedPipelineIds, ChannelCode.BS) - summaries.add( - 0, PipelineNewViewSummary( - id = PIPELINE_VIEW_UNCLASSIFIED, + pipelineInfoDao.countExcludePipelineIds( + dslContext = dslContext, projectId = projectId, - name = I18nUtil.getCodeLanMessage(PIPELINE_VIEW_UNCLASSIFIED), - projected = true, - createTime = LocalDateTime.now().timestamp(), - updateTime = LocalDateTime.now().timestamp(), - creator = "admin", - top = false, - viewType = PipelineViewType.UNCLASSIFIED, - pipelineCount = unclassifiedCount + excludePipelineIds = classifiedPipelineIds, + channelCode = ChannelCode.BS, + filterPipelineIds = authPipelines ) + summaries.add( + 0, PipelineNewViewSummary( + id = PIPELINE_VIEW_UNCLASSIFIED, + projectId = projectId, + name = I18nUtil.getCodeLanMessage(PIPELINE_VIEW_UNCLASSIFIED), + projected = true, + createTime = LocalDateTime.now().timestamp(), + updateTime = LocalDateTime.now().timestamp(), + creator = "admin", + top = false, + viewType = PipelineViewType.UNCLASSIFIED, + pipelineCount = unclassifiedCount + ) ) } return summaries @@ -972,15 +984,7 @@ class PipelineViewGroupService @Autowired constructor( } fun listViewIdsByPipelineId(projectId: String, pipelineId: String): Set { - return pipelineViewGroupDao.listByPipelineId(dslContext, projectId, pipelineId).map { it.viewId }.toSet() - } - - fun listViewIdsMap(projectId: String, pipelineIds: List): Map> { - return pipelineViewGroupDao.listByPipelineIds( - dslContext = dslContext, - projectId = projectId, - pipelineIds = pipelineIds - ).groupBy({ it.pipelineId }, { it.viewId }) + return pipelineViewGroupCommonService.listViewIdsByPipelineId(projectId, pipelineId) } fun listViewIdsByProjectId(projectId: String): Set { diff --git a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/yaml/exception/hanlder/YamlTriggerExceptionUtil.kt b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/yaml/exception/hanlder/YamlTriggerExceptionUtil.kt index fc94adb36af..3f875e34f5c 100644 --- a/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/yaml/exception/hanlder/YamlTriggerExceptionUtil.kt +++ b/src/backend/ci/core/process/biz-process/src/main/kotlin/com/tencent/devops/process/yaml/exception/hanlder/YamlTriggerExceptionUtil.kt @@ -52,7 +52,7 @@ object YamlTriggerExceptionUtil { PipelineTriggerFailedMsg(exception.message ?: PipelineTriggerReason.UNKNOWN_ERROR.detail) ) else -> Pair( - PipelineTriggerReason.UNKNOWN_ERROR.name, + PipelineTriggerReason.TRIGGER_FAILED.name, PipelineTriggerFailedMsg(exception.message ?: PipelineTriggerReason.UNKNOWN_ERROR.detail) ) } diff --git a/src/backend/ci/core/process/biz-process/src/test/kotlin/com/tencent/devops/process/service/view/PipelineViewGroupServiceTest.kt b/src/backend/ci/core/process/biz-process/src/test/kotlin/com/tencent/devops/process/service/view/PipelineViewGroupServiceTest.kt index 039ff64eaa9..74c52929c5e 100644 --- a/src/backend/ci/core/process/biz-process/src/test/kotlin/com/tencent/devops/process/service/view/PipelineViewGroupServiceTest.kt +++ b/src/backend/ci/core/process/biz-process/src/test/kotlin/com/tencent/devops/process/service/view/PipelineViewGroupServiceTest.kt @@ -56,6 +56,7 @@ class PipelineViewGroupServiceTest : BkCiAbstractTest() { private val clientTokenService: ClientTokenService = mockk() private val pipelineYamlViewDao: PipelineYamlViewDao = mockk() private val operationLogService: PipelineOperationLogService = mockk() + private val pipelineViewGroupCommonService: PipelineViewGroupCommonService = mockk() private val self: PipelineViewGroupService = spyk( PipelineViewGroupService( @@ -70,7 +71,17 @@ class PipelineViewGroupServiceTest : BkCiAbstractTest() { client = client, clientTokenService = clientTokenService, operationLogService = operationLogService, - pipelineYamlViewDao = pipelineYamlViewDao + pipelineYamlViewDao = pipelineYamlViewDao, + pipelinePermissionService = pipelinePermissionService, + pipelineViewGroupCommonService = pipelineViewGroupCommonService + ), + recordPrivateCalls = true + ) + + private val self1: PipelineViewGroupCommonService = spyk( + PipelineViewGroupCommonService( + pipelineViewGroupDao = pipelineViewGroupDao, + dslContext = dslContext ), recordPrivateCalls = true ) @@ -393,7 +404,7 @@ class PipelineViewGroupServiceTest : BkCiAbstractTest() { @DisplayName("当ViewGroup为空列表时") fun test_1() { every { pipelineViewGroupDao.listByViewIds(anyDslContext(), any(), any()) } returns emptyList() - self.listPipelineIdsByViewIds("test", listOf("test")).let { + self1.listPipelineIdsByViewIds("test", listOf("test")).let { Assertions.assertTrue(it.size == 1) Assertions.assertEquals(it[0], "##NONE##") } @@ -403,7 +414,7 @@ class PipelineViewGroupServiceTest : BkCiAbstractTest() { @DisplayName("当ViewGroup不为空列表时") fun test_2() { every { pipelineViewGroupDao.listByViewIds(anyDslContext(), any(), any()) } returns listOf(pvg) - self.listPipelineIdsByViewIds("test", listOf("test")).let { + self1.listPipelineIdsByViewIds("test", listOf("test")).let { Assertions.assertTrue(it.size == 1) Assertions.assertEquals(it[0], pvg.pipelineId) } @@ -904,8 +915,11 @@ class PipelineViewGroupServiceTest : BkCiAbstractTest() { @DisplayName("是项目流水线组") fun test_1() { every { pipelineViewDao.list(anyDslContext(), any(), any(), any(), any()) } returns emptyList() - every { pipelineViewGroupDao.countByViewId(anyDslContext(), any(), any()) } returns emptyMap() + every { pipelineViewGroupDao.countByViewId(anyDslContext(), any(), any(), any()) } returns emptyMap() every { pipelineYamlViewDao.listViewIds(anyDslContext(), any()) } returns emptyList() + every { pipelinePermissionService.getResourceByPermission(any(), any(), any()) } returns emptyList() + every { pipelinePermissionService.isControlPipelineListPermission(any()) } returns true + every { self["sortViews2Summary"]( any() as String, @@ -924,8 +938,10 @@ class PipelineViewGroupServiceTest : BkCiAbstractTest() { @DisplayName("不是项目流水线组") fun test_2() { every { pipelineViewDao.list(anyDslContext(), any(), any(), any(), any()) } returns emptyList() - every { pipelineViewGroupDao.countByViewId(anyDslContext(), any(), any()) } returns emptyMap() + every { pipelineViewGroupDao.countByViewId(anyDslContext(), any(), any(), any()) } returns emptyMap() every { pipelineYamlViewDao.listViewIds(anyDslContext(), any()) } returns emptyList() + every { pipelinePermissionService.getResourceByPermission(any(), any(), any()) } returns emptyList() + every { pipelinePermissionService.isControlPipelineListPermission(any()) } returns true every { self["sortViews2Summary"]( any() as String, @@ -936,7 +952,16 @@ class PipelineViewGroupServiceTest : BkCiAbstractTest() { ) } returns mutableListOf() every { self["getClassifiedPipelineIds"](any() as String) } returns emptyList() - every { pipelineInfoDao.countExcludePipelineIds(anyDslContext(), any(), any(), any()) } returns 0 + every { + pipelineInfoDao.countExcludePipelineIds( + dslContext = anyDslContext(), + projectId = any(), + excludePipelineIds = any(), + channelCode = any(), + includeDelete = any(), + filterPipelineIds = any() + ) + } returns 0 self.listView("test", "test", true, PipelineViewType.DYNAMIC).let { Assertions.assertEquals(it.size, 1) Assertions.assertEquals(it[0].id, PIPELINE_VIEW_UNCLASSIFIED) diff --git a/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/api/service/ServiceProjectResource.kt b/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/api/service/ServiceProjectResource.kt index 294e28697d2..302852b5ac3 100644 --- a/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/api/service/ServiceProjectResource.kt +++ b/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/api/service/ServiceProjectResource.kt @@ -36,14 +36,15 @@ import com.tencent.devops.common.auth.api.pojo.ProjectConditionDTO import com.tencent.devops.common.auth.api.pojo.SubjectScopeInfo import com.tencent.devops.project.pojo.OrgInfo import com.tencent.devops.project.pojo.ProjectBaseInfo +import com.tencent.devops.project.pojo.ProjectByConditionDTO import com.tencent.devops.project.pojo.ProjectCreateInfo import com.tencent.devops.project.pojo.ProjectCreateUserInfo import com.tencent.devops.project.pojo.ProjectOrganizationInfo import com.tencent.devops.project.pojo.ProjectProperties import com.tencent.devops.project.pojo.ProjectUpdateInfo import com.tencent.devops.project.pojo.ProjectVO -import com.tencent.devops.project.pojo.ProjectByConditionDTO import com.tencent.devops.project.pojo.Result +import com.tencent.devops.project.pojo.enums.PluginDetailsDisplayOrder import com.tencent.devops.project.pojo.enums.ProjectChannelCode import com.tencent.devops.project.pojo.enums.ProjectValidateType import io.swagger.v3.oas.annotations.Operation @@ -378,4 +379,15 @@ interface ServiceProjectResource { @QueryParam("englishName") englishName: List ): Result?> + + @PUT + @Path("{projectId}/updatePluginDetailsDisplay") + @Operation(summary = "更新插件展示顺序") + fun updatePluginDetailsDisplay( + @Parameter(description = "项目Code", required = true) + @PathParam("projectId") + projectId: String, + @Parameter(description = "插件展示顺序", required = true) + pluginDetailsDisplayOrder: List + ): Result } diff --git a/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/api/user/UserProjectResource.kt b/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/api/user/UserProjectResource.kt index eda2451c862..e8f127c08cd 100644 --- a/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/api/user/UserProjectResource.kt +++ b/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/api/user/UserProjectResource.kt @@ -34,6 +34,7 @@ import com.tencent.devops.common.api.auth.AUTH_HEADER_USER_ID_DEFAULT_VALUE import com.tencent.devops.common.api.pojo.Pagination import com.tencent.devops.common.auth.api.AuthPermission import com.tencent.devops.project.pojo.OperationalProductVO +import com.tencent.devops.project.pojo.ProjectByConditionDTO import com.tencent.devops.project.pojo.ProjectCollation import com.tencent.devops.project.pojo.ProjectCreateInfo import com.tencent.devops.project.pojo.ProjectDiffVO @@ -41,7 +42,6 @@ import com.tencent.devops.project.pojo.ProjectLogo import com.tencent.devops.project.pojo.ProjectSortType import com.tencent.devops.project.pojo.ProjectUpdateInfo import com.tencent.devops.project.pojo.ProjectVO -import com.tencent.devops.project.pojo.ProjectByConditionDTO import com.tencent.devops.project.pojo.Result import com.tencent.devops.project.pojo.enums.ProjectValidateType import io.swagger.v3.oas.annotations.Operation @@ -372,4 +372,16 @@ interface UserProjectResource { @PathParam("bgName") bgName: String ): Result> + + @Operation(summary = "获取项目级流水线方言, 流水线编辑/修改时调用") + @GET + @Path("{projectId}/pipelineDialect") + fun getPipelineDialect( + @Parameter(description = "用户ID", required = true, example = AUTH_HEADER_USER_ID_DEFAULT_VALUE) + @HeaderParam(AUTH_HEADER_USER_ID) + userId: String, + @Parameter(description = "项目ID", required = true) + @PathParam("projectId") + projectId: String + ): Result } diff --git a/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/pojo/ProjectApprovalInfo.kt b/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/pojo/ProjectApprovalInfo.kt index c2e7fbb0947..e616522fab0 100644 --- a/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/pojo/ProjectApprovalInfo.kt +++ b/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/pojo/ProjectApprovalInfo.kt @@ -81,5 +81,7 @@ data class ProjectApprovalInfo( @get:Schema(title = "运营产品ID") val productId: Int? = null, @get:Schema(title = "运营产品名称") - val productName: String? = null + val productName: String? = null, + @get:Schema(title = "项目相关配置") + val properties: ProjectProperties? = null ) diff --git a/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/pojo/ProjectDiffVO.kt b/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/pojo/ProjectDiffVO.kt index 4a61a381064..1096d030ade 100644 --- a/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/pojo/ProjectDiffVO.kt +++ b/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/pojo/ProjectDiffVO.kt @@ -120,5 +120,9 @@ data class ProjectDiffVO( @get:Schema(title = "运营产品名称") val productName: String? = null, @get:Schema(title = "审批中运营产品名称") - val afterProductName: String? = null + val afterProductName: String? = null, + @get:Schema(title = "流水线语言风格") + val pipelineDialect: String? = null, + @get:Schema(title = "审批中流水线语言风格") + val afterPipelineDialect: String? = null ) diff --git a/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/pojo/ProjectProperties.kt b/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/pojo/ProjectProperties.kt index 2d33acb0ac4..95f524fe914 100644 --- a/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/pojo/ProjectProperties.kt +++ b/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/pojo/ProjectProperties.kt @@ -28,6 +28,7 @@ package com.tencent.devops.project.pojo import com.tencent.devops.common.api.pojo.PipelineAsCodeSettings +import com.tencent.devops.project.pojo.enums.PluginDetailsDisplayOrder import io.swagger.v3.oas.annotations.media.Schema @Schema(title = "项目其他配置") @@ -49,5 +50,15 @@ data class ProjectProperties( @get:Schema(title = "当项目不活跃时,是否禁用") var disableWhenInactive: Boolean? = null, @get:Schema(title = "该项目是否开启流水线可观测数据", required = false) - val buildMetrics: Boolean? = null + val buildMetrics: Boolean? = null, + @get:Schema(title = "是否控制流水线列表权限", required = false) + var pipelineListPermissionControl: Boolean? = null, + @get:Schema(title = "插件详情展示顺序", required = false) + var pluginDetailsDisplayOrder: List? = listOf( + PluginDetailsDisplayOrder.LOG, + PluginDetailsDisplayOrder.ARTIFACT, + PluginDetailsDisplayOrder.CONFIG + ), + @get:Schema(title = "流水线语法风格") + var pipelineDialect: String? = "CLASSIC" ) diff --git a/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/pojo/ProjectUpdateInfo.kt b/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/pojo/ProjectUpdateInfo.kt index a61e85e4f31..22d2fd7c453 100644 --- a/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/pojo/ProjectUpdateInfo.kt +++ b/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/pojo/ProjectUpdateInfo.kt @@ -66,7 +66,7 @@ data class ProjectUpdateInfo( @get:Schema(title = "是否保密") var secrecy: Boolean = false, @get:Schema(title = "项目相关配置") - val properties: ProjectProperties? = null, + var properties: ProjectProperties? = null, @get:Schema(title = "项目最大可授权人员范围") val subjectScopes: List? = emptyList(), @get:Schema(title = "logo地址") diff --git a/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/pojo/enums/PluginDetailsDisplayOrder.kt b/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/pojo/enums/PluginDetailsDisplayOrder.kt new file mode 100644 index 00000000000..91d9dcc4e48 --- /dev/null +++ b/src/backend/ci/core/project/api-project/src/main/kotlin/com/tencent/devops/project/pojo/enums/PluginDetailsDisplayOrder.kt @@ -0,0 +1,7 @@ +package com.tencent.devops.project.pojo.enums + +enum class PluginDetailsDisplayOrder { + LOG, + ARTIFACT, + CONFIG +} diff --git a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/dao/ProjectApprovalDao.kt b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/dao/ProjectApprovalDao.kt index 5918e9d4a2e..8dfd138cf41 100644 --- a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/dao/ProjectApprovalDao.kt +++ b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/dao/ProjectApprovalDao.kt @@ -36,6 +36,7 @@ import com.tencent.devops.model.project.tables.TProjectApproval import com.tencent.devops.model.project.tables.records.TProjectApprovalRecord import com.tencent.devops.project.pojo.ProjectApprovalInfo import com.tencent.devops.project.pojo.ProjectCreateInfo +import com.tencent.devops.project.pojo.ProjectProperties import com.tencent.devops.project.pojo.ProjectUpdateInfo import com.tencent.devops.project.pojo.enums.ProjectAuthSecrecyStatus import org.jooq.DSLContext @@ -78,7 +79,8 @@ class ProjectApprovalDao { TIPS_STATUS, PROJECT_TYPE, PRODUCT_ID, - PRODUCT_NAME + PRODUCT_NAME, + PROPERTIES ).values( projectCreateInfo.projectName, projectCreateInfo.englishName, @@ -102,7 +104,10 @@ class ProjectApprovalDao { tipsStatus, projectCreateInfo.projectType, projectCreateInfo.productId, - projectCreateInfo.productName + projectCreateInfo.productName, + projectCreateInfo.properties?.let { + JsonUtil.toJson(it, false) + } ).onDuplicateKeyUpdate() .set(PROJECT_NAME, projectCreateInfo.projectName) .set(DESCRIPTION, projectCreateInfo.description) @@ -122,6 +127,9 @@ class ProjectApprovalDao { .set(PROJECT_TYPE, projectCreateInfo.projectType) .set(PRODUCT_ID, projectCreateInfo.productId) .set(PRODUCT_NAME, projectCreateInfo.productName) + .set(PROPERTIES, projectCreateInfo.properties?.let { + JsonUtil.toJson(it, false) + }) .execute() } } @@ -156,6 +164,9 @@ class ProjectApprovalDao { .set(PROJECT_TYPE, projectUpdateInfo.projectType) .set(PRODUCT_ID, projectUpdateInfo.productId) .set(PRODUCT_NAME, projectUpdateInfo.productName) + .set(PROPERTIES, projectUpdateInfo.properties?.let { + JsonUtil.toJson(it, false) + }) .where(ENGLISH_NAME.eq(projectUpdateInfo.englishName)) .execute() } @@ -187,6 +198,9 @@ class ProjectApprovalDao { .set(PROJECT_TYPE, projectApprovalInfo.projectType) .set(PRODUCT_ID, projectApprovalInfo.productId) .set(PRODUCT_NAME, projectApprovalInfo.productName) + .set(PROPERTIES, projectApprovalInfo.properties?.let { + JsonUtil.toJson(it, false) + }) .where(ENGLISH_NAME.eq(projectApprovalInfo.englishName)) .execute() } @@ -287,7 +301,8 @@ class ProjectApprovalDao { tipsStatus = tipsStatus, projectType = projectType, productId = productId, - productName = productName + productName = productName, + properties = properties?.let { JsonUtil.to(it, ProjectProperties::class.java) } ) } } diff --git a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/dao/ProjectDao.kt b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/dao/ProjectDao.kt index e29c002f4b0..75b337443c8 100644 --- a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/dao/ProjectDao.kt +++ b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/dao/ProjectDao.kt @@ -48,9 +48,6 @@ import com.tencent.devops.project.pojo.enums.ProjectAuthSecrecyStatus import com.tencent.devops.project.pojo.enums.ProjectChannelCode import com.tencent.devops.project.pojo.user.UserDeptDetail import com.tencent.devops.project.util.ProjectUtils -import java.net.URLDecoder -import java.time.LocalDateTime -import java.util.Locale import org.jooq.Condition import org.jooq.DSLContext import org.jooq.Record @@ -61,6 +58,9 @@ import org.jooq.Result import org.jooq.impl.DSL import org.jooq.impl.DSL.lower import org.springframework.stereotype.Repository +import java.net.URLDecoder +import java.time.LocalDateTime +import java.util.Locale @Suppress("ALL") @Repository diff --git a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/resources/ServiceProjectResourceImpl.kt b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/resources/ServiceProjectResourceImpl.kt index 92299bf2244..b04b607d3c4 100644 --- a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/resources/ServiceProjectResourceImpl.kt +++ b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/resources/ServiceProjectResourceImpl.kt @@ -46,6 +46,7 @@ import com.tencent.devops.project.pojo.ProjectUpdateInfo import com.tencent.devops.project.pojo.ProjectVO import com.tencent.devops.project.pojo.ProjectByConditionDTO import com.tencent.devops.project.pojo.Result +import com.tencent.devops.project.pojo.enums.PluginDetailsDisplayOrder import com.tencent.devops.project.pojo.enums.ProjectChannelCode import com.tencent.devops.project.pojo.enums.ProjectValidateType import com.tencent.devops.project.service.ProjectOrganizationService @@ -276,4 +277,16 @@ class ServiceProjectResourceImpl @Autowired constructor( projectService.getExistedEnglishName(englishName) ) } + + override fun updatePluginDetailsDisplay( + projectId: String, + pluginDetailsDisplayOrder: List + ): Result { + return Result( + projectService.updatePluginDetailsDisplay( + englishName = projectId, + pluginDetailsDisplayOrder = pluginDetailsDisplayOrder + ) + ) + } } diff --git a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/resources/UserProjectResourceImpl.kt b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/resources/UserProjectResourceImpl.kt index 827713fc742..5dc5d7e84de 100644 --- a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/resources/UserProjectResourceImpl.kt +++ b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/resources/UserProjectResourceImpl.kt @@ -39,6 +39,7 @@ import com.tencent.devops.common.web.utils.I18nUtil import com.tencent.devops.project.api.user.UserProjectResource import com.tencent.devops.project.constant.ProjectMessageCode.PROJECT_NOT_EXIST import com.tencent.devops.project.pojo.OperationalProductVO +import com.tencent.devops.project.pojo.ProjectByConditionDTO import com.tencent.devops.project.pojo.ProjectCollation import com.tencent.devops.project.pojo.ProjectCreateExtInfo import com.tencent.devops.project.pojo.ProjectCreateInfo @@ -47,7 +48,6 @@ import com.tencent.devops.project.pojo.ProjectLogo import com.tencent.devops.project.pojo.ProjectSortType import com.tencent.devops.project.pojo.ProjectUpdateInfo import com.tencent.devops.project.pojo.ProjectVO -import com.tencent.devops.project.pojo.ProjectByConditionDTO import com.tencent.devops.project.pojo.Result import com.tencent.devops.project.pojo.enums.ProjectChannelCode import com.tencent.devops.project.pojo.enums.ProjectValidateType @@ -272,4 +272,8 @@ class UserProjectResourceImpl @Autowired constructor( projectService.getOperationalProductsByBgName(bgName) ) } + + override fun getPipelineDialect(userId: String, projectId: String): Result { + return Result(projectService.getPipelineDialect(projectId = projectId)) + } } diff --git a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/ProjectApprovalService.kt b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/ProjectApprovalService.kt index 788a42bde66..0bf229b60c9 100644 --- a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/ProjectApprovalService.kt +++ b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/ProjectApprovalService.kt @@ -41,6 +41,7 @@ import com.tencent.devops.project.dao.ProjectUpdateHistoryDao import com.tencent.devops.project.pojo.ProjectApprovalInfo import com.tencent.devops.project.pojo.ProjectCreateExtInfo import com.tencent.devops.project.pojo.ProjectCreateInfo +import com.tencent.devops.project.pojo.ProjectProperties import com.tencent.devops.project.pojo.ProjectUpdateInfo import com.tencent.devops.project.pojo.enums.ProjectApproveStatus import com.tencent.devops.project.pojo.enums.ProjectTipsStatus @@ -257,6 +258,15 @@ class ProjectApprovalService @Autowired constructor( params = arrayOf(projectId), defaultMessage = "project $projectId is not exist" ) + // 属性只能变更前端展示的,其他的字段由op变更 + val projectProperties = projectInfo.properties?.let { JsonUtil.to(it, ProjectProperties::class.java) } + val updateProjectProperties = if (projectApprovalInfo.properties != null) { + projectProperties?.copy( + pipelineDialect = projectApprovalInfo.properties!!.pipelineDialect + ) + } else { + projectProperties + } val projectUpdateInfo = with(projectApprovalInfo) { ProjectUpdateInfo( projectName = projectName, @@ -277,7 +287,8 @@ class ProjectApprovalService @Autowired constructor( ccAppName = projectInfo.ccAppName, kind = projectInfo.kind, projectType = projectType ?: 0, - productId = projectApprovalInfo.productId + productId = projectApprovalInfo.productId, + properties = updateProjectProperties ) } val logoAddress = projectUpdateInfo.logoAddress diff --git a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/ProjectService.kt b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/ProjectService.kt index 967e9c1169f..c59da263686 100644 --- a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/ProjectService.kt +++ b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/ProjectService.kt @@ -35,6 +35,7 @@ import com.tencent.devops.common.auth.api.pojo.SubjectScopeInfo import com.tencent.devops.model.project.tables.records.TProjectRecord import com.tencent.devops.project.pojo.OperationalProductVO import com.tencent.devops.project.pojo.ProjectBaseInfo +import com.tencent.devops.project.pojo.ProjectByConditionDTO import com.tencent.devops.project.pojo.ProjectCollation import com.tencent.devops.project.pojo.ProjectCreateExtInfo import com.tencent.devops.project.pojo.ProjectCreateInfo @@ -47,8 +48,8 @@ import com.tencent.devops.project.pojo.ProjectSortType import com.tencent.devops.project.pojo.ProjectUpdateCreatorDTO import com.tencent.devops.project.pojo.ProjectUpdateInfo import com.tencent.devops.project.pojo.ProjectVO -import com.tencent.devops.project.pojo.ProjectByConditionDTO import com.tencent.devops.project.pojo.Result +import com.tencent.devops.project.pojo.enums.PluginDetailsDisplayOrder import com.tencent.devops.project.pojo.enums.ProjectChannelCode import com.tencent.devops.project.pojo.enums.ProjectValidateType import org.glassfish.jersey.media.multipart.FormDataContentDisposition @@ -284,4 +285,11 @@ interface ProjectService { userId: String, englishName: String ): Boolean + + fun updatePluginDetailsDisplay( + englishName: String, + pluginDetailsDisplayOrder: List + ): Boolean + + fun getPipelineDialect(projectId: String): String } diff --git a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/impl/AbsProjectServiceImpl.kt b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/impl/AbsProjectServiceImpl.kt index 4ac1aeebc18..2052f0b2ff3 100644 --- a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/impl/AbsProjectServiceImpl.kt +++ b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/service/impl/AbsProjectServiceImpl.kt @@ -62,6 +62,7 @@ import com.tencent.devops.common.auth.code.ProjectAuthServiceCode import com.tencent.devops.common.client.Client import com.tencent.devops.common.client.ClientTokenService import com.tencent.devops.common.event.dispatcher.SampleEventDispatcher +import com.tencent.devops.common.pipeline.dialect.PipelineDialectType import com.tencent.devops.common.redis.RedisOperation import com.tencent.devops.common.service.Profile import com.tencent.devops.common.service.utils.LogUtils @@ -97,6 +98,7 @@ import com.tencent.devops.project.pojo.ProjectUpdateInfo import com.tencent.devops.project.pojo.ProjectVO import com.tencent.devops.project.pojo.ResourceUpdateInfo import com.tencent.devops.project.pojo.Result +import com.tencent.devops.project.pojo.enums.PluginDetailsDisplayOrder import com.tencent.devops.project.pojo.enums.ProjectApproveStatus import com.tencent.devops.project.pojo.enums.ProjectChannelCode import com.tencent.devops.project.pojo.enums.ProjectOperation @@ -549,6 +551,13 @@ abstract class AbsProjectServiceImpl @Autowired constructor( ) } } + // 属性只能变更前端展示的,其他的字段由op变更 + val properties = projectInfo.properties?.let { JsonUtil.to(it, ProjectProperties::class.java) } + ?: ProjectProperties() + if (projectUpdateInfo.properties != null) { + projectUpdateInfo.properties = + properties.copy(pipelineDialect = projectUpdateInfo.properties!!.pipelineDialect) + } // 判断是否需要审批,当修改最大授权范围/权限敏感/关联运营产品时需要审批 val (finalNeedApproval, newApprovalStatus) = getUpdateApprovalStatus( needApproval = needApproval, @@ -569,7 +578,9 @@ abstract class AbsProjectServiceImpl @Autowired constructor( originalProjectName = projectInfo.projectName, modifiedProjectName = projectUpdateInfo.projectName, finalNeedApproval = finalNeedApproval, - beforeSubjectScopes = JsonUtil.to(projectInfo.subjectScopes, object : TypeReference>() {}), + beforeSubjectScopes = JsonUtil.to( + projectInfo.subjectScopes, object : TypeReference>() {} + ), afterSubjectScopes = subjectScopes )) { modifyProjectAuthResource(resourceUpdateInfo) @@ -619,8 +630,12 @@ abstract class AbsProjectServiceImpl @Autowired constructor( afterProjectName = projectUpdateInfo.projectName, beforeProductId = projectInfo.productId, afterProductId = projectUpdateInfo.productId, - beforeOrganization = with(projectInfo) { getOrganizationStr(bgName, businessLineName, deptName, centerName) }, - afterOrganization = with(projectUpdateInfo) { getOrganizationStr(bgName, businessLineName, deptName, centerName) }, + beforeOrganization = with(projectInfo) { + getOrganizationStr(bgName, businessLineName, deptName, centerName) + }, + afterOrganization = with(projectUpdateInfo) { + getOrganizationStr(bgName, businessLineName, deptName, centerName) + }, beforeSubjectScopes = projectInfo.subjectScopes, afterSubjectScopes = subjectScopesStr, operator = userId, @@ -725,7 +740,10 @@ abstract class AbsProjectServiceImpl @Autowired constructor( // 判断是否需要审批 return if (approveStatus.isSuccess()) { val isSubjectScopesChange = isSubjectScopesChange( - beforeSubjectScopes = JsonUtil.to(projectInfo.subjectScopes, object : TypeReference>() {}), + beforeSubjectScopes = JsonUtil.to( + projectInfo.subjectScopes, + object : TypeReference>() {} + ), afterSubjectScopes = afterSubjectScopes ) // 当项目创建成功,则只有最大授权范围和项目性质修改才审批 @@ -1599,6 +1617,38 @@ abstract class AbsProjectServiceImpl @Autowired constructor( deptName: String? ) + override fun updatePluginDetailsDisplay( + englishName: String, + pluginDetailsDisplayOrder: List + ): Boolean { + logger.info("update plugin details display|$englishName|$pluginDetailsDisplayOrder") + val projectInfo = getByEnglishName(englishName) + ?: throw NotFoundException("project - $englishName is not exist!") + + val validDisplayOrder = setOf( + PluginDetailsDisplayOrder.LOG, + PluginDetailsDisplayOrder.ARTIFACT, + PluginDetailsDisplayOrder.CONFIG + ) + + val isParamsLegal = pluginDetailsDisplayOrder.size == 3 && pluginDetailsDisplayOrder.toSet() == validDisplayOrder + + if (isParamsLegal) { + val properties = projectInfo.properties ?: ProjectProperties() + properties.pluginDetailsDisplayOrder = pluginDetailsDisplayOrder + updateProjectProperties(null, englishName, properties) + } else { + throw IllegalArgumentException("The parameter is invalid. It must contain LOG, ARTIFACT, CONFIG in any order.") + } + + return true + } + + override fun getPipelineDialect(projectId: String): String { + return getByEnglishName(englishName = projectId)?.properties?.pipelineDialect + ?: PipelineDialectType.CLASSIC.name + } + companion object { const val MAX_PROJECT_NAME_LENGTH = 64 private val logger = LoggerFactory.getLogger(AbsProjectServiceImpl::class.java)!! diff --git a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/util/ProjectUtils.kt b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/util/ProjectUtils.kt index 4d21fac15f5..3fa1e431c43 100644 --- a/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/util/ProjectUtils.kt +++ b/src/backend/ci/core/project/biz-project/src/main/kotlin/com/tencent/devops/project/util/ProjectUtils.kt @@ -110,7 +110,9 @@ object ProjectUtils { pipelineLimit = pipelineLimit, routerTag = routerTag, relationId = relationId, - properties = properties.takeIf { !it.isNullOrBlank() }?.let { JsonUtil.to(it, ProjectProperties::class.java) }, + properties = properties.takeIf { !it.isNullOrBlank() } + ?.let { JsonUtil.to(it, ProjectProperties::class.java) } + ?: ProjectProperties(), subjectScopes = subjectScopes.takeIf { !it.isNullOrBlank() }?.let { JsonUtil.to(it, object : TypeReference>() {}) }, @@ -136,6 +138,8 @@ object ProjectUtils { JsonUtil.to(it, object : TypeReference>() {}) } return with(tProjectRecord) { + val projectProperties = properties?.let { JsonUtil.to(it, ProjectProperties::class.java) } + val projectApprovalProperties = projectApprovalInfo?.properties ProjectDiffVO( id = id, projectId = projectId, @@ -190,7 +194,9 @@ object ProjectUtils { productId = productId, afterProductId = projectApprovalInfo?.productId, productName = beforeProductName, - afterProductName = projectApprovalInfo?.productName + afterProductName = projectApprovalInfo?.productName, + pipelineDialect = projectProperties?.pipelineDialect, + afterPipelineDialect = projectApprovalProperties?.pipelineDialect ) } } diff --git a/src/backend/ci/core/repository/api-repository/src/main/kotlin/com/tencent/devops/repository/sdk/github/response/PullRequestResponse.kt b/src/backend/ci/core/repository/api-repository/src/main/kotlin/com/tencent/devops/repository/sdk/github/response/PullRequestResponse.kt index 1499bc0be3e..2e9a50cfa83 100644 --- a/src/backend/ci/core/repository/api-repository/src/main/kotlin/com/tencent/devops/repository/sdk/github/response/PullRequestResponse.kt +++ b/src/backend/ci/core/repository/api-repository/src/main/kotlin/com/tencent/devops/repository/sdk/github/response/PullRequestResponse.kt @@ -11,7 +11,7 @@ import com.tencent.devops.repository.sdk.github.pojo.PullRequestLabel @JsonIgnoreProperties(ignoreUnknown = true) data class PullRequestResponse( val url: String, - val id: Int, + val id: Long, @JsonProperty("node_id") val nodeId: String, @JsonProperty("html_url") diff --git a/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/code/CodeGitRepositoryService.kt b/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/code/CodeGitRepositoryService.kt index cbc9e51386c..a8a6744a5e8 100644 --- a/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/code/CodeGitRepositoryService.kt +++ b/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/code/CodeGitRepositoryService.kt @@ -175,7 +175,7 @@ class CodeGitRepositoryService @Autowired constructor( ).url var gitProjectId: Long? = null // 需要更新gitProjectId - if (sourceUrl != repository.url) { + if (sourceUrl != repository.url || repository.gitProjectId == null || repository.gitProjectId == 0L) { logger.info( "repository url unMatch,need change gitProjectId,sourceUrl=[$sourceUrl] targetUrl=[${repository.url}]" ) @@ -418,6 +418,15 @@ class CodeGitRepositoryService @Autowired constructor( userName = userId, event = CodeGitWebhookEvent.MERGE_REQUESTS_EVENTS.value ) + // 修复历史数据 + if (repository.gitProjectId == null || repository.gitProjectId == 0L) { + val repositoryId = HashUtil.decodeOtherIdToLong(repository.repoHashId!!) + repositoryCodeGitDao.updateGitProjectId( + dslContext = dslContext, + id = repositoryId, + gitProjectId = gitProjectInfo.id + ) + } } override fun getGitFileTree(projectId: String, userId: String, record: TRepositoryRecord): List { diff --git a/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/code/CodeGithubRepositoryService.kt b/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/code/CodeGithubRepositoryService.kt index ab5a0362ef3..a92d7e01ab9 100644 --- a/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/code/CodeGithubRepositoryService.kt +++ b/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/code/CodeGithubRepositoryService.kt @@ -127,7 +127,7 @@ class CodeGithubRepositoryService @Autowired constructor( repositoryId = repositoryId ).url var gitProjectId: Long? = null - if (sourceUrl != repository.url) { + if (sourceUrl != repository.url || repository.gitProjectId == null || repository.gitProjectId == 0L) { logger.info( "repository url unMatch,need change gitProjectId,sourceUrl=[$sourceUrl] " + "targetUrl=[${repository.url}]" diff --git a/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/code/CodeGitlabRepositoryService.kt b/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/code/CodeGitlabRepositoryService.kt index 3746f088acd..1add0515c76 100644 --- a/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/code/CodeGitlabRepositoryService.kt +++ b/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/code/CodeGitlabRepositoryService.kt @@ -128,7 +128,7 @@ class CodeGitlabRepositoryService @Autowired constructor( val repositoryId = HashUtil.decodeOtherIdToLong(repositoryHashId) var gitProjectId: Long? = null // 需要更新gitProjectId - if (record.url != repository.url) { + if (record.url != repository.url || repository.gitProjectId == null || repository.gitProjectId == 0L) { logger.info( "repository url unMatch,need change gitProjectId,sourceUrl=[${record.url}] " + "targetUrl=[${repository.url}]" diff --git a/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/code/CodeTGitRepositoryService.kt b/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/code/CodeTGitRepositoryService.kt index 5374f049a52..34fcdecd8d9 100644 --- a/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/code/CodeTGitRepositoryService.kt +++ b/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/code/CodeTGitRepositoryService.kt @@ -127,7 +127,7 @@ class CodeTGitRepositoryService @Autowired constructor( val repositoryId = HashUtil.decodeOtherIdToLong(repositoryHashId) var gitProjectId: Long? = null // 需要更新gitProjectId - if (record.url != repository.url) { + if (record.url != repository.url || repository.gitProjectId == null || repository.gitProjectId == 0L) { logger.info( "repository url unMatch,need change gitProjectId,sourceUrl=[${record.url}] " + "targetUrl=[${repository.url}]" diff --git a/src/backend/ci/core/store/biz-store/src/main/kotlin/com/tencent/devops/store/atom/dao/AtomDao.kt b/src/backend/ci/core/store/biz-store/src/main/kotlin/com/tencent/devops/store/atom/dao/AtomDao.kt index a92e1a663a3..5b32480ec18 100644 --- a/src/backend/ci/core/store/biz-store/src/main/kotlin/com/tencent/devops/store/atom/dao/AtomDao.kt +++ b/src/backend/ci/core/store/biz-store/src/main/kotlin/com/tencent/devops/store/atom/dao/AtomDao.kt @@ -99,6 +99,7 @@ import org.jooq.Record3 import org.jooq.Result import org.jooq.SelectOnConditionStep import org.jooq.impl.DSL +import org.jooq.impl.DSL.countDistinct import org.springframework.stereotype.Repository @Suppress("ALL") @@ -827,7 +828,7 @@ class AtomDao : AtomBaseDao() { taf: TAtomFeature, tsst: TStoreStatisticsTotal ): SelectOnConditionStep> { - return dslContext.select(DSL.countDistinct(ta.ATOM_CODE)).from(ta) + return dslContext.select(countDistinct(ta.ATOM_CODE)).from(ta) .leftJoin(taf) .on(ta.ATOM_CODE.eq(taf.ATOM_CODE)) .leftJoin(tsst) @@ -1113,25 +1114,25 @@ class AtomDao : AtomBaseDao() { */ fun countInstalledAtoms( dslContext: DSLContext, - projectCode: String, + projectCode: String? = null, classifyCode: String? = null, - name: String? = null, - queryDefaultFlag: Boolean = false + name: String? = null ): Int { val (ta, tspr, conditions) = getInstalledConditions( projectCode = projectCode, classifyCode = classifyCode, name = name, - dslContext = dslContext, - queryDefaultFlag = queryDefaultFlag + dslContext = dslContext ) - return dslContext.select(DSL.countDistinct(ta.ATOM_CODE)) - .from(ta) - .join(tspr) - .on(ta.ATOM_CODE.eq(tspr.STORE_CODE)) - .where(conditions) - .fetchOne(0, Int::class.java)!! + val step = dslContext.select(countDistinct(ta.ATOM_CODE)).from(ta) + if (!projectCode.isNullOrBlank()) { + step.join(tspr).on(ta.ATOM_CODE.eq(tspr.STORE_CODE)) + } + val tc = TClassify.T_CLASSIFY + return step.join(tc) + .on(ta.CLASSIFY_ID.eq(tc.ID)) + .where(conditions).fetchOne(0, Int::class.java)!! } /** @@ -1143,16 +1144,14 @@ class AtomDao : AtomBaseDao() { classifyCode: String? = null, name: String? = null, page: Int? = null, - pageSize: Int? = null, - queryDefaultFlag: Boolean = false + pageSize: Int? = null ): Result? { val (ta, tspr, conditions) = getInstalledConditions( projectCode = projectCode, classifyCode = classifyCode, name = name, - dslContext = dslContext, - queryDefaultFlag = queryDefaultFlag + dslContext = dslContext ) val tc = TClassify.T_CLASSIFY // 查找每组atomCode最新的记录 @@ -1194,20 +1193,23 @@ class AtomDao : AtomBaseDao() { } private fun getInstalledConditions( - projectCode: String, + projectCode: String? = null, classifyCode: String?, name: String?, - dslContext: DSLContext, - queryDefaultFlag: Boolean + dslContext: DSLContext ): Triple> { val ta = TAtom.T_ATOM val tspr = TStoreProjectRel.T_STORE_PROJECT_REL val conditions = mutableListOf() - if (queryDefaultFlag) { - conditions.add(ta.DEFAULT_FLAG.eq(true).or(tspr.PROJECT_CODE.eq(projectCode).and(tspr.STORE_TYPE.eq(0)))) + if (projectCode.isNullOrBlank()) { + conditions.add(ta.DEFAULT_FLAG.eq(true)) + conditions.add(ta.ATOM_STATUS.eq(AtomStatusEnum.RELEASED.status.toByte())) } else { conditions.add(tspr.PROJECT_CODE.eq(projectCode).and(tspr.STORE_TYPE.eq(0))) + conditions.add(tspr.TYPE.eq(StoreProjectTypeEnum.COMMON.type.toByte())) + conditions.add(ta.DEFAULT_FLAG.eq(false)) } + if (!classifyCode.isNullOrEmpty()) { val tClassify = TClassify.T_CLASSIFY val classifyId = dslContext.select(tClassify.ID) @@ -1422,4 +1424,56 @@ class AtomDao : AtomBaseDao() { } } } + + /** + * 获取默认插件 + */ + fun getDefaultAtoms( + dslContext: DSLContext, + classifyCode: String? = null, + name: String? = null, + offset: Int? = null, + limit: Int? = null + ): Result? { + + val (ta, _, conditions) = getInstalledConditions( + classifyCode = classifyCode, + name = name, + dslContext = dslContext + ) + val tc = TClassify.T_CLASSIFY + // 查找每组atomCode最新的记录 + val t = dslContext.select(ta.ATOM_CODE.`as`(KEY_ATOM_CODE), DSL.max(ta.CREATE_TIME).`as`(KEY_CREATE_TIME)) + .from(ta) + .where(ta.DEFAULT_FLAG.eq(true).and(ta.ATOM_STATUS.eq(AtomStatusEnum.RELEASED.status.toByte()))) + .groupBy(ta.ATOM_CODE) + + val sql = dslContext.select( + ta.ID.`as`(KEY_ID), + ta.ATOM_CODE.`as`(KEY_ATOM_CODE), + ta.VERSION.`as`(KEY_VERSION), + ta.NAME.`as`(NAME), + ta.LOGO_URL.`as`(KEY_LOGO_URL), + ta.CATEGROY.`as`(KEY_CATEGORY), + ta.SUMMARY.`as`(KEY_SUMMARY), + ta.PUBLISHER.`as`(KEY_PUBLISHER), + ta.DEFAULT_FLAG.`as`(KEY_DEFAULT_FLAG), + tc.ID.`as`(KEY_CLASSIFY_ID), + tc.CLASSIFY_CODE.`as`(KEY_CLASSIFY_CODE), + tc.CLASSIFY_NAME.`as`(KEY_CLASSIFY_NAME) + ) + .from(ta) + .join(t) + .on( + ta.ATOM_CODE.eq(t.field(KEY_ATOM_CODE, String::class.java)) + .and(ta.CREATE_TIME.eq(t.field(KEY_CREATE_TIME, LocalDateTime::class.java))) + ) + .join(tc) + .on(ta.CLASSIFY_ID.eq(tc.ID)) + .where(conditions) + .groupBy(ta.ATOM_CODE) + .orderBy(ta.CREATE_TIME, ta.ID) + if (offset != null && limit != null) sql.offset(offset).limit(limit) + return sql.skipCheck().fetch() + } } diff --git a/src/backend/ci/core/store/biz-store/src/main/kotlin/com/tencent/devops/store/atom/service/impl/AtomServiceImpl.kt b/src/backend/ci/core/store/biz-store/src/main/kotlin/com/tencent/devops/store/atom/service/impl/AtomServiceImpl.kt index ef8eff088f0..5cd045a7b2d 100644 --- a/src/backend/ci/core/store/biz-store/src/main/kotlin/com/tencent/devops/store/atom/service/impl/AtomServiceImpl.kt +++ b/src/backend/ci/core/store/biz-store/src/main/kotlin/com/tencent/devops/store/atom/service/impl/AtomServiceImpl.kt @@ -134,6 +134,7 @@ import java.time.LocalDateTime import java.util.concurrent.TimeUnit import org.apache.commons.collections4.ListUtils import org.jooq.DSLContext +import org.jooq.Record import org.jooq.impl.DSL import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired @@ -1056,43 +1057,110 @@ abstract class AtomServiceImpl @Autowired constructor() : AtomService { ): Page { // 项目下已安装插件记录 val result = mutableListOf() - val count = atomDao.countInstalledAtoms( + val defaultAtomCount = atomDao.countInstalledAtoms( dslContext = dslContext, - projectCode = projectCode, classifyCode = classifyCode, - name = name, - queryDefaultFlag = true + name = name ) - if (count == 0) { - return Page(page, pageSize, 0, result) - } - val records = atomDao.getInstalledAtoms( + val projectAtomCount = atomDao.countInstalledAtoms( dslContext = dslContext, projectCode = projectCode, classifyCode = classifyCode, - name = name, - page = page, - pageSize = pageSize, - queryDefaultFlag = true + name = name ) + val count = projectAtomCount + defaultAtomCount + if (count == 0) { + return Page(page, pageSize, 0, result) + } + var records: org.jooq.Result? = null val atomCodeList = mutableListOf() + if (projectAtomCount > (page - 1) * pageSize) { + records = atomDao.getInstalledAtoms( + dslContext = dslContext, + projectCode = projectCode, + classifyCode = classifyCode, + name = name, + page = page, + pageSize = pageSize + ) + } records?.forEach { atomCodeList.add(it[KEY_ATOM_CODE] as String) } + // 查询完项目下安装插件则开始查询默认插件 + var defaultAtoms: org.jooq.Result? = null + if (records.isNullOrEmpty() || records.size < pageSize) { + var limit = pageSize + var offset = 0 + // 通过计算已分页总量与项目下已安装插件的差值确定查询默认插件的起始值 + val thresholdNum = (page - 1) * pageSize - projectAtomCount + if (thresholdNum > 0) { + offset = thresholdNum + } else { + limit = page * pageSize - projectAtomCount + } + logger.info("getDefaultAtoms projectCode:$projectCode |page:$page|pageSize:$pageSize offset:$offset|limit:$limit") + defaultAtoms = atomDao.getDefaultAtoms( + dslContext = dslContext, + classifyCode = classifyCode, + name = name, + offset = offset, + limit = limit + ) + } + defaultAtoms?.forEach { + atomCodeList.add(it[KEY_ATOM_CODE] as String) + } + // 插件关联的流水线 val pipelineStat = client.get(ServiceMeasurePipelineResource::class).batchGetPipelineCountByAtomCode( atomCodes = atomCodeList.joinToString(","), projectCode = projectCode ).data val hasManagerPermission = hasManagerPermission(projectCode, userId) - records?.forEach { + records?.let { + convertInstalledAtom( + userId = userId, + records = it, + pipelineStat = pipelineStat, + hasManagerPermission = hasManagerPermission, + result = result + ) + } + defaultAtoms?.let { + convertInstalledAtom( + userId = userId, + records = it, + pipelineStat = pipelineStat, + hasManagerPermission = hasManagerPermission, + result = result + ) + } + return Page(page, pageSize, count.toLong(), result) + } + + private fun convertInstalledAtom( + userId: String, + records: org.jooq.Result, + pipelineStat: Map?, + hasManagerPermission: Boolean, + result: MutableList + ) { + records.forEach { val atomCode = it[KEY_ATOM_CODE] as String - val installer = it[KEY_INSTALLER] as String - val installType = it[KEY_INSTALL_TYPE] as Byte + val default = it[KEY_DEFAULT_FLAG] as Boolean + val installer = if (default) { + SYSTEM + } else { + it[KEY_INSTALLER] as String + } // 判断项目是否是初始化项目或者调试项目 - val isInitTest = installType == StoreProjectTypeEnum.INIT.type.toByte() || - installType == StoreProjectTypeEnum.TEST.type.toByte() + val isInitTest = if (default) false else { + val installType = it[KEY_INSTALL_TYPE] as? Byte + installType == StoreProjectTypeEnum.INIT.type.toByte() || + installType == StoreProjectTypeEnum.TEST.type.toByte() + } val atomClassifyCode = it[KEY_CLASSIFY_CODE] as String val classifyName = it[KEY_CLASSIFY_NAME] as String val classifyLanName = I18nUtil.getCodeLanMessage( @@ -1100,7 +1168,6 @@ abstract class AtomServiceImpl @Autowired constructor() : AtomService { defaultMessage = classifyName ) val logoUrl = it[KEY_LOGO_URL] as? String - val default = it[KEY_DEFAULT_FLAG] as Boolean result.add( InstalledAtom( atomId = it[KEY_ID] as String, @@ -1115,9 +1182,15 @@ abstract class AtomServiceImpl @Autowired constructor() : AtomService { category = AtomCategoryEnum.getAtomCategory((it[KEY_CATEGORY] as Byte).toInt()), summary = it[KEY_SUMMARY] as? String, publisher = it[KEY_PUBLISHER] as? String, - installer = if (default) SYSTEM else installer, - installTime = DateTimeUtil.toDateTime(it[KEY_INSTALL_TIME] as LocalDateTime), - installType = StoreProjectTypeEnum.getProjectType((it[KEY_INSTALL_TYPE] as Byte).toInt()), + installer = installer, + installTime = if (default) { + "" + } else { + DateTimeUtil.toDateTime(it[KEY_INSTALL_TIME] as LocalDateTime) + }, + installType = if (default) { + StoreProjectTypeEnum.COMMON.name + } else { StoreProjectTypeEnum.getProjectType((it[KEY_INSTALL_TYPE] as Byte).toInt()) }, pipelineCnt = pipelineStat?.get(atomCode) ?: 0, hasPermission = if (default) { false @@ -1127,7 +1200,6 @@ abstract class AtomServiceImpl @Autowired constructor() : AtomService { ) ) } - return Page(page, pageSize, count.toLong(), result) } /** diff --git a/src/backend/ci/core/worker/build.gradle.kts b/src/backend/ci/core/worker/build.gradle.kts index 6d112723b4f..52f512667d4 100644 --- a/src/backend/ci/core/worker/build.gradle.kts +++ b/src/backend/ci/core/worker/build.gradle.kts @@ -29,7 +29,6 @@ subprojects { configurations.forEach { it.exclude(group = "com.perforce", module = "*") - it.exclude(group = "com.google.guava", module = "*") it.exclude(group = "com.googlecode.javaewah", module = "*") it.exclude(group = "com.vdurmont", module = "*") it.exclude(group = "com.github.ulisesbocchio", module = "*") diff --git a/src/backend/ci/core/worker/worker-common/src/main/java/com/tencent/process/BkProcessTree.java b/src/backend/ci/core/worker/worker-common/src/main/java/com/tencent/process/BkProcessTree.java index 84364562140..99a1c1c1878 100644 --- a/src/backend/ci/core/worker/worker-common/src/main/java/com/tencent/process/BkProcessTree.java +++ b/src/backend/ci/core/worker/worker-common/src/main/java/com/tencent/process/BkProcessTree.java @@ -34,11 +34,16 @@ import com.sun.jna.ptr.NativeLongByReference; import com.tencent.process.ProcessTreeRemoting.IOSProcess; import com.tencent.process.ProcessTreeRemoting.IProcessTree; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.jvnet.winp.WinProcess; +import org.jvnet.winp.WinpException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; @@ -59,16 +64,9 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; -import java.util.Map.Entry; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; -import org.jvnet.winp.WinProcess; -import org.jvnet.winp.WinpException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import static com.sun.jna.Pointer.NULL; import static com.tencent.process.jna.GNUCLibrary.LIBC; @@ -78,8 +76,7 @@ public abstract class BkProcessTree implements Iterable protected final Map processes; private transient volatile List killers; private static final boolean IS_LITTLE_ENDIAN = "little".equals(System.getProperty("sun.cpu.endian")); - public static boolean enabled = !Boolean.getBoolean(BkProcessTree.class.getName() + ".disable"); - private static Logger logger = LoggerFactory.getLogger(BkProcessTree.class); + private static final Logger logger = LoggerFactory.getLogger(BkProcessTree.class); private BkProcessTree() { this.processes = new HashMap<>(); @@ -133,17 +130,17 @@ public static BkProcessTree get() { } String os = Util.fixNull(System.getProperty("os.name")); - if (os.equals("Linux")) { - return new BkProcessTree.Linux(); + switch (os) { + case "Linux": + return new Linux(); + case "SunOS": + return new Solaris(); + case "Mac OS X": + return new Darwin(); + default: + return new Default(); } - if (os.equals("SunOS")) { - return new BkProcessTree.Solaris(); - } - - if (os.equals("Mac OS X")) { - return new BkProcessTree.Darwin(); - } } catch (Exception var1) { log("Failed to load winp. Reverting to the default", var1); } @@ -170,7 +167,6 @@ public void killRecursively(boolean forceFlag) { public void kill0(boolean forceFlag) throws InterruptedException { proc.destroy(); - this.killByKiller(); } public List getArguments() { @@ -190,6 +186,10 @@ public void killAll(Map modelEnvVars, boolean forceFlag) { private static class Darwin extends Unix { Darwin() { String arch = System.getProperty("sun.arch.data.model"); + // local constants + int sizeOf_kinfo_proc; + int kinfo_proc_pid_offset; + int kinfo_proc_ppid_offset; if ("64".equals(arch)) { sizeOf_kinfo_proc = sizeOf_kinfo_proc_64; kinfo_proc_pid_offset = kinfo_proc_pid_offset_64; @@ -219,7 +219,7 @@ private static class Darwin extends Unix { } int count = size.getValue().intValue() / sizeOf_kinfo_proc; - logger.info("Found " + count + " processes"); + logger.info("Found {} processes", count); for (int base = 0; base < size.getValue().intValue(); base += sizeOf_kinfo_proc) { int pid = m.getInt(base + kinfo_proc_pid_offset); @@ -282,7 +282,7 @@ private void parse() { class StringArrayMemory extends Memory { private long offset = 0; - private long length = 0; + private long length; StringArrayMemory(long l) { super(l); @@ -394,14 +394,10 @@ void skip0() { } } - // local constants - private final int sizeOf_kinfo_proc; private static final int sizeOf_kinfo_proc_32 = 492; // on 32bit Mac OS X. private static final int sizeOf_kinfo_proc_64 = 648; // on 64bit Mac OS X. - private final int kinfo_proc_pid_offset; private static final int kinfo_proc_pid_offset_32 = 24; private static final int kinfo_proc_pid_offset_64 = 40; - private final int kinfo_proc_ppid_offset; private static final int kinfo_proc_ppid_offset_32 = 416; private static final int kinfo_proc_ppid_offset_64 = 560; private static final int sizeOfInt = Native.getNativeSize(int.class); @@ -409,7 +405,7 @@ void skip0() { private static final int KERN_PROC = 14; private static final int KERN_PROC_ALL = 0; private static final int ENOMEM = 12; - private static int[] MIB_PROC_ALL = {CTL_KERN, KERN_PROC, KERN_PROC_ALL}; + private static final int[] MIB_PROC_ALL = {CTL_KERN, KERN_PROC, KERN_PROC_ALL}; private static final int KERN_ARGMAX = 8; private static final int KERN_PROCARGS2 = 49; } @@ -467,15 +463,12 @@ public synchronized List getArguments() { if (this.arguments == null) { this.arguments = new ArrayList<>(this.argc); - try { - - try (RandomAccessFile as = new RandomAccessFile(this.getFile("as"), "r")) { - BkProcessTree.log("Reading " + this.getFile("as")); - for (int n = 0; n < this.argc; ++n) { - as.seek(Solaris.to64(this.argp + n * 4)); - int p = Solaris.adjust(as.readInt()); - this.arguments.add(this.readLine(as, p, "argv[" + n + "]")); - } + try (RandomAccessFile as = new RandomAccessFile(this.getFile("as"), "r")) { + BkProcessTree.log("Reading " + this.getFile("as")); + for (int n = 0; n < this.argc; ++n) { + as.seek(Solaris.to64(this.argp + n * 4)); + int p = Solaris.adjust(as.readInt()); + this.arguments.add(this.readLine(as, p, "argv[" + n + "]")); } } catch (IOException var8) { log(var8.getMessage()); @@ -490,22 +483,19 @@ public synchronized EnvVars getEnvironmentVariables() { if (this.envVars == null) { this.envVars = new EnvVars(); - try { - - try (RandomAccessFile as = new RandomAccessFile(this.getFile("as"), "r")) { - BkProcessTree.log("Reading " + this.getFile("as")); - int n = 0; - - while (true) { - as.seek(Solaris.to64(this.envp + n * 4)); - int p = Solaris.adjust(as.readInt()); - if (p == 0) { - break; - } + try (RandomAccessFile as = new RandomAccessFile(this.getFile("as"), "r")) { + BkProcessTree.log("Reading " + this.getFile("as")); + int n = 0; - this.envVars.addLine(this.readLine(as, p, "env[" + n + "]")); - ++n; + while (true) { + as.seek(Solaris.to64(this.envp + n * 4)); + int p = Solaris.adjust(as.readInt()); + if (p == 0) { + break; } + + this.envVars.addLine(this.readLine(as, p, "env[" + n + "]")); + ++n; } } catch (IOException var8) { log(var8.getMessage()); @@ -524,7 +514,7 @@ private String readLine(RandomAccessFile as, int p, String prefix) throws IOExce for (int i = 0; (ch = as.read()) > 0; buf.write(ch)) { ++i; if (i % 100 == 0) { - BkProcessTree.log(prefix + " is so far " + buf.toString()); + BkProcessTree.log(prefix + " is so far " + buf); } } @@ -700,8 +690,6 @@ public void kill0(boolean forceFlag) throws InterruptedException { BkProcessTree.log("Failed to terminate pid=" + this.getPid(), var4); } - - this.killByKiller(); } public void killRecursively(boolean forceFlag) throws InterruptedException { @@ -719,11 +707,7 @@ public void killRecursively(boolean forceFlag) throws InterruptedException { abstract static class ProcfsUnix extends BkProcessTree.Unix { ProcfsUnix() { - File[] processes = (new File("/proc")).listFiles(new FileFilter() { - public boolean accept(File f) { - return f.isDirectory(); - } - }); + File[] processes = (new File("/proc")).listFiles(File::isDirectory); if (processes == null) { log("No /proc"); } else { @@ -790,13 +774,11 @@ public OSProcess getParent() { public void killRecursively(boolean forceFlag) throws InterruptedException { BkProcessTree.log("Killing recursively " + this.getPid()); p.killRecursively(); - this.killByKiller(); } public void kill0(boolean forceFlag) throws InterruptedException { BkProcessTree.log("Killing " + this.getPid()); p.kill(); - this.killByKiller(); } public synchronized List getArguments() { @@ -880,7 +862,7 @@ Object readResolve() { } public abstract class OSProcess implements IOSProcess, Serializable { - private Set keepAlivePids = new HashSet(64); + private final Set keepAlivePids = new HashSet<>(64); final int pid; @@ -898,10 +880,6 @@ public final void addKeepAlivePids(Collection pids) { public abstract BkProcessTree.OSProcess getParent(); - final BkProcessTree getTree() { - return BkProcessTree.this; - } - public final List getChildren() { List r = new ArrayList<>(); @@ -928,20 +906,6 @@ public void kill(boolean forceFlag) throws InterruptedException { public abstract void kill0(boolean forceFlag) throws InterruptedException; - void killByKiller() throws InterruptedException { - - for (ProcessKiller killer : BkProcessTree.this.getKillers()) { - try { - if (killer.kill(this)) { - break; - } - } catch (IOException var4) { - BkProcessTree.log("Failed to kill pid=" + this.getPid(), var4); - } - } - - } - public abstract void killRecursively(boolean forceFlag) throws InterruptedException; public abstract List getArguments(); diff --git a/src/backend/ci/core/worker/worker-common/src/main/java/com/tencent/process/QuotedStringTokenizer.java b/src/backend/ci/core/worker/worker-common/src/main/java/com/tencent/process/QuotedStringTokenizer.java index 47e8cae15ed..01732701f3f 100644 --- a/src/backend/ci/core/worker/worker-common/src/main/java/com/tencent/process/QuotedStringTokenizer.java +++ b/src/backend/ci/core/worker/worker-common/src/main/java/com/tencent/process/QuotedStringTokenizer.java @@ -56,6 +56,7 @@ * @see StringTokenizer * @author Greg Wilkins (gregw) */ +@SuppressWarnings("all") public class QuotedStringTokenizer extends StringTokenizer { diff --git a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/plugin/worker/task/archive/BuildPushDockerImageTask.kt b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/plugin/worker/task/archive/BuildPushDockerImageTask.kt index 896a0935e1c..2a796da7d2d 100644 --- a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/plugin/worker/task/archive/BuildPushDockerImageTask.kt +++ b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/plugin/worker/task/archive/BuildPushDockerImageTask.kt @@ -56,11 +56,11 @@ import com.tencent.devops.worker.common.logger.LoggerService import com.tencent.devops.worker.common.task.ITask import com.tencent.devops.worker.common.task.TaskClassType import com.tencent.devops.worker.common.task.script.CommandFactory -import java.io.File import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.Request import okhttp3.RequestBody import org.slf4j.LoggerFactory +import java.io.File @TaskClassType(classTypes = [BuildPushDockerImageElement.classType]) @Suppress("ALL") @@ -150,8 +150,7 @@ class BuildPushDockerImageTask : ITask() { runtimeVariables = runtimeVariables, projectId = projectId, dir = workspace, - buildEnvs = buildVariables.buildEnvs, - asCodeEnabled = buildVariables.pipelineAsCodeSettings?.enable + buildEnvs = buildVariables.buildEnvs ) LoggerService.addNormalLine("Start to build the docker image. imageName:$imageName; imageTag:$imageTag") @@ -165,8 +164,7 @@ class BuildPushDockerImageTask : ITask() { runtimeVariables = runtimeVariables, projectId = projectId, dir = workspace, - buildEnvs = buildVariables.buildEnvs, - asCodeEnabled = buildVariables.pipelineAsCodeSettings?.enable + buildEnvs = buildVariables.buildEnvs ) } catch (t: RuntimeException) { val message = MessageUtil.getMessageByLocale( @@ -191,8 +189,7 @@ class BuildPushDockerImageTask : ITask() { runtimeVariables = runtimeVariables, projectId = projectId, dir = workspace, - buildEnvs = buildVariables.buildEnvs, - asCodeEnabled = buildVariables.pipelineAsCodeSettings?.enable + buildEnvs = buildVariables.buildEnvs ) } } diff --git a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/Runner.kt b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/Runner.kt index 82c9adc42a3..9fa3abb4582 100644 --- a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/Runner.kt +++ b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/Runner.kt @@ -37,6 +37,7 @@ import com.tencent.devops.common.api.util.JsonUtil import com.tencent.devops.common.api.util.MessageUtil import com.tencent.devops.common.pipeline.EnvReplacementParser import com.tencent.devops.common.pipeline.NameAndValue +import com.tencent.devops.common.pipeline.dialect.PipelineDialectUtil import com.tencent.devops.common.pipeline.enums.BuildFormPropertyType import com.tencent.devops.common.pipeline.enums.BuildTaskStatus import com.tencent.devops.common.pipeline.pojo.BuildParameters @@ -45,6 +46,7 @@ import com.tencent.devops.process.engine.common.VMUtils import com.tencent.devops.process.pojo.BuildJobResult import com.tencent.devops.process.pojo.BuildTask import com.tencent.devops.process.pojo.BuildVariables +import com.tencent.devops.process.utils.PIPELINE_DIALECT import com.tencent.devops.process.utils.PIPELINE_RETRY_COUNT import com.tencent.devops.process.utils.PipelineVarUtil import com.tencent.devops.worker.common.constants.WorkerMessageCode.BK_PREPARE_TO_BUILD @@ -54,7 +56,6 @@ import com.tencent.devops.worker.common.constants.WorkerMessageCode.UNKNOWN_ERRO import com.tencent.devops.worker.common.env.AgentEnv import com.tencent.devops.worker.common.env.BuildEnv import com.tencent.devops.worker.common.env.BuildType -import com.tencent.devops.worker.common.env.DockerEnv import com.tencent.devops.worker.common.exception.TaskExecuteExceptionDecorator import com.tencent.devops.worker.common.expression.SpecialFunctions import com.tencent.devops.worker.common.heartbeat.Heartbeat @@ -66,10 +67,10 @@ import com.tencent.devops.worker.common.task.TaskFactory import com.tencent.devops.worker.common.utils.CredentialUtils import com.tencent.devops.worker.common.utils.KillBuildProcessTree import com.tencent.devops.worker.common.utils.ShellUtil +import org.slf4j.LoggerFactory import java.io.File import java.io.FileNotFoundException import java.io.IOException -import org.slf4j.LoggerFactory import kotlin.system.exitProcess object Runner { @@ -176,7 +177,7 @@ object Runner { if (endBuildFlag) { // 启动失败,尝试结束构建 try { - EngineService.endBuild(emptyMap(), DockerEnv.getBuildId(), BuildJobResult(ignored.message)) + EngineService.endBuild(emptyMap(), "", BuildJobResult(ignored.message)) } catch (ignored: Exception) { logger.warn("End build catch unknown exceptions", ignored) } @@ -444,6 +445,7 @@ object Runner { // 填充插件级的ENV参数 val customEnvStr = buildTask.params?.get(Element::customEnv.name) + val dialect = PipelineDialectUtil.getPipelineDialect(jobBuildVariables.variables[PIPELINE_DIALECT]) if (customEnvStr != null) { val customEnv = try { JsonUtil.toOrNull(customEnvStr, object : TypeReference>() {}) @@ -459,7 +461,7 @@ object Runner { val value = EnvReplacementParser.parse( value = it.value ?: "", contextMap = jobVariables, - onlyExpression = jobBuildVariables.pipelineAsCodeSettings?.enable, + onlyExpression = dialect.supportUseExpression(), functions = SpecialFunctions.functions, output = SpecialFunctions.output ) diff --git a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/service/CIKeywordsService.kt b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/service/CIKeywordsService.kt index cc7c015dc3c..6d9a2a81be0 100644 --- a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/service/CIKeywordsService.kt +++ b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/service/CIKeywordsService.kt @@ -6,6 +6,7 @@ import com.tencent.devops.common.expression.context.StringContextData import com.tencent.devops.worker.common.CI_TOKEN_CONTEXT import com.tencent.devops.worker.common.api.ApiFactory import com.tencent.devops.worker.common.api.engine.EngineBuildSDKApi +import org.slf4j.LoggerFactory object CIKeywordsService { private val buildApi = ApiFactory.create(EngineBuildSDKApi::class) @@ -26,6 +27,8 @@ object CIKeywordsService { override val key: String = "ci" ) : RuntimeNamedValue { override fun getValue(key: String): PipelineContextData? { + // TODO: 理论上不会走这里的代码逻辑,先打印日志保守点看看 + logger.info("CIKeywordsRuntimeNamedValue|ci.token") // 不是需要的关键字直接返回空 if (key != CI_TOKEN_CONTEXT.removePrefix("ci.")) { return null @@ -33,4 +36,6 @@ object CIKeywordsService { return StringContextData(getOrRequestToken() ?: "") } } + + private val logger = LoggerFactory.getLogger(CIKeywordsService::class.java) } diff --git a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/ITask.kt b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/ITask.kt index 90b707e2e39..6fe338fd723 100644 --- a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/ITask.kt +++ b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/ITask.kt @@ -30,21 +30,22 @@ package com.tencent.devops.worker.common.task import com.tencent.devops.common.api.exception.TaskExecuteException import com.tencent.devops.common.api.pojo.ErrorCode import com.tencent.devops.common.api.pojo.ErrorType +import com.tencent.devops.common.pipeline.dialect.IPipelineDialect +import com.tencent.devops.common.pipeline.dialect.PipelineDialectUtil import com.tencent.devops.common.pipeline.pojo.BuildParameters import com.tencent.devops.process.pojo.BuildTask import com.tencent.devops.process.pojo.BuildVariables +import com.tencent.devops.process.utils.PIPELINE_DIALECT +import com.tencent.devops.process.utils.PIPELINE_VARIABLES_STRING_LENGTH_MAX import com.tencent.devops.worker.common.env.BuildEnv import com.tencent.devops.worker.common.env.BuildType import com.tencent.devops.worker.common.logger.LoggerService import java.io.File +import java.util.regex.Pattern import java.util.stream.Collectors -import org.slf4j.LoggerFactory @Suppress("NestedBlockDepth", "TooManyFunctions") abstract class ITask { - - private val logger = LoggerFactory.getLogger(ITask::class.java) - private val environment = HashMap() private val monitorData = HashMap() @@ -57,6 +58,13 @@ abstract class ITask { /* 存储常量的key */ private lateinit var constVar: List + /* */ + private lateinit var dialect: IPipelineDialect + + companion object { + // 有的插件输出的变量会带taskId,taskId包含-,所以需要保留 + private const val ENGLISH_NAME_PATTERN = "[A-Za-z_][A-Za-z_0-9.-]*" + } fun run( buildTask: BuildTask, @@ -67,6 +75,7 @@ abstract class ITask { .filter { it.readOnly == true } .map { it.key } .collect(Collectors.toList()) + dialect = PipelineDialectUtil.getPipelineDialect(buildVariables.variables[PIPELINE_DIALECT]) execute(buildTask, buildVariables, workspace) } @@ -95,23 +104,60 @@ abstract class ITask { ) protected fun addEnv(env: Map) { - if (this::constVar.isInitialized) { - var errFlag = false - env.forEach { (key, _) -> - if (key in constVar) { - LoggerService.addErrorLine("Variable $key is read-only and cannot be modified.") - errFlag = true - } + var errReadOnlyFlag = false + var errLongValueFlag = false + val errLongValueVars = mutableSetOf() + var errChineseVarName = false + val errChineseVars = mutableSetOf() + env.forEach { (key, value) -> + if (this::constVar.isInitialized && key in constVar) { + LoggerService.addErrorLine("Variable $key is read-only and cannot be modified.") + errReadOnlyFlag = true + } + if (value.length >= PIPELINE_VARIABLES_STRING_LENGTH_MAX) { + errLongValueVars.add(key) + errLongValueFlag = true + } + if (!Pattern.matches(ENGLISH_NAME_PATTERN, key)) { + errChineseVars.add(key) + errChineseVarName = true + } + } + if (errReadOnlyFlag) { + throw TaskExecuteException( + errorMsg = "[Finish task] status: false, errorType: ${ErrorType.USER.num}, " + + "errorCode: ${ErrorCode.USER_INPUT_INVAILD}, message: read-only cannot be modified.", + errorType = ErrorType.USER, + errorCode = ErrorCode.USER_INPUT_INVAILD + ) + } + if (this::dialect.isInitialized) { + if (errLongValueFlag && !dialect.supportLongVarValue()) { + LoggerService.addWarnLine("Variable $errLongValueVars value exceeds 4000 length limit.") + throw TaskExecuteException( + errorMsg = "[Finish task] status: false, errorType: ${ErrorType.USER.num}, " + + "errorCode: ${ErrorCode.USER_INPUT_INVAILD}," + + " message: variable value exceeds 4000 length limit.", + errorType = ErrorType.USER, + errorCode = ErrorCode.USER_INPUT_INVAILD + ) } - if (errFlag) { + if (errChineseVarName && !dialect.supportChineseVarName()) { + LoggerService.addWarnLine( + "Variable $errChineseVars name is illegal,Variable names can only use letters, " + + "numbers and underscores, " + + "and the first character cannot start with a number" + ) throw TaskExecuteException( errorMsg = "[Finish task] status: false, errorType: ${ErrorType.USER.num}, " + - "errorCode: ${ErrorCode.USER_INPUT_INVAILD}, message: read-only cannot be modified.", + "errorCode: ${ErrorCode.USER_INPUT_INVAILD}," + + " message: variable name is illegal.", errorType = ErrorType.USER, errorCode = ErrorCode.USER_INPUT_INVAILD ) } } + environment.putAll(env) } diff --git a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/market/MarketAtomTask.kt b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/market/MarketAtomTask.kt index 03800282e45..ee8b1851af7 100644 --- a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/market/MarketAtomTask.kt +++ b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/market/MarketAtomTask.kt @@ -45,16 +45,19 @@ import com.tencent.devops.common.api.constant.VALUE import com.tencent.devops.common.api.enums.OSType import com.tencent.devops.common.api.exception.RemoteServiceException import com.tencent.devops.common.api.exception.TaskExecuteException +import com.tencent.devops.common.api.exception.VariableNotFoundException import com.tencent.devops.common.api.factory.BkDiskLruFileCacheFactory import com.tencent.devops.common.api.pojo.ErrorCode import com.tencent.devops.common.api.pojo.ErrorType -import com.tencent.devops.common.api.util.EnvUtils import com.tencent.devops.common.api.util.JsonUtil import com.tencent.devops.common.api.util.MessageUtil +import com.tencent.devops.common.api.util.ObjectReplaceEnvVarUtil import com.tencent.devops.common.api.util.ShaUtils import com.tencent.devops.common.archive.element.ReportArchiveElement import com.tencent.devops.common.pipeline.EnvReplacementParser import com.tencent.devops.common.pipeline.container.VMBuildContainer +import com.tencent.devops.common.pipeline.dialect.IPipelineDialect +import com.tencent.devops.common.pipeline.dialect.PipelineDialectUtil import com.tencent.devops.common.pipeline.enums.BuildStatus import com.tencent.devops.common.pipeline.pojo.element.Element import com.tencent.devops.common.service.utils.CommonUtils @@ -69,6 +72,7 @@ import com.tencent.devops.process.utils.PIPELINE_ATOM_CODE import com.tencent.devops.process.utils.PIPELINE_ATOM_NAME import com.tencent.devops.process.utils.PIPELINE_ATOM_TIMEOUT import com.tencent.devops.process.utils.PIPELINE_ATOM_VERSION +import com.tencent.devops.process.utils.PIPELINE_DIALECT import com.tencent.devops.process.utils.PIPELINE_START_USER_ID import com.tencent.devops.process.utils.PIPELINE_STEP_ID import com.tencent.devops.process.utils.PIPELINE_TASK_NAME @@ -156,10 +160,9 @@ open class MarketAtomTask : ITask() { val workspacePath = workspace.absolutePath // 输出参数的用户命名空间:防止重名窘况 val namespace: String? = map["namespace"] as String? - val asCodeEnabled = buildVariables.pipelineAsCodeSettings?.enable == true logger.info( "${buildTask.buildId}|RUN_ATOM|taskName=$taskName|ver=$atomVersion|code=$atomCode" + - "|workspace=$workspacePath|asCodeEnabled=$asCodeEnabled" + "|workspace=$workspacePath" ) // 获取插件基本信息 @@ -227,13 +230,14 @@ open class MarketAtomTask : ITask() { ) ) + val dialect = PipelineDialectUtil.getPipelineDialect(variables[PIPELINE_DIALECT]) // 解析并打印插件执行传入的所有参数 val inputParams = map["input"]?.let { input -> parseInputParams( inputMap = input as Map, variables = variables.plus(getContainerVariables(buildTask, buildVariables, workspacePath)), acrossInfo = acrossInfo, - asCodeEnabled = asCodeEnabled + dialect = dialect ) } ?: emptyMap() printInput(atomData, inputParams, inputTemplate) @@ -248,7 +252,7 @@ open class MarketAtomTask : ITask() { buildTask.stepId?.let { variables = variables.plus(PIPELINE_STEP_ID to it) } - val inputVariables = if (asCodeEnabled) { + val inputVariables = if (dialect.supportUseExpression()) { // 如果开启PAC,插件入参增加旧变量,防止开启PAC后,插件获取参数失败 PipelineVarUtil.mixOldVarAndNewVar(variables.toMutableMap()) } else { @@ -493,11 +497,11 @@ open class MarketAtomTask : ITask() { inputMap: Map, variables: Map, acrossInfo: BuildTemplateAcrossInfo?, - asCodeEnabled: Boolean + dialect: IPipelineDialect ): Map { val atomParams = mutableMapOf() try { - if (asCodeEnabled) { + if (dialect.supportUseExpression()) { val customReplacement = EnvReplacementParser.getCustomExecutionContextByMap( variables = variables, extendNamedValueMap = listOf( @@ -510,7 +514,7 @@ open class MarketAtomTask : ITask() { atomParams[name] = EnvReplacementParser.parse( value = JsonUtil.toJson(value), contextMap = variables, - onlyExpression = true, + dialect = dialect, contextPair = customReplacement, functions = SpecialFunctions.functions, output = SpecialFunctions.output @@ -519,12 +523,20 @@ open class MarketAtomTask : ITask() { } else { inputMap.forEach { (name, value) -> // 修复插件input环境变量替换问题 #5682 - atomParams[name] = EnvUtils.parseEnv( - command = JsonUtil.toJson(value), - data = variables + atomParams[name] = JsonUtil.toJson( + ObjectReplaceEnvVarUtil.replaceEnvVar(value, variables) ).parseCredentialValue(null, acrossInfo?.targetProjectId) } } + } catch (exception: VariableNotFoundException) { + val errMessage = "Variable ${exception.variableKey} not found" + LoggerService.addErrorLine(errMessage) + throw TaskExecuteException( + errorMsg = "[Finish task] status: false, errorType: ${ErrorType.USER.num}, " + + "errorCode: ${ErrorCode.USER_INPUT_INVAILD}, message: $errMessage", + errorType = ErrorType.USER, + errorCode = ErrorCode.USER_INPUT_INVAILD + ) } catch (e: Throwable) { logger.error("plugin input illegal! ", e) LoggerService.addErrorLine("plugin input illegal! ${e.message}") diff --git a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/ICommand.kt b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/ICommand.kt index 23f7f94753a..6be2d7876a1 100644 --- a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/ICommand.kt +++ b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/ICommand.kt @@ -30,6 +30,8 @@ package com.tencent.devops.worker.common.task.script import com.tencent.devops.common.api.util.KeyReplacement import com.tencent.devops.common.api.util.ReplacementUtils import com.tencent.devops.common.pipeline.EnvReplacementParser +import com.tencent.devops.common.pipeline.dialect.PipelineDialectUtil +import com.tencent.devops.process.utils.PIPELINE_DIALECT import com.tencent.devops.process.utils.PipelineVarUtil import com.tencent.devops.store.pojo.app.BuildEnv import com.tencent.devops.worker.common.CI_TOKEN_CONTEXT @@ -58,8 +60,7 @@ interface ICommand { jobId: String? = null, stepId: String? = null, charsetType: String? = null, - taskId: String? = null, - asCodeEnabled: Boolean? = null + taskId: String? = null ) fun parseTemplate( @@ -67,8 +68,7 @@ interface ICommand { command: String, variables: Map, dir: File, - taskId: String?, - asCodeEnabled: Boolean? + taskId: String? ): String { // 解析跨项目模板信息 val acrossTargetProjectId by lazy { @@ -83,11 +83,12 @@ interface ICommand { ).toMutableMap() // 增加上下文的替换 PipelineVarUtil.fillContextVarMap(contextMap) - return if (asCodeEnabled == true) { + val dialect = PipelineDialectUtil.getPipelineDialect(variables[PIPELINE_DIALECT]) + return if (dialect.supportUseExpression()) { EnvReplacementParser.parse( value = command, contextMap = contextMap, - onlyExpression = true, + dialect = dialect, contextPair = EnvReplacementParser.getCustomExecutionContextByMap( variables = contextMap, extendNamedValueMap = listOf( diff --git a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/ScriptTask.kt b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/ScriptTask.kt index e24f5fe3c51..cf666b39ca7 100644 --- a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/ScriptTask.kt +++ b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/ScriptTask.kt @@ -29,6 +29,7 @@ package com.tencent.devops.worker.common.task.script import com.tencent.bkrepo.repository.pojo.token.TokenType import com.tencent.devops.common.api.exception.TaskExecuteException +import com.tencent.devops.common.api.exception.VariableNotFoundException import com.tencent.devops.common.api.pojo.ErrorCode import com.tencent.devops.common.api.pojo.ErrorCode.USER_SCRIPT_TASK_FAIL import com.tencent.devops.common.api.pojo.ErrorType @@ -78,6 +79,7 @@ open class ScriptTask : ITask() { val continueNoneZero = taskParams["continueNoneZero"] ?: "false" // 如果脚本执行失败之后可以选择归档这个问题 val archiveFileIfExecFail = taskParams["archiveFile"] + val enableArchiveFile = taskParams["enableArchiveFile"]?.toBooleanStrictOrNull() val script = URLDecoder.decode( taskParams["script"] ?: throw TaskExecuteException( @@ -118,12 +120,20 @@ open class ScriptTask : ITask() { continueNoneZero = continueNoneZero.toBoolean(), errorMessage = "Fail to run the plugin", charsetType = charsetType, - taskId = buildTask.taskId, - asCodeEnabled = buildVariables.pipelineAsCodeSettings?.enable + taskId = buildTask.taskId + ) + } catch (exception: VariableNotFoundException) { + val errMessage = "Variable ${exception.variableKey} not found" + LoggerService.addErrorLine(errMessage) + throw TaskExecuteException( + errorMsg = "[Finish task] status: false, errorType: ${ErrorType.USER.num}, " + + "errorCode: ${ErrorCode.USER_INPUT_INVAILD}, message: $errMessage", + errorType = ErrorType.USER, + errorCode = ErrorCode.USER_INPUT_INVAILD ) } catch (ignore: Throwable) { logger.warn("Fail to run the script task", ignore) - if (!archiveFileIfExecFail.isNullOrBlank()) { + if (enableArchiveFile == true && !archiveFileIfExecFail.isNullOrBlank()) { LoggerService.addErrorLine( MessageUtil.getMessageByLocale( SCRIPT_EXECUTION_FAIL, diff --git a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/bat/CommandBatImpl.kt b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/bat/CommandBatImpl.kt index d75ca55bf48..373ec9e566d 100644 --- a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/bat/CommandBatImpl.kt +++ b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/bat/CommandBatImpl.kt @@ -47,10 +47,9 @@ class CommandBatImpl : ICommand { jobId: String?, stepId: String?, charsetType: String?, - taskId: String?, - asCodeEnabled: Boolean? + taskId: String? ) { - val realCommand = parseTemplate(buildId, script, taskParam.plus(runtimeVariables), dir, taskId, asCodeEnabled) + val realCommand = parseTemplate(buildId, script, taskParam.plus(runtimeVariables), dir, taskId) BatScriptUtil.execute( buildId = buildId, script = realCommand, diff --git a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/powershell/CommandPowerShellImpl.kt b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/powershell/CommandPowerShellImpl.kt index 2d39b0731bc..66b82888753 100644 --- a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/powershell/CommandPowerShellImpl.kt +++ b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/powershell/CommandPowerShellImpl.kt @@ -47,8 +47,7 @@ class CommandPowerShellImpl : ICommand { jobId: String?, stepId: String?, charsetType: String?, - taskId: String?, - asCodeEnabled: Boolean? + taskId: String? ) { TODO("Not yet implemented") } diff --git a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/shell/CommandShellImpl.kt b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/shell/CommandShellImpl.kt index 43537a1b8d6..e9a62255f99 100644 --- a/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/shell/CommandShellImpl.kt +++ b/src/backend/ci/core/worker/worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/script/shell/CommandShellImpl.kt @@ -47,16 +47,14 @@ class CommandShellImpl : ICommand { jobId: String?, stepId: String?, charsetType: String?, - taskId: String?, - asCodeEnabled: Boolean? + taskId: String? ) { val realCommand = parseTemplate( buildId = buildId, command = script, variables = taskParam.plus(runtimeVariables), dir = dir, - taskId = taskId, - asCodeEnabled = asCodeEnabled + taskId = taskId ) ShellUtil.execute( buildId = buildId, diff --git a/src/frontend/devops-atomstore/src/views/release_progress.vue b/src/frontend/devops-atomstore/src/views/release_progress.vue index 188fa93769d..b3e2149eb05 100755 --- a/src/frontend/devops-atomstore/src/views/release_progress.vue +++ b/src/frontend/devops-atomstore/src/views/release_progress.vue @@ -696,6 +696,9 @@ } .card-item { text-align: center; + word-wrap: break-word; + word-break: break-all; + padding: 5px; i { font-size: 12px; font-weight: bold; diff --git a/src/frontend/devops-pipeline/src/components/AtomFormComponent/InputParameterArray/index.vue b/src/frontend/devops-pipeline/src/components/AtomFormComponent/InputParameterArray/index.vue index 878314b75ff..408c56cf612 100644 --- a/src/frontend/devops-pipeline/src/components/AtomFormComponent/InputParameterArray/index.vue +++ b/src/frontend/devops-pipeline/src/components/AtomFormComponent/InputParameterArray/index.vue @@ -54,7 +54,7 @@ methods: { handleChangeInput (val, index) { this.parameters[index] = val - this.handleChange(this.name, this.parameters) + this.handleChange(this.name, this.parameters.filter(i => !!i)) }, plusParam () { this.parameters.push('') diff --git a/src/frontend/devops-pipeline/src/components/ExecDetail/Artifactory.vue b/src/frontend/devops-pipeline/src/components/ExecDetail/Artifactory.vue index f518fd72baf..6233e0fd107 100644 --- a/src/frontend/devops-pipeline/src/components/ExecDetail/Artifactory.vue +++ b/src/frontend/devops-pipeline/src/components/ExecDetail/Artifactory.vue @@ -77,6 +77,7 @@ import Logo from '@/components/Logo' import { extForFile } from '@/utils/pipelineConst' import { convertFileSize } from '@/utils/util' + import { SET_PLUGIN_HEAD_TAB } from '@/store/modules/atom/constants' export default { components: { @@ -107,6 +108,7 @@ created () { this.initData() + this.$store.commit(`atom/${SET_PLUGIN_HEAD_TAB}`, { isGetPluginHeadTab: false }) }, methods: { @@ -140,6 +142,9 @@ size: item.folder ? this.sizeFormatter(this.getFolderSize(item)) : this.sizeFormatter(item.size) })) || [] this.hasPermission = permission + if (res) { + this.$store.commit(`atom/${SET_PLUGIN_HEAD_TAB}`, { isGetPluginHeadTab: true }) + } if (this.artifactories.length > 0) { this.$emit('toggle', true) } diff --git a/src/frontend/devops-pipeline/src/components/ExecDetail/plugin.vue b/src/frontend/devops-pipeline/src/components/ExecDetail/plugin.vue index 1c391bccb77..814d43bfa7c 100644 --- a/src/frontend/devops-pipeline/src/components/ExecDetail/plugin.vue +++ b/src/frontend/devops-pipeline/src/components/ExecDetail/plugin.vue @@ -9,8 +9,9 @@ - diff --git a/src/frontend/devops-stream/src/components/StageReviewPanel/components/describe.vue b/src/frontend/devops-stream/src/components/StageReviewPanel/components/describe.vue index bed83e4904f..f116c130c8f 100644 --- a/src/frontend/devops-stream/src/components/StageReviewPanel/components/describe.vue +++ b/src/frontend/devops-stream/src/components/StageReviewPanel/components/describe.vue @@ -67,6 +67,7 @@ word-break: break-all; font-size: 12px; color: #666770; + white-space: pre; &.show-more { display: block; } diff --git a/src/frontend/locale/pipeline/en-US.json b/src/frontend/locale/pipeline/en-US.json index 4c0d8b059f0..376e2dbf71f 100644 --- a/src/frontend/locale/pipeline/en-US.json +++ b/src/frontend/locale/pipeline/en-US.json @@ -293,7 +293,8 @@ "noRelatedPipeline": "No linked pipeline yet", "all": "All", "installedAt": "Installed at", - "createdAt": "Created at" + "createdAt": "Created at", + "systemPlugin": "System plugin" }, "settings": { "baseInfo": "Basic Information", @@ -757,7 +758,11 @@ "directoryName": "Folder Name", "directoryPath": "Folder Path", "include": "Include", - "fileAndFolder": "{0} files, {1} folders" + "fileAndFolder": "{0} files, {1} folders", + "triggerResult": "Trigger Result", + "triggerSuc": "Trigger successful", + "triggerFail": "Trigger failed", + "triggerMismatch": "Trigger mismatch" }, "editPage": { "invalidValue": "Plugin's Input {0} Invalid", diff --git a/src/frontend/locale/pipeline/zh-CN.json b/src/frontend/locale/pipeline/zh-CN.json index a8286df4299..c703836d22f 100644 --- a/src/frontend/locale/pipeline/zh-CN.json +++ b/src/frontend/locale/pipeline/zh-CN.json @@ -292,7 +292,8 @@ "noRelatedPipeline": "暂无关联流水线", "all": "所有", "installedAt": "安装于", - "createdAt": "创建于" + "createdAt": "创建于", + "systemPlugin": "系统插件" }, "settings": { "baseInfo": "基本信息", @@ -756,7 +757,11 @@ "directoryName": "目录名称", "directoryPath": "目录路径", "include": "包含", - "fileAndFolder": "{0} 个文件,{1} 个目录" + "fileAndFolder": "{0} 个文件,{1} 个目录", + "triggerResult": "触发结果", + "triggerSuc": "触发成功", + "triggerFail": "触发失败", + "triggerMismatch": "触发器不匹配" }, "editPage": { "invalidValue": "插件表单参数{0}非法 ", diff --git a/support-files/i18n/message_en_US.properties b/support-files/i18n/message_en_US.properties index 364ae164953..49734706707 100644 --- a/support-files/i18n/message_en_US.properties +++ b/support-files/i18n/message_en_US.properties @@ -134,6 +134,7 @@ 2100133=Trigger [{0}] Merge Request Accept event type has been adjusted to action:merged for Merge Request event. Please switch version to 2.latest or above. 2100135=SVN token is incorrect or SVN path does not have permission 2100136=SVN token is empty, please check the credential type. +2100137=variable [{0}] not found 2189500=daemon interrupted 2189501=An error occurred in the internal service call of the system 2189502=The execution request timed out diff --git a/support-files/i18n/message_zh_CN.properties b/support-files/i18n/message_zh_CN.properties index 4748ae998c7..98d9323cb83 100644 --- a/support-files/i18n/message_zh_CN.properties +++ b/support-files/i18n/message_zh_CN.properties @@ -134,6 +134,7 @@ 2100133=触发器[{0}]Merge Request Accept事件类型已调整为Merge Request事件对应的监听动作:合并MR,请切换版本至2.latest及以上 2100135=SVN token 不正确 或者 SVN 路径没有权限 2100136=SVN Token 为空, 请检查代码库的凭证类型 +2100137=变量[${0}]不存在 2189500=守护进程中断 2189501=系统内部服务调用出错 2189502=执行请求超时 diff --git a/support-files/i18n/process/message_en_US.properties b/support-files/i18n/process/message_en_US.properties index 39b358a3b7a..10965e0ac2c 100644 --- a/support-files/i18n/process/message_en_US.properties +++ b/support-files/i18n/process/message_en_US.properties @@ -51,7 +51,7 @@ 2101051=Store image is used in the model, but code is empty 2101052=Store image is used in the model, but version is empty 2101053=Image value of non-store BK-CI / third party sources cannot be empty -2101054=Variable names can only use letters, numbers and underscores, and the first character cannot start with a number +2101054=Variable [{0}] names can only use letters, numbers and underscores, and the first character cannot start with a number 2101055=Stage [{0}] admission configuration is incorrect 2101056=The pipeline description is too long. 2101057=The timing parameter [{0}] of a timing trigger cannot be triggered in seconds. @@ -247,6 +247,7 @@ 2101253=Condition expression in [{0}] is longer than {1} digits 2101254=Parameter {0} is required and cannot be empty 2101255=Start the build machine repeatedly. The current status of the build machine is: {0} +2101256=pipeline build parameter {0} value exceeds 4000 length limit ATOM_POST_EXECUTE_TIP=###Tip:this is the post-action hooked by [step{0}]{1}### # 公共变量 diff --git a/support-files/i18n/process/message_zh_CN.properties b/support-files/i18n/process/message_zh_CN.properties index 27f134c4dd6..e6dfa2b9517 100644 --- a/support-files/i18n/process/message_zh_CN.properties +++ b/support-files/i18n/process/message_zh_CN.properties @@ -51,7 +51,7 @@ 2101051=模型中使用了商店镜像,但code为空 2101052=模型中使用了商店镜像,但version为空 2101053=非商店蓝盾源/第三方源的镜像value不可为空 -2101054=变量名只能使用英文字母,数字和下划线,首字符不能以数字开头 +2101054=变量名[{0}]只能使用英文字母,数字和下划线,首字符不能以数字开头 2101055=Stage[{0}]准入配置不正确 2101056=流水线描述过长 2101057=定时触发器的定时参数[{0}]不能秒级触发 @@ -247,6 +247,7 @@ 2101253=[{0}]的条件表达式的长度超过{1}位 2101254=构建入参[{0}]必填,不能为空 2101255=重复启动构建机,当前构建机的状态为:{0} +2101256=流水线变量{0}值超出4000长度限制 ATOM_POST_EXECUTE_TIP=###Tip:this is the post-action hooked by [step{0}]{1}### ci.build-no=构建号,开启推荐版本号时有效 diff --git a/support-files/sql/1001_ci_project_ddl_mysql.sql b/support-files/sql/1001_ci_project_ddl_mysql.sql index 05ffb1d89de..377a2e01b5c 100644 --- a/support-files/sql/1001_ci_project_ddl_mysql.sql +++ b/support-files/sql/1001_ci_project_ddl_mysql.sql @@ -374,6 +374,7 @@ CREATE TABLE IF NOT EXISTS `T_PROJECT_APPROVAL` ( `PROJECT_TYPE` int(10) DEFAULT NULL COMMENT '项目类型', `PRODUCT_ID` int(10) DEFAULT NULL COMMENT '运营产品ID', `PRODUCT_NAME` VARCHAR(64) DEFAULT NULL comment '运营产品名称', + `PROPERTIES` text null comment '项目其他配置', PRIMARY KEY (`ID`) USING BTREE, UNIQUE KEY `project_name` (`PROJECT_NAME`) USING BTREE, UNIQUE KEY `english_name` (`ENGLISH_NAME`) USING BTREE diff --git a/support-files/sql/2004_v3.x/2030_ci_project-update_v3.0_mysql.sql b/support-files/sql/2004_v3.x/2030_ci_project-update_v3.0_mysql.sql index a0b29441394..436aefe0196 100644 --- a/support-files/sql/2004_v3.x/2030_ci_project-update_v3.0_mysql.sql +++ b/support-files/sql/2004_v3.x/2030_ci_project-update_v3.0_mysql.sql @@ -20,6 +20,15 @@ BEGIN ADD COLUMN `DOC_URL` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '文档链接'; END IF; + IF NOT EXISTS(SELECT 1 + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = db + AND TABLE_NAME = 'T_PROJECT_APPROVAL' + AND COLUMN_NAME = 'PROPERTIES') THEN + ALTER TABLE T_PROJECT_APPROVAL + ADD COLUMN `PROPERTIES` text null DEFAULT NULL comment '项目其他配置'; + END IF; + COMMIT; END DELIMITER ;