<feature>[vpc-dns]: support vpc router dns records#4040
Conversation
Walkthrough新增 VPC 路由器 DNS 记录表及外键索引;实现域名校验工具并添加单元测试;在测试库加入 4 个 DNS 记录相关 DSL 方法;补充中/英文国际化提示文案。 变更内容VPC 路由器 DNS 记录管理
🎯 代码审查工作量评估🎯 3 (中等) | ⏱️ ~20 分钟 诗歌
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@utils/src/test/java/org/zstack/utils/test/TestNetworkUtils.java`:
- Line 52: In TestNetworkUtils replace the Chinese-containing test string used
in the assertion so it doesn't violate encoding rules: change the assert that
calls NetworkUtils.isDomainName(...) in the TestNetworkUtils test to use a
non-ASCII but non-Chinese example (e.g. "münchen.local") to keep the
invalid-domain coverage while removing Chinese characters; update the literal
inside the assertFalse call referencing NetworkUtils.isDomainName to the chosen
sample.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: http://open.zstack.ai:20001/code-reviews/zstack-cloud.yaml (via .coderabbit.yaml)
Review profile: CHILL
Plan: Pro
Run ID: 935f6b1b-0b68-4b55-857b-aeb85352e623
⛔ Files ignored due to path filters (11)
conf/i18n.jsonis excluded by!**/*.jsonsdk/src/main/java/org/zstack/sdk/AddDnsRecordToVpcRouterAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/AddDnsRecordToVpcRouterResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/QueryVpcRouterDnsRecordAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/QueryVpcRouterDnsRecordResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/RemoveDnsRecordFromVpcRouterAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/RemoveDnsRecordFromVpcRouterResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/UpdateDnsRecordToVpcRouterAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/UpdateDnsRecordToVpcRouterResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/VpcRouterDnsRecordInventory.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/VpcRouterVmInventory.javais excluded by!sdk/**
📒 Files selected for processing (6)
conf/db/upgrade/V5.4.7__schema.sqlconf/i18n/messages_en_US.propertiesconf/i18n/messages_zh_CN.propertiestestlib/src/main/java/org/zstack/testlib/ApiHelper.groovyutils/src/main/java/org/zstack/utils/network/NetworkUtils.javautils/src/test/java/org/zstack/utils/test/TestNetworkUtils.java
| assertFalse(NetworkUtils.isDomainName(null)); | ||
| assertFalse(NetworkUtils.isDomainName("*.app.local")); | ||
| assertFalse(NetworkUtils.isDomainName("db_prod.local")); | ||
| assertFalse(NetworkUtils.isDomainName("中文.local")); |
There was a problem hiding this comment.
测试字符串包含中文,违反仓库通用编码规范。
Line 52 的 "中文.local" 建议替换为不含中文的非法域名样例(例如 "münchen.local"),既保留“非 ASCII 非法输入”覆盖,也符合规范。
💡 建议修改
- assertFalse(NetworkUtils.isDomainName("中文.local"));
+ assertFalse(NetworkUtils.isDomainName("münchen.local"));As per coding guidelines: "代码里不应当有中文,包括报错、注释等都应当使用正确的、无拼写错误的英文来写".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@utils/src/test/java/org/zstack/utils/test/TestNetworkUtils.java` at line 52,
In TestNetworkUtils replace the Chinese-containing test string used in the
assertion so it doesn't violate encoding rules: change the assert that calls
NetworkUtils.isDomainName(...) in the TestNetworkUtils test to use a non-ASCII
but non-Chinese example (e.g. "münchen.local") to keep the invalid-domain
coverage while removing Chinese characters; update the literal inside the
assertFalse call referencing NetworkUtils.isDomainName to the chosen sample.
| # args: msg.getDns() | ||
| dns[%s]\ is\ not\ a\ IP\ address = dns[{0}] is not a IP address | ||
|
|
||
| # at: src/main/java/org/zstack/vpc/VpcApiInterceptor.java:627 |
There was a problem hiding this comment.
Comment from shixin.ruan:
这个文件是脚本自动生成的。不要让ai修改i18n文件
There was a problem hiding this comment.
Comment from 刘洪波:
done.
Add DNS record API messages, SDK actions, schema, and i18n. Validate domain names and private VPC IP ranges for writes. Resolves: ZSTAC-85249 Change-Id: I56c4a542e16d4c8ca36fd32cd824f1ebce750008
a4e2fd1 to
1066edf
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy (1)
56086-56086: ⚡ Quick win魔法值:字符串字面量 "apipath" 应定义为常量
根据编码规范要求避免使用魔法值,系统属性名称
"apipath"在四个方法中重复出现,应提取为类级常量。♻️ 建议改进
private static final String API_PATH_PROPERTY = "apipath" // 然后在方法中使用: if (System.getProperty(API_PATH_PROPERTY) != null) { // ... }Also applies to: 56111-56111, 56136-56136, 56163-56163
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy` at line 56086, Replace the repeated magic string literal "apipath" with a class-level constant in ApiHelper (e.g., declare private static final String API_PATH_PROPERTY = "apipath") and update all occurrences of System.getProperty("apipath") in the file to use System.getProperty(API_PATH_PROPERTY); specifically change each check like System.getProperty("apipath") != null in the methods where it appears (the four occurrences shown as System.getProperty("apipath") in the diff) to reference API_PATH_PROPERTY instead to eliminate the magic value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@conf/db/upgrade/V5.4.7__schema.sql`:
- Around line 8-9: Change the TIMESTAMP default for the createDate column so it
does not use '0000-00-00 00:00:00' or DEFAULT CURRENT_TIMESTAMP when another
TIMESTAMP (lastOpDate) uses ON UPDATE CURRENT_TIMESTAMP; update the createDate
definition to use a non-zero sentinel like '2000-01-01 00:00:00' (e.g.,
`createDate TIMESTAMP NOT NULL DEFAULT '2000-01-01 00:00:00'`) and keep
lastOpDate as `TIMESTAMP ... ON UPDATE CURRENT_TIMESTAMP`, and rely on the JPA
`@PrePersist` logic in the Java entity to set the real creation timestamp.
In `@testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy`:
- Around line 56079-56179: The four methods addDnsRecordToVpcRouter,
removeDnsRecordFromVpcRouter, updateDnsRecordToVpcRouter and
getVpcRouterDnsRecord duplicate the same request/response/tracking logic;
extract the common flow into a reusable helper (e.g., executeApiAction) that
accepts an action instance, sets sessionId, applies the closure
delegate/strategy, ensures apiId via Platform.uuid, runs ApiPathTracker, calls
errorOut(action.call()) and records Test.apiPaths[action.class.name]; keep
getVpcRouterDnsRecord’s special a.conditions = a.conditions.collect {
it.toString() } before invoking the helper or provide a pre-call hook so the
conversion still happens.
---
Nitpick comments:
In `@testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy`:
- Line 56086: Replace the repeated magic string literal "apipath" with a
class-level constant in ApiHelper (e.g., declare private static final String
API_PATH_PROPERTY = "apipath") and update all occurrences of
System.getProperty("apipath") in the file to use
System.getProperty(API_PATH_PROPERTY); specifically change each check like
System.getProperty("apipath") != null in the methods where it appears (the four
occurrences shown as System.getProperty("apipath") in the diff) to reference
API_PATH_PROPERTY instead to eliminate the magic value.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: http://open.zstack.ai:20001/code-reviews/zstack-cloud.yaml (via .coderabbit.yaml)
Review profile: CHILL
Plan: Pro
Run ID: b3ddddfa-4c5f-47df-b498-ddcb3f85b95f
⛔ Files ignored due to path filters (11)
conf/i18n.jsonis excluded by!**/*.jsonsdk/src/main/java/org/zstack/sdk/AddDnsRecordToVpcRouterAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/AddDnsRecordToVpcRouterResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/QueryVpcRouterDnsRecordAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/QueryVpcRouterDnsRecordResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/RemoveDnsRecordFromVpcRouterAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/RemoveDnsRecordFromVpcRouterResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/UpdateDnsRecordToVpcRouterAction.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/UpdateDnsRecordToVpcRouterResult.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/VpcRouterDnsRecordInventory.javais excluded by!sdk/**sdk/src/main/java/org/zstack/sdk/VpcRouterVmInventory.javais excluded by!sdk/**
📒 Files selected for processing (6)
conf/db/upgrade/V5.4.7__schema.sqlconf/i18n/messages_en_US.propertiesconf/i18n/messages_zh_CN.propertiestestlib/src/main/java/org/zstack/testlib/ApiHelper.groovyutils/src/main/java/org/zstack/utils/network/NetworkUtils.javautils/src/test/java/org/zstack/utils/test/TestNetworkUtils.java
✅ Files skipped from review due to trivial changes (2)
- conf/i18n/messages_zh_CN.properties
- conf/i18n/messages_en_US.properties
| `createDate` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', | ||
| `lastOpDate` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP, |
There was a problem hiding this comment.
修复 createDate 的默认值以避免 MySQL 5.7 兼容性问题。
根据编码规范,不应使用 DEFAULT '0000-00-00 00:00:00'。此外,根据仓库历史经验,在 MySQL 5.7 且 explicit_defaults_for_timestamp=OFF 的环境下,当表中已有一个 TIMESTAMP 列带 ON UPDATE CURRENT_TIMESTAMP(如 Line 9 的 lastOpDate)时,第二个 TIMESTAMP 列(如 Line 8 的 createDate)不能使用 DEFAULT CURRENT_TIMESTAMP,否则会触发 MySQL Error 1293。
应将 createDate 的默认值改为 '2000-01-01 00:00:00' 这样的非零哨兵值,并依赖 JPA 的 @PrePersist 在 Java 层为新记录设置真实的创建时间戳。
🔧 建议修改
- `createDate` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
+ `createDate` TIMESTAMP NOT NULL DEFAULT '2000-01-01 00:00:00',Based on learnings: if a table already contains a TIMESTAMP column defined with ON UPDATE CURRENT_TIMESTAMP (e.g., lastOpDate), then any second TIMESTAMP column (e.g., createDate) must NOT use DEFAULT CURRENT_TIMESTAMP. Use a non-CURRENT_TIMESTAMP sentinel default such as '2000-01-01 00:00:00' and rely on JPA PrePersist to set the real creation timestamp.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| `createDate` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', | |
| `lastOpDate` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP, | |
| `createDate` TIMESTAMP NOT NULL DEFAULT '2000-01-01 00:00:00', | |
| `lastOpDate` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP, |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@conf/db/upgrade/V5.4.7__schema.sql` around lines 8 - 9, Change the TIMESTAMP
default for the createDate column so it does not use '0000-00-00 00:00:00' or
DEFAULT CURRENT_TIMESTAMP when another TIMESTAMP (lastOpDate) uses ON UPDATE
CURRENT_TIMESTAMP; update the createDate definition to use a non-zero sentinel
like '2000-01-01 00:00:00' (e.g., `createDate TIMESTAMP NOT NULL DEFAULT
'2000-01-01 00:00:00'`) and keep lastOpDate as `TIMESTAMP ... ON UPDATE
CURRENT_TIMESTAMP`, and rely on the JPA `@PrePersist` logic in the Java entity to
set the real creation timestamp.
| def addDnsRecordToVpcRouter(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.AddDnsRecordToVpcRouterAction.class) Closure c) { | ||
| def a = new org.zstack.sdk.AddDnsRecordToVpcRouterAction() | ||
| a.sessionId = Test.currentEnvSpec?.session?.uuid | ||
| c.resolveStrategy = Closure.OWNER_FIRST | ||
| c.delegate = a | ||
| c() | ||
|
|
||
| if (System.getProperty("apipath") != null) { | ||
| if (a.apiId == null) { | ||
| a.apiId = Platform.uuid | ||
| } | ||
|
|
||
| def tracker = new ApiPathTracker(a.apiId) | ||
| def out = errorOut(a.call()) | ||
| def path = tracker.getApiPath() | ||
| if (!path.isEmpty()) { | ||
| Test.apiPaths[a.class.name] = path.join(" --->\n") | ||
| } | ||
|
|
||
| return out | ||
| } else { | ||
| return errorOut(a.call()) | ||
| } | ||
| } | ||
|
|
||
| def removeDnsRecordFromVpcRouter(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.RemoveDnsRecordFromVpcRouterAction.class) Closure c) { | ||
| def a = new org.zstack.sdk.RemoveDnsRecordFromVpcRouterAction() | ||
| a.sessionId = Test.currentEnvSpec?.session?.uuid | ||
| c.resolveStrategy = Closure.OWNER_FIRST | ||
| c.delegate = a | ||
| c() | ||
|
|
||
| if (System.getProperty("apipath") != null) { | ||
| if (a.apiId == null) { | ||
| a.apiId = Platform.uuid | ||
| } | ||
|
|
||
| def tracker = new ApiPathTracker(a.apiId) | ||
| def out = errorOut(a.call()) | ||
| def path = tracker.getApiPath() | ||
| if (!path.isEmpty()) { | ||
| Test.apiPaths[a.class.name] = path.join(" --->\n") | ||
| } | ||
|
|
||
| return out | ||
| } else { | ||
| return errorOut(a.call()) | ||
| } | ||
| } | ||
|
|
||
| def updateDnsRecordToVpcRouter(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.UpdateDnsRecordToVpcRouterAction.class) Closure c) { | ||
| def a = new org.zstack.sdk.UpdateDnsRecordToVpcRouterAction() | ||
| a.sessionId = Test.currentEnvSpec?.session?.uuid | ||
| c.resolveStrategy = Closure.OWNER_FIRST | ||
| c.delegate = a | ||
| c() | ||
|
|
||
| if (System.getProperty("apipath") != null) { | ||
| if (a.apiId == null) { | ||
| a.apiId = Platform.uuid | ||
| } | ||
|
|
||
| def tracker = new ApiPathTracker(a.apiId) | ||
| def out = errorOut(a.call()) | ||
| def path = tracker.getApiPath() | ||
| if (!path.isEmpty()) { | ||
| Test.apiPaths[a.class.name] = path.join(" --->\n") | ||
| } | ||
|
|
||
| return out | ||
| } else { | ||
| return errorOut(a.call()) | ||
| } | ||
| } | ||
|
|
||
| def getVpcRouterDnsRecord(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.QueryVpcRouterDnsRecordAction.class) Closure c) { | ||
| def a = new org.zstack.sdk.QueryVpcRouterDnsRecordAction() | ||
| a.sessionId = Test.currentEnvSpec?.session?.uuid | ||
| c.resolveStrategy = Closure.OWNER_FIRST | ||
| c.delegate = a | ||
| c() | ||
|
|
||
| a.conditions = a.conditions.collect { it.toString() } | ||
|
|
||
| if (System.getProperty("apipath") != null) { | ||
| if (a.apiId == null) { | ||
| a.apiId = Platform.uuid | ||
| } | ||
|
|
||
| def tracker = new ApiPathTracker(a.apiId) | ||
| def out = errorOut(a.call()) | ||
| def path = tracker.getApiPath() | ||
| if (!path.isEmpty()) { | ||
| Test.apiPaths[a.class.name] = path.join(" --->\n") | ||
| } | ||
|
|
||
| return out | ||
| } else { | ||
| return errorOut(a.call()) | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
严重代码重复:四个方法结构几乎完全相同
这四个方法除了 SDK Action 类型不同外,逻辑完全一致(getVpcRouterDnsRecord 仅多一行 conditions 转换)。根据编码规范要求避免代码重复,应重构为通用方法。
♻️ 建议的重构方案
提取公共逻辑到泛型方法:
private <T> def executeApiAction(T action, Closure c) {
action.sessionId = Test.currentEnvSpec?.session?.uuid
c.resolveStrategy = Closure.OWNER_FIRST
c.delegate = action
c()
if (System.getProperty("apipath") != null) {
if (action.apiId == null) {
action.apiId = Platform.uuid
}
def tracker = new ApiPathTracker(action.apiId)
def out = errorOut(action.call())
def path = tracker.getApiPath()
if (!path.isEmpty()) {
Test.apiPaths[action.class.name] = path.join(" --->\n")
}
return out
} else {
return errorOut(action.call())
}
}
def addDnsRecordToVpcRouter(`@DelegatesTo`(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.AddDnsRecordToVpcRouterAction.class) Closure c) {
executeApiAction(new org.zstack.sdk.AddDnsRecordToVpcRouterAction(), c)
}
def removeDnsRecordFromVpcRouter(`@DelegatesTo`(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.RemoveDnsRecordFromVpcRouterAction.class) Closure c) {
executeApiAction(new org.zstack.sdk.RemoveDnsRecordFromVpcRouterAction(), c)
}
def updateDnsRecordToVpcRouter(`@DelegatesTo`(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.UpdateDnsRecordToVpcRouterAction.class) Closure c) {
executeApiAction(new org.zstack.sdk.UpdateDnsRecordToVpcRouterAction(), c)
}
def getVpcRouterDnsRecord(`@DelegatesTo`(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.QueryVpcRouterDnsRecordAction.class) Closure c) {
def a = new org.zstack.sdk.QueryVpcRouterDnsRecordAction()
a.sessionId = Test.currentEnvSpec?.session?.uuid
c.resolveStrategy = Closure.OWNER_FIRST
c.delegate = a
c()
a.conditions = a.conditions.collect { it.toString() }
// 重用后续逻辑或再抽取
if (System.getProperty("apipath") != null) {
if (a.apiId == null) {
a.apiId = Platform.uuid
}
def tracker = new ApiPathTracker(a.apiId)
def out = errorOut(a.call())
def path = tracker.getApiPath()
if (!path.isEmpty()) {
Test.apiPaths[a.class.name] = path.join(" --->\n")
}
return out
} else {
return errorOut(a.call())
}
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy` around lines 56079
- 56179, The four methods addDnsRecordToVpcRouter, removeDnsRecordFromVpcRouter,
updateDnsRecordToVpcRouter and getVpcRouterDnsRecord duplicate the same
request/response/tracking logic; extract the common flow into a reusable helper
(e.g., executeApiAction) that accepts an action instance, sets sessionId,
applies the closure delegate/strategy, ensures apiId via Platform.uuid, runs
ApiPathTracker, calls errorOut(action.call()) and records
Test.apiPaths[action.class.name]; keep getVpcRouterDnsRecord’s special
a.conditions = a.conditions.collect { it.toString() } before invoking the helper
or provide a pre-call hook so the conversion still happens.
Add DNS record API messages, SDK actions, schema, and i18n.
Validate domain names and private VPC IP ranges for writes.
Resolves: ZSTAC-85249
Change-Id: I56c4a542e16d4c8ca36fd32cd824f1ebce750008
sync from gitlab !9936