From f7ac7c5060a966b052c748b172517ae05ffea874 Mon Sep 17 00:00:00 2001 From: Prucek Date: Wed, 26 Aug 2026 09:48:52 +0200 Subject: [PATCH] ci-operator: allow multi-stage params to opt into trigger-time overrides Adds an Overridable flag on step parameters, so ci-operator can apply a trigger-time environment variable override (e.g. from Gangway's pod_spec_options.envs) to a normally-named parameter, without requiring the ref/script boilerplate of declaring and reassigning a separate MULTISTAGE_PARAM_OVERRIDE_ parameter. The legacy prefixed form is still supported and takes precedence for backwards compatibility, and overrides are only ever honored for parameters that explicitly set Overridable: true, so unrelated parameters can't be tampered with. Co-authored-by: Cursor --- cmd/ci-operator/main.go | 79 +++++++-- cmd/ci-operator/main_test.go | 111 ++++++++++++- pkg/api/types.go | 9 ++ pkg/api/zz_generated.deepcopy.go | 7 + pkg/steps/multi_stage/gen.go | 7 + pkg/steps/multi_stage/gen_test.go | 151 ++++++++++++++++++ pkg/steps/multi_stage/multi_stage.go | 2 + .../zz_generated.ci_operator_reference.go | 56 +++++++ 8 files changed, 404 insertions(+), 18 deletions(-) diff --git a/cmd/ci-operator/main.go b/cmd/ci-operator/main.go index f462cdd402..5b6724c579 100644 --- a/cmd/ci-operator/main.go +++ b/cmd/ci-operator/main.go @@ -831,25 +831,80 @@ func overrideMultiStageParams(o *options) error { return nil } -// applyEnvOverrides processes environment variables with override prefixes and applies them to the test configurations. -// It checks for environment variables that start with "MULTISTAGE_PARAM_OVERRIDE_" and applies them to the environment settings of each test. +// multiStageParamOverridePrefix is the legacy prefix used to request a multi-stage parameter override. +const multiStageParamOverridePrefix = "MULTISTAGE_PARAM_OVERRIDE_" + +// overridableParamNames returns parameter names, across all Pre/Test/Post steps and +// observers of ms, declared with Overridable: true. +func overridableParamNames(ms *api.MultiStageTestConfigurationLiteral) sets.Set[string] { + names := sets.New[string]() + for _, steps := range [][]api.LiteralTestStep{ms.Pre, ms.Test, ms.Post} { + for _, step := range steps { + for _, param := range step.Environment { + if param.Overridable { + names.Insert(param.Name) + } + } + } + } + for _, observer := range ms.Observers { + for _, param := range observer.Environment { + if param.Overridable { + names.Insert(param.Name) + } + } + } + return names +} + +// applyEnvOverrides applies trigger-time multi-stage parameter overrides from the environment. +// Two forms are supported: the legacy "MULTISTAGE_PARAM_OVERRIDE_" prefix (kept for backwards +// compatibility, and takes precedence if both forms are set), and a plain "" variable, honored +// only if some step declares that parameter with Overridable: true. Both forms populate ParamOverrides, +// which generateParams (pkg/steps/multi_stage/gen.go) applies only to opted-in parameters. func applyEnvOverrides(o *options) { + envPairs := make(map[string]string) for _, envVar := range os.Environ() { - if !strings.HasPrefix(envVar, "MULTISTAGE_PARAM_OVERRIDE_") { - continue - } parts := strings.SplitN(envVar, "=", 2) if len(parts) != 2 { continue } - key, value := parts[0], parts[1] - for _, test := range o.configSpec.Tests { - if test.MultiStageTestConfigurationLiteral != nil { - if test.MultiStageTestConfigurationLiteral.Environment == nil { - test.MultiStageTestConfigurationLiteral.Environment = make(api.TestEnvironment) - } - test.MultiStageTestConfigurationLiteral.Environment[key] = value + envPairs[parts[0]] = parts[1] + } + + for _, test := range o.configSpec.Tests { + ms := test.MultiStageTestConfigurationLiteral + if ms == nil { + continue + } + + for key, value := range envPairs { + if !strings.HasPrefix(key, multiStageParamOverridePrefix) { + continue + } + if ms.Environment == nil { + ms.Environment = make(api.TestEnvironment) + } + ms.Environment[key] = value + + if ms.ParamOverrides == nil { + ms.ParamOverrides = make(api.TestEnvironment) + } + ms.ParamOverrides[strings.TrimPrefix(key, multiStageParamOverridePrefix)] = value + } + + for name := range overridableParamNames(ms) { + if _, alreadySet := ms.ParamOverrides[name]; alreadySet { + continue // prefixed form already set this parameter and takes precedence. + } + value, ok := envPairs[name] + if !ok { + continue + } + if ms.ParamOverrides == nil { + ms.ParamOverrides = make(api.TestEnvironment) } + ms.ParamOverrides[name] = value } } } diff --git a/cmd/ci-operator/main_test.go b/cmd/ci-operator/main_test.go index 5ae600dc65..3596e12f82 100644 --- a/cmd/ci-operator/main_test.go +++ b/cmd/ci-operator/main_test.go @@ -1337,11 +1337,12 @@ func TestMultiStageParams(t *testing.T) { func TestApplyEnvOverrides(t *testing.T) { testCases := []struct { - id string - envVars map[string]string - expectedParams map[string]string - testConfig []api.TestStepConfiguration - expectedErrs []string + id string + envVars map[string]string + expectedParams map[string]string + expectedOverrides map[string]string + testConfig []api.TestStepConfiguration + expectedErrs []string }{ { id: "Apply overrides", @@ -1354,6 +1355,10 @@ func TestApplyEnvOverrides(t *testing.T) { "MULTISTAGE_PARAM_OVERRIDE_PARAM1": "VAL1", "MULTISTAGE_PARAM_OVERRIDE_PARAM2": "VAL2", }, + expectedOverrides: map[string]string{ + "PARAM1": "VAL1", + "PARAM2": "VAL2", + }, testConfig: []api.TestStepConfiguration{ { MultiStageTestConfigurationLiteral: &api.MultiStageTestConfigurationLiteral{ @@ -1370,7 +1375,8 @@ func TestApplyEnvOverrides(t *testing.T) { envVars: map[string]string{ "PARAM1": "VAL1", }, - expectedParams: map[string]string{}, + expectedParams: map[string]string{}, + expectedOverrides: map[string]string{}, testConfig: []api.TestStepConfiguration{ { MultiStageTestConfigurationLiteral: &api.MultiStageTestConfigurationLiteral{ @@ -1391,6 +1397,11 @@ func TestApplyEnvOverrides(t *testing.T) { "MULTISTAGE_PARAM_OVERRIDE_PARAM2": "VAL=2", "MULTISTAGE_PARAM_OVERRIDE_PARAM3": "VAL2", }, + expectedOverrides: map[string]string{ + "PARAM1": "VAL2", + "PARAM2": "VAL=2", + "PARAM3": "VAL2", + }, testConfig: []api.TestStepConfiguration{ { MultiStageTestConfigurationLiteral: &api.MultiStageTestConfigurationLiteral{ @@ -1403,6 +1414,87 @@ func TestApplyEnvOverrides(t *testing.T) { }, }, }, + { + id: "plain name honored for an opted-in parameter", + envVars: map[string]string{ + "EVAL_MODEL": "gpt-5", + }, + expectedParams: map[string]string{}, + expectedOverrides: map[string]string{ + "EVAL_MODEL": "gpt-5", + }, + testConfig: []api.TestStepConfiguration{ + { + MultiStageTestConfigurationLiteral: &api.MultiStageTestConfigurationLiteral{ + Test: []api.LiteralTestStep{{ + As: "step", + Environment: []api.StepParameter{{Name: "EVAL_MODEL", Overridable: true}}, + }}, + }, + }, + }, + }, + { + id: "plain name ignored for a parameter that did not opt in", + envVars: map[string]string{ + "EVAL_MODEL": "gpt-5", + }, + expectedParams: map[string]string{}, + expectedOverrides: map[string]string{}, + testConfig: []api.TestStepConfiguration{ + { + MultiStageTestConfigurationLiteral: &api.MultiStageTestConfigurationLiteral{ + Test: []api.LiteralTestStep{{ + As: "step", + Environment: []api.StepParameter{{Name: "EVAL_MODEL"}}, + }}, + }, + }, + }, + }, + { + id: "plain name honored for an opted-in observer parameter", + envVars: map[string]string{ + "EVAL_MODEL": "gpt-5", + }, + expectedParams: map[string]string{}, + expectedOverrides: map[string]string{ + "EVAL_MODEL": "gpt-5", + }, + testConfig: []api.TestStepConfiguration{ + { + MultiStageTestConfigurationLiteral: &api.MultiStageTestConfigurationLiteral{ + Observers: []api.Observer{{ + Name: "observer", + Environment: []api.StepParameter{{Name: "EVAL_MODEL", Overridable: true}}, + }}, + }, + }, + }, + }, + { + id: "prefixed form takes precedence over the plain form when both are present", + envVars: map[string]string{ + "MULTISTAGE_PARAM_OVERRIDE_EVAL_MODEL": "prefixed-value", + "EVAL_MODEL": "plain-value", + }, + expectedParams: map[string]string{ + "MULTISTAGE_PARAM_OVERRIDE_EVAL_MODEL": "prefixed-value", + }, + expectedOverrides: map[string]string{ + "EVAL_MODEL": "prefixed-value", + }, + testConfig: []api.TestStepConfiguration{ + { + MultiStageTestConfigurationLiteral: &api.MultiStageTestConfigurationLiteral{ + Test: []api.LiteralTestStep{{ + As: "step", + Environment: []api.StepParameter{{Name: "EVAL_MODEL", Overridable: true}}, + }}, + }, + }, + }, + }, } for _, tc := range testCases { @@ -1423,18 +1515,25 @@ func TestApplyEnvOverrides(t *testing.T) { applyEnvOverrides(o) actualParams := make(map[string]string) + actualOverrides := make(map[string]string) for _, test := range o.configSpec.Tests { if test.MultiStageTestConfigurationLiteral != nil { for name, val := range test.MultiStageTestConfigurationLiteral.Environment { actualParams[name] = val } + for name, val := range test.MultiStageTestConfigurationLiteral.ParamOverrides { + actualOverrides[name] = val + } } } if diff := cmp.Diff(tc.expectedParams, actualParams); diff != "" { t.Errorf("actual does not match expected, diff: %s", diff) } + if diff := cmp.Diff(tc.expectedOverrides, actualOverrides); diff != "" { + t.Errorf("actual overrides do not match expected, diff: %s", diff) + } }) } } diff --git a/pkg/api/types.go b/pkg/api/types.go index db04c1b76a..9046eabaf7 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -1193,6 +1193,11 @@ type StepParameter struct { Default *string `json:"default,omitempty"` // Documentation is a textual description of the parameter. Documentation string `json:"documentation,omitempty"` + // Overridable, if true, allows this parameter to be set via a trigger-time + // environment variable on ci-operator (its own name, or the legacy + // MULTISTAGE_PARAM_OVERRIDE_ form, which takes precedence). + // Must be explicitly opted into per parameter. + Overridable bool `json:"overridable,omitempty"` } // CredentialReference defines a secret to mount into a step and where to mount it. @@ -1347,6 +1352,10 @@ type MultiStageTestConfigurationLiteral struct { Post []LiteralTestStep `json:"post,omitempty"` // Environment has the values of parameters for the steps. Environment TestEnvironment `json:"env,omitempty"` + // ParamOverrides holds trigger-time parameter values, populated by + // ci-operator from the environment. Only honored for parameters + // declared with Overridable: true. Not meant to be set in CI config. + ParamOverrides TestEnvironment `json:"param_overrides,omitempty"` // Dependencies holds override values for dependency parameters. Dependencies TestDependencies `json:"dependencies,omitempty"` // DnsConfig for step's Pod. diff --git a/pkg/api/zz_generated.deepcopy.go b/pkg/api/zz_generated.deepcopy.go index bc771c7dc9..08b83cafe4 100644 --- a/pkg/api/zz_generated.deepcopy.go +++ b/pkg/api/zz_generated.deepcopy.go @@ -1155,6 +1155,13 @@ func (in *MultiStageTestConfigurationLiteral) DeepCopyInto(out *MultiStageTestCo (*out)[key] = val } } + if in.ParamOverrides != nil { + in, out := &in.ParamOverrides, &out.ParamOverrides + *out = make(TestEnvironment, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.Dependencies != nil { in, out := &in.Dependencies, &out.Dependencies *out = make(TestDependencies, len(*in)) diff --git a/pkg/steps/multi_stage/gen.go b/pkg/steps/multi_stage/gen.go index d7f50abe90..2cf1cc2e7f 100644 --- a/pkg/steps/multi_stage/gen.go +++ b/pkg/steps/multi_stage/gen.go @@ -413,6 +413,13 @@ func (s *multiStageTestStep) generateParams(env []api.StepParameter) []coreapi.E if v, ok := s.env[env.Name]; ok { value = v } + // paramOverrides carries untrusted, trigger-time values. Only honor it if this exact + // parameter opted in, so a same-named parameter on another step never receives it. + if env.Overridable { + if v, ok := s.paramOverrides[env.Name]; ok { + value = v + } + } ret = append(ret, coreapi.EnvVar{Name: env.Name, Value: value}) } return ret diff --git a/pkg/steps/multi_stage/gen_test.go b/pkg/steps/multi_stage/gen_test.go index 481a80d309..aa695a3947 100644 --- a/pkg/steps/multi_stage/gen_test.go +++ b/pkg/steps/multi_stage/gen_test.go @@ -383,6 +383,157 @@ func TestGeneratePodsEnvironment(t *testing.T) { } } +func TestGeneratePodsOverrideEnvironment(t *testing.T) { + value := "override-value" + defValue := "default" + empty := "" + for _, tc := range []struct { + name string + env api.TestEnvironment + overrides api.TestEnvironment + test api.LiteralTestStep + expected *string + }{{ + name: "override is applied to an opted-in parameter", + overrides: api.TestEnvironment{"TEST": value}, + test: api.LiteralTestStep{ + Environment: []api.StepParameter{{Name: "TEST", Overridable: true}}, + }, + expected: &value, + }, { + name: "override is ignored for a parameter that did not opt in", + overrides: api.TestEnvironment{"TEST": value}, + test: api.LiteralTestStep{ + Environment: []api.StepParameter{{Name: "TEST"}}, + }, + expected: &empty, + }, { + name: "override takes precedence over the parameter's default when opted in", + overrides: api.TestEnvironment{"TEST": value}, + test: api.LiteralTestStep{ + Environment: []api.StepParameter{{Name: "TEST", Default: &defValue, Overridable: true}}, + }, + expected: &value, + }, { + name: "default is used when opted in but no override is present", + overrides: api.TestEnvironment{}, + test: api.LiteralTestStep{ + Environment: []api.StepParameter{{Name: "TEST", Default: &defValue, Overridable: true}}, + }, + expected: &defValue, + }, { + name: "override does not leak into a parameter with the same name on a different, non-opted-in step", + env: api.TestEnvironment{}, + overrides: api.TestEnvironment{"TEST": value}, + test: api.LiteralTestStep{ + Environment: []api.StepParameter{{Name: "TEST"}}, + }, + expected: &empty, + }} { + t.Run(tc.name, func(t *testing.T) { + jobSpec := api.JobSpec{ + JobSpec: prowdapi.JobSpec{ + Job: "job", + BuildID: "build_id", + ProwJobID: "prow_job_id", + Type: prowapi.PeriodicJob, + DecorationConfig: &prowapi.DecorationConfig{ + Timeout: &prowapi.Duration{Duration: time.Minute}, + GracePeriod: &prowapi.Duration{Duration: time.Second}, + UtilityImages: &prowapi.UtilityImages{ + Sidecar: "sidecar", + Entrypoint: "entrypoint", + }, + }, + }, + } + jobSpec.SetNamespace("ns") + test := []api.LiteralTestStep{tc.test} + step := MultiStageTestStep(api.TestStepConfiguration{ + MultiStageTestConfigurationLiteral: &api.MultiStageTestConfigurationLiteral{ + Test: test, + Environment: tc.env, + ParamOverrides: tc.overrides, + }, + }, &api.ReleaseBuildConfiguration{}, fakeStepParams{}, nil, &jobSpec, nil, "node-name", "", nil, false, nil, false, wait.Backoff{}) + pods, _, err := step.(*multiStageTestStep).generatePods(test, nil, nil, nil, nil) + if err != nil { + t.Fatal(err) + } + var env *string + for i, v := range pods[0].Spec.Containers[0].Env { + if v.Name == "TEST" { + env = &pods[0].Spec.Containers[0].Env[i].Value + } + } + if !reflect.DeepEqual(env, tc.expected) { + t.Errorf("incorrect environment:\n%s", diff.ObjectReflectDiff(env, tc.expected)) + } + }) + } +} + +func TestGeneratePodsOverrideEnvironmentCrossStepIsolation(t *testing.T) { + jobSpec := api.JobSpec{ + JobSpec: prowdapi.JobSpec{ + Job: "job", + BuildID: "build_id", + ProwJobID: "prow_job_id", + Type: prowapi.PeriodicJob, + DecorationConfig: &prowapi.DecorationConfig{ + Timeout: &prowapi.Duration{Duration: time.Minute}, + GracePeriod: &prowapi.Duration{Duration: time.Second}, + UtilityImages: &prowapi.UtilityImages{ + Sidecar: "sidecar", + Entrypoint: "entrypoint", + }, + }, + }, + } + jobSpec.SetNamespace("ns") + + optedIn := api.LiteralTestStep{ + As: "opted-in", + Environment: []api.StepParameter{ + {Name: "TEST", Overridable: true}, + }, + } + notOptedIn := api.LiteralTestStep{ + As: "not-opted-in", + Environment: []api.StepParameter{ + {Name: "TEST"}, + }, + } + test := []api.LiteralTestStep{optedIn, notOptedIn} + + step := MultiStageTestStep(api.TestStepConfiguration{ + MultiStageTestConfigurationLiteral: &api.MultiStageTestConfigurationLiteral{ + Test: test, + ParamOverrides: api.TestEnvironment{"TEST": "override-value"}, + }, + }, &api.ReleaseBuildConfiguration{}, fakeStepParams{}, nil, &jobSpec, nil, "node-name", "", nil, false, nil, false, wait.Backoff{}) + pods, _, err := step.(*multiStageTestStep).generatePods(test, nil, nil, nil, nil) + if err != nil { + t.Fatal(err) + } + + valueFor := func(podIdx int) string { + for _, v := range pods[podIdx].Spec.Containers[0].Env { + if v.Name == "TEST" { + return v.Value + } + } + return "" + } + + if got := valueFor(0); got != "override-value" { + t.Errorf("opted-in step: expected TEST=override-value, got %q", got) + } + if got := valueFor(1); got != "" { + t.Errorf("non-opted-in step: expected TEST to remain empty, but the override leaked in: got %q", got) + } +} + func TestGeneratePodBestEffort(t *testing.T) { yes := true no := false diff --git a/pkg/steps/multi_stage/multi_stage.go b/pkg/steps/multi_stage/multi_stage.go index f47f6dcc6e..7ee31c9323 100644 --- a/pkg/steps/multi_stage/multi_stage.go +++ b/pkg/steps/multi_stage/multi_stage.go @@ -102,6 +102,7 @@ type multiStageTestStep struct { // params exposes getters for variables created by other steps params api.Parameters env api.TestEnvironment + paramOverrides api.TestEnvironment client kubernetes.PodClient jobSpec *api.JobSpec observers []api.Observer @@ -175,6 +176,7 @@ func newMultiStageTestStep( config: config, params: params, env: ms.Environment, + paramOverrides: ms.ParamOverrides, client: client, jobSpec: jobSpec, observers: ms.Observers, diff --git a/pkg/webreg/zz_generated.ci_operator_reference.go b/pkg/webreg/zz_generated.ci_operator_reference.go index 32938c35ff..cad0fbae44 100644 --- a/pkg/webreg/zz_generated.ci_operator_reference.go +++ b/pkg/webreg/zz_generated.ci_operator_reference.go @@ -730,6 +730,11 @@ const ciOperatorReferenceYaml = "# The list of base images describe\n" + " documentation: ' '\n" + " # Name of the environment variable.\n" + " name: ' '\n" + + " # Overridable, if true, allows this parameter to be set via a trigger-time\n" + + " # environment variable on ci-operator (its own name, or the legacy\n" + + " # MULTISTAGE_PARAM_OVERRIDE_ form, which takes precedence).\n" + + " # Must be explicitly opted into per parameter.\n" + + " overridable: true\n" + " # From is the container image that will be used for this observer.\n" + " from: ' '\n" + " # FromImage is a literal ImageStreamTag reference to use for this observer.\n" + @@ -756,6 +761,11 @@ const ciOperatorReferenceYaml = "# The list of base images describe\n" + " \"\": \"\"\n" + " # Timeout is how long the we will wait before aborting a job with SIGINT.\n" + " timeout: 0s\n" + + " # ParamOverrides holds trigger-time parameter values, populated by\n" + + " # ci-operator from the environment. Only honored for parameters\n" + + " # declared with Overridable: true. Not meant to be set in CI config.\n" + + " param_overrides:\n" + + " \"\": \"\"\n" + " # Post is the array of test steps run after the tests finish and teardown/deprovision resources.\n" + " # Post steps always run, even if previous steps fail.\n" + " post:\n" + @@ -815,6 +825,11 @@ const ciOperatorReferenceYaml = "# The list of base images describe\n" + " documentation: ' '\n" + " # Name of the environment variable.\n" + " name: ' '\n" + + " # Overridable, if true, allows this parameter to be set via a trigger-time\n" + + " # environment variable on ci-operator (its own name, or the legacy\n" + + " # MULTISTAGE_PARAM_OVERRIDE_ form, which takes precedence).\n" + + " # Must be explicitly opted into per parameter.\n" + + " overridable: true\n" + " # From is the container image that will be used for this step.\n" + " from: ' '\n" + " # FromImage is a literal ImageStreamTag reference to use for this step.\n" + @@ -923,6 +938,11 @@ const ciOperatorReferenceYaml = "# The list of base images describe\n" + " documentation: ' '\n" + " # Name of the environment variable.\n" + " name: ' '\n" + + " # Overridable, if true, allows this parameter to be set via a trigger-time\n" + + " # environment variable on ci-operator (its own name, or the legacy\n" + + " # MULTISTAGE_PARAM_OVERRIDE_ form, which takes precedence).\n" + + " # Must be explicitly opted into per parameter.\n" + + " overridable: true\n" + " # From is the container image that will be used for this step.\n" + " from: ' '\n" + " # FromImage is a literal ImageStreamTag reference to use for this step.\n" + @@ -1031,6 +1051,11 @@ const ciOperatorReferenceYaml = "# The list of base images describe\n" + " documentation: ' '\n" + " # Name of the environment variable.\n" + " name: ' '\n" + + " # Overridable, if true, allows this parameter to be set via a trigger-time\n" + + " # environment variable on ci-operator (its own name, or the legacy\n" + + " # MULTISTAGE_PARAM_OVERRIDE_ form, which takes precedence).\n" + + " # Must be explicitly opted into per parameter.\n" + + " overridable: true\n" + " # From is the container image that will be used for this step.\n" + " from: ' '\n" + " # FromImage is a literal ImageStreamTag reference to use for this step.\n" + @@ -1301,6 +1326,7 @@ const ciOperatorReferenceYaml = "# The list of base images describe\n" + " - default: \"\"\n" + " documentation: ' '\n" + " name: ' '\n" + + " overridable: true\n" + " from: ' '\n" + " from_image:\n" + " # LiteralTestStep is a full test step definition.\n" + @@ -1372,6 +1398,7 @@ const ciOperatorReferenceYaml = "# The list of base images describe\n" + " - default: \"\"\n" + " documentation: ' '\n" + " name: ' '\n" + + " overridable: true\n" + " from: ' '\n" + " from_image:\n" + " # LiteralTestStep is a full test step definition.\n" + @@ -1443,6 +1470,7 @@ const ciOperatorReferenceYaml = "# The list of base images describe\n" + " - default: \"\"\n" + " documentation: ' '\n" + " name: ' '\n" + + " overridable: true\n" + " from: ' '\n" + " from_image:\n" + " # LiteralTestStep is a full test step definition.\n" + @@ -1721,6 +1749,11 @@ const ciOperatorReferenceYaml = "# The list of base images describe\n" + " documentation: ' '\n" + " # Name of the environment variable.\n" + " name: ' '\n" + + " # Overridable, if true, allows this parameter to be set via a trigger-time\n" + + " # environment variable on ci-operator (its own name, or the legacy\n" + + " # MULTISTAGE_PARAM_OVERRIDE_ form, which takes precedence).\n" + + " # Must be explicitly opted into per parameter.\n" + + " overridable: true\n" + " # From is the container image that will be used for this observer.\n" + " from: ' '\n" + " # FromImage is a literal ImageStreamTag reference to use for this observer.\n" + @@ -1747,6 +1780,11 @@ const ciOperatorReferenceYaml = "# The list of base images describe\n" + " \"\": \"\"\n" + " # Timeout is how long the we will wait before aborting a job with SIGINT.\n" + " timeout: 0s\n" + + " # ParamOverrides holds trigger-time parameter values, populated by\n" + + " # ci-operator from the environment. Only honored for parameters\n" + + " # declared with Overridable: true. Not meant to be set in CI config.\n" + + " param_overrides:\n" + + " \"\": \"\"\n" + " # Post is the array of test steps run after the tests finish and teardown/deprovision resources.\n" + " # Post steps always run, even if previous steps fail.\n" + " post:\n" + @@ -1806,6 +1844,11 @@ const ciOperatorReferenceYaml = "# The list of base images describe\n" + " documentation: ' '\n" + " # Name of the environment variable.\n" + " name: ' '\n" + + " # Overridable, if true, allows this parameter to be set via a trigger-time\n" + + " # environment variable on ci-operator (its own name, or the legacy\n" + + " # MULTISTAGE_PARAM_OVERRIDE_ form, which takes precedence).\n" + + " # Must be explicitly opted into per parameter.\n" + + " overridable: true\n" + " # From is the container image that will be used for this step.\n" + " from: ' '\n" + " # FromImage is a literal ImageStreamTag reference to use for this step.\n" + @@ -1914,6 +1957,11 @@ const ciOperatorReferenceYaml = "# The list of base images describe\n" + " documentation: ' '\n" + " # Name of the environment variable.\n" + " name: ' '\n" + + " # Overridable, if true, allows this parameter to be set via a trigger-time\n" + + " # environment variable on ci-operator (its own name, or the legacy\n" + + " # MULTISTAGE_PARAM_OVERRIDE_ form, which takes precedence).\n" + + " # Must be explicitly opted into per parameter.\n" + + " overridable: true\n" + " # From is the container image that will be used for this step.\n" + " from: ' '\n" + " # FromImage is a literal ImageStreamTag reference to use for this step.\n" + @@ -2022,6 +2070,11 @@ const ciOperatorReferenceYaml = "# The list of base images describe\n" + " documentation: ' '\n" + " # Name of the environment variable.\n" + " name: ' '\n" + + " # Overridable, if true, allows this parameter to be set via a trigger-time\n" + + " # environment variable on ci-operator (its own name, or the legacy\n" + + " # MULTISTAGE_PARAM_OVERRIDE_ form, which takes precedence).\n" + + " # Must be explicitly opted into per parameter.\n" + + " overridable: true\n" + " # From is the container image that will be used for this step.\n" + " from: ' '\n" + " # FromImage is a literal ImageStreamTag reference to use for this step.\n" + @@ -2292,6 +2345,7 @@ const ciOperatorReferenceYaml = "# The list of base images describe\n" + " - default: \"\"\n" + " documentation: ' '\n" + " name: ' '\n" + + " overridable: true\n" + " from: ' '\n" + " from_image:\n" + " # LiteralTestStep is a full test step definition.\n" + @@ -2363,6 +2417,7 @@ const ciOperatorReferenceYaml = "# The list of base images describe\n" + " - default: \"\"\n" + " documentation: ' '\n" + " name: ' '\n" + + " overridable: true\n" + " from: ' '\n" + " from_image:\n" + " # LiteralTestStep is a full test step definition.\n" + @@ -2434,6 +2489,7 @@ const ciOperatorReferenceYaml = "# The list of base images describe\n" + " - default: \"\"\n" + " documentation: ' '\n" + " name: ' '\n" + + " overridable: true\n" + " from: ' '\n" + " from_image:\n" + " # LiteralTestStep is a full test step definition.\n" +