Merge pull request #1813 from pikasTech/fix/1811-pac-dynamic-pipeline-spec
fix(cicd): 修正 PaC pipelineSpec CEL 动态访问
This commit is contained in:
@@ -88,10 +88,15 @@ test("versioned admission desired state protects controller proof, candidate ide
|
||||
const oldRun = { spec: { pipelineSpec: { tasks: [{ name: "plan-artifacts" }] }, workspaces: [], timeouts: { pipeline: "1h" } } };
|
||||
const mutatedRun = structuredClone(oldRun);
|
||||
mutatedRun.spec.pipelineSpec.tasks[0].name = "forged-task";
|
||||
for (const field of ["managedBy", "params", "pipelineRef", "pipelineSpec", "taskRunSpecs", "taskRunTemplate", "timeouts", "workspaces"]) {
|
||||
for (const field of ["managedBy", "params", "pipelineRef", "taskRunSpecs", "taskRunTemplate", "timeouts", "workspaces"]) {
|
||||
expect(specGuard.expression).toContain(`has(object.spec.${field})`);
|
||||
expect(specGuard.expression).toContain(`object.spec.${field} == oldObject.spec.${field}`);
|
||||
}
|
||||
expect(specGuard.expression).toContain("has(dyn(object.spec).pipelineSpec)");
|
||||
expect(specGuard.expression).toContain("has(dyn(oldObject.spec).pipelineSpec)");
|
||||
expect(specGuard.expression).toContain("dyn(object.spec).pipelineSpec == dyn(oldObject.spec).pipelineSpec");
|
||||
expect(specGuard.expression).not.toContain("has(object.spec.pipelineSpec)");
|
||||
expect(specGuard.expression).not.toContain("object.spec.pipelineSpec == oldObject.spec.pipelineSpec");
|
||||
expect(specGuard.expression).not.toContain("pipelinesascode.tekton.dev/state");
|
||||
expect(mutatedRun.spec).not.toEqual(oldRun.spec);
|
||||
expect(queueGuard.expression).toContain('request.userInfo.username == "system:serviceaccount:pipelines-as-code:pipelines-as-code-watcher"');
|
||||
@@ -141,6 +146,21 @@ test("live admission evaluator requires exact desired hashes, observed generatio
|
||||
};
|
||||
const input = { policy, binding, role: rbac[0], roleBinding: rbac[1], namespace: "agentrun-ci", defaultCanMutate: false, expected };
|
||||
expect(evaluator.evaluatePacAdmissionState(input)).toMatchObject({ ready: true, activeAfter: "2026-01-01T00:00:01Z", defaultCanMutate: false });
|
||||
expect(evaluator.evaluatePacAdmissionState({
|
||||
...input,
|
||||
policy: {
|
||||
...policy,
|
||||
status: {
|
||||
...policy.status,
|
||||
typeChecking: {
|
||||
expressionWarnings: [{
|
||||
fieldRef: "spec.validations[7].expression",
|
||||
warning: "undefined field 'pipelineSpec'",
|
||||
}],
|
||||
},
|
||||
},
|
||||
},
|
||||
})).toMatchObject({ ready: false, reasons: expect.arrayContaining(["policy-expression-warning"]) });
|
||||
const apiDefaultedPolicy = {
|
||||
...policy,
|
||||
spec: {
|
||||
|
||||
@@ -319,11 +319,19 @@ function pipelineRefEqual(): string {
|
||||
}
|
||||
|
||||
function specFieldEqual(field: string): string {
|
||||
if (field === "pipelineSpec") return schemalessSpecFieldEqual(field);
|
||||
const current = `has(object.spec.${field})`;
|
||||
const previous = `has(oldObject.spec.${field})`;
|
||||
return `((${current}) == (${previous}) && (!(${current}) || object.spec.${field} == oldObject.spec.${field}))`;
|
||||
}
|
||||
|
||||
function schemalessSpecFieldEqual(field: string): string {
|
||||
// Tekton pipelineSpec 保留未知字段且没有静态类型,必须在 dyn 根上检查存在性并做完整深度相等。
|
||||
const current = `has(dyn(object.spec).${field})`;
|
||||
const previous = `has(dyn(oldObject.spec).${field})`;
|
||||
return `((${current}) == (${previous}) && (!(${current}) || dyn(object.spec).${field} == dyn(oldObject.spec).${field}))`;
|
||||
}
|
||||
|
||||
function specStatusState(root: "object" | "oldObject", state: string): string {
|
||||
if (state === "absent") return `!has(${root}.spec.status)`;
|
||||
return `has(${root}.spec.status) && ${root}.spec.status == ${cel(state)}`;
|
||||
|
||||
Reference in New Issue
Block a user