把评审结果变成部署
评审和实际提交各使用一个 release 请求。渲染评审前,应从应用自己持久化的 release store 读取完整 release 历史,让预览和实际提交使用相同的安装/升级状态及下一 revision。审批后只能改变 DryRun;Chart 标识、values、命名空间、release 状态和 options provider 配置必须保持不变。
var releaseHistory = await releases.LoadHistoryForUpgradeInstallAsync(
releaseName,
targetNamespace,
createNamespace: true,
cancellationToken);
var latestRelease = releaseHistory.MaxBy(release => release.Revision);
var isUpgrade = latestRelease is not null &&
!string.Equals(latestRelease.Status, "uninstalled", StringComparison.OrdinalIgnoreCase);
var nextRevision = latestRelease?.Revision + 1 ?? 1;
var preview = new HelmUpgradeInstallRequest
{
ReleaseName = releaseName,
Namespace = targetNamespace,
Chart = approvedChartPath,
ValuesFiles = approvedValuesFiles,
SkipCRDs = true,
CreateNamespace = true,
Wait = true,
TimeoutSeconds = 300,
DryRun = true,
DryRunIsUpgrade = isUpgrade,
DryRunRevision = nextRevision
};
var previewResult = await client.UpgradeInstallAsync(preview, cancellationToken);
if (!previewResult.Succeeded)
return Results.BadRequest("Release 预览失败。");
await approvals.SaveAsync(preview, previewResult.StandardOutput, cancellationToken);审批服务校验该记录后,获取该 release 的应用自有锁;在持有锁时校验已持久化的 release 状态,再重建请求并提交,最后才释放锁:
await using var releaseLock = await releaseLocks.AcquireAsync(
releaseName,
targetNamespace,
cancellationToken);
var apply = await approvals.CreateApprovedRequestAsync(approvalId, cancellationToken);
await approvals.VerifyReleaseStateAsync(apply, cancellationToken);
apply.DryRun = false;
var result = await client.UpgradeInstallAsync(apply, cancellationToken);
if (!result.Succeeded)
return Results.Problem("已审批的 release 无法提交。", statusCode: 409);
return Results.Ok(new { result.StandardOutput });不要相信浏览器第二次提交的 values 或 Chart 版本。执行提交的服务应当自己读取已评审记录。将内容寻址的不可变 Chart 归档和 values 内容快照(或其哈希校验)随审批记录保存;仅保存路径不是稳定输入。本示例在预览和 apply 中都设置 SkipCRDs = true:CRD 应作为单独、明确审批的操作来审查和安装。LoadHistoryForUpgradeInstallAsync 是应用自有的 HistoryAsync 适配器:启用 CreateNamespace 时,它会像 apply 路径一样将目标命名空间不存在视为零历史。必须从该完整历史中取最高 revision 来派生预览状态,其中包括失败和保留卸载的 revision,然后将解析出的状态和审批记录一起保存。状态校验和 apply 必须是原子的:先获取按 release 粒度的锁,再校验已持久化的状态,直到生命周期调用结束才释放该锁;所有 install、upgrade、rollback 和 uninstall 路径都必须使用同一把锁。若 release 状态已变化,就拒绝提交,否则重新渲染并要求新的审批。该工作流要求模板具有确定性:由于 apply 会再次渲染 Chart,审批服务必须拒绝使用 now、uuidv4 或 randAlphaNum 等非确定性函数的 Chart。若必须支持这类 Chart,应使用另一条部署路径来应用已审批的精确 manifest。把 CommandResult.StandardError、退出码和操作 ID 保存到受限的操作记录中;对不受信任的调用方只返回通用失败信息。
Wait、WaitForJobs、Atomic 和 TimeoutSeconds 共同决定一次操作何时算完成。选择它们之前,请阅读安装和升级 Release。