diff --git a/docs/pipeline-api.md b/docs/pipeline-api.md
index f100fbf2561..16f4cb440f5 100644
--- a/docs/pipeline-api.md
+++ b/docs/pipeline-api.md
@@ -6541,6 +6541,34 @@ string
If Script is not empty, the Step cannot have an Command and the Args will be passed to the Script.
+
+
+Params
+
+
+ParamSpecs
+
+
+ |
+
+(Optional)
+ Params is a list of input parameters required to run the stepAction.
+Params must be supplied as inputs in Steps unless they declare a defaultvalue.
+ |
+
+
+
+Results
+
+
+[]StepActionResult
+
+
+ |
+
+ Results are values that this StepAction can output
+ |
+
@@ -6991,6 +7019,200 @@ HashAlgorithm
ModeType indicates the type of a mode for VerificationPolicy
+ParamSpec
+
+
+
ParamSpec defines arbitrary parameters needed beyond typed inputs (such as
+resources). Parameter values are provided by users as inputs on a TaskRun
+or PipelineRun.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+name
+
+string
+
+ |
+
+ Name declares the name by which a parameter is referenced.
+ |
+
+
+
+type
+
+
+ParamType
+
+
+ |
+
+(Optional)
+ Type is the user-specified type of the parameter. The possible types
+are currently “string”, “array” and “object”, and “string” is the default.
+ |
+
+
+
+description
+
+string
+
+ |
+
+(Optional)
+ Description is a user-facing description of the parameter that may be
+used to populate a UI.
+ |
+
+
+
+properties
+
+
+map[string]github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.PropertySpec
+
+
+ |
+
+(Optional)
+ Properties is the JSON Schema properties to support key-value pairs parameter.
+ |
+
+
+
+default
+
+
+ParamValue
+
+
+ |
+
+(Optional)
+ Default is the value a parameter takes if no input value is supplied. If
+default is set, a Task may be executed without a supplied value for the
+parameter.
+ |
+
+
+
+ParamSpecs
+([]github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.ParamSpec
alias)
+
+(Appears on:StepActionSpec)
+
+
+
ParamSpecs is a list of ParamSpec
+
+ParamType
+(string
alias)
+
+(Appears on:ParamSpec, ParamValue, PropertySpec)
+
+
+
ParamType indicates the type of an input parameter;
+Used to distinguish between a single string and an array of strings.
+
+ParamValue
+
+
+(Appears on:ParamSpec)
+
+
+
ResultValue is a type alias of ParamValue
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+Type
+
+
+ParamType
+
+
+ |
+
+ |
+
+
+
+StringVal
+
+string
+
+ |
+
+ Represents the stored type of ParamValues.
+ |
+
+
+
+ArrayVal
+
+[]string
+
+ |
+
+ |
+
+
+
+ObjectVal
+
+map[string]string
+
+ |
+
+ |
+
+
+
+PropertySpec
+
+
+(Appears on:ParamSpec, StepActionResult)
+
+
+
PropertySpec defines the struct for object keys
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+type
+
+
+ParamType
+
+
+ |
+
+ |
+
+
+
ResourcePattern
@@ -7024,6 +7246,18 @@ Hub resource: https://artifacthub.io/*,
+
ResultsType
+(string
alias)
+
+(Appears on:StepActionResult)
+
+
+
ResultsType indicates the type of a result;
+Used to distinguish between a single string and an array of strings.
+Note that there is ResultType used to find out whether a
+RunResult is from a task result or not, which is different from
+this ResultsType.
+
RunReason
(string
alias)
@@ -7206,6 +7440,76 @@ Refer Go’s ParseDuration documentation for expected format:
StepActionResult
+
+
+(Appears on:StepActionSpec)
+
+
+
StepActionResult used to describe the results of a task
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+name
+
+string
+
+ |
+
+ Name the given name
+ |
+
+
+
+type
+
+
+ResultsType
+
+
+ |
+
+(Optional)
+ Type is the user-specified type of the result. The possible type
+is currently “string” and will support “array” in following work.
+ |
+
+
+
+properties
+
+
+map[string]github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.PropertySpec
+
+
+ |
+
+(Optional)
+ Properties is the JSON Schema properties to support key-value pairs results.
+ |
+
+
+
+description
+
+string
+
+ |
+
+(Optional)
+ Description is a human-readable description of the result
+ |
+
+
+
StepActionSpec
@@ -7301,6 +7605,34 @@ string
If Script is not empty, the Step cannot have an Command and the Args will be passed to the Script.
+
+
+Params
+
+
+ParamSpecs
+
+
+ |
+
+(Optional)
+ Params is a list of input parameters required to run the stepAction.
+Params must be supplied as inputs in Steps unless they declare a defaultvalue.
+ |
+
+
+
+Results
+
+
+[]StepActionResult
+
+
+ |
+
+ Results are values that this StepAction can output
+ |
+
VerificationPolicySpec
diff --git a/pkg/apis/pipeline/v1alpha1/openapi_generated.go b/pkg/apis/pipeline/v1alpha1/openapi_generated.go
index 876cb0bdc84..406ef76c04c 100644
--- a/pkg/apis/pipeline/v1alpha1/openapi_generated.go
+++ b/pkg/apis/pipeline/v1alpha1/openapi_generated.go
@@ -35,12 +35,16 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.Authority": schema_pkg_apis_pipeline_v1alpha1_Authority(ref),
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.EmbeddedRunSpec": schema_pkg_apis_pipeline_v1alpha1_EmbeddedRunSpec(ref),
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.KeyRef": schema_pkg_apis_pipeline_v1alpha1_KeyRef(ref),
+ "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.ParamSpec": schema_pkg_apis_pipeline_v1alpha1_ParamSpec(ref),
+ "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.ParamValue": schema_pkg_apis_pipeline_v1alpha1_ParamValue(ref),
+ "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.PropertySpec": schema_pkg_apis_pipeline_v1alpha1_PropertySpec(ref),
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.ResourcePattern": schema_pkg_apis_pipeline_v1alpha1_ResourcePattern(ref),
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.Run": schema_pkg_apis_pipeline_v1alpha1_Run(ref),
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.RunList": schema_pkg_apis_pipeline_v1alpha1_RunList(ref),
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.RunSpec": schema_pkg_apis_pipeline_v1alpha1_RunSpec(ref),
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.StepAction": schema_pkg_apis_pipeline_v1alpha1_StepAction(ref),
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.StepActionList": schema_pkg_apis_pipeline_v1alpha1_StepActionList(ref),
+ "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.StepActionResult": schema_pkg_apis_pipeline_v1alpha1_StepActionResult(ref),
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.StepActionSpec": schema_pkg_apis_pipeline_v1alpha1_StepActionSpec(ref),
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.VerificationPolicy": schema_pkg_apis_pipeline_v1alpha1_VerificationPolicy(ref),
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.VerificationPolicyList": schema_pkg_apis_pipeline_v1alpha1_VerificationPolicyList(ref),
@@ -445,6 +449,147 @@ func schema_pkg_apis_pipeline_v1alpha1_KeyRef(ref common.ReferenceCallback) comm
}
}
+func schema_pkg_apis_pipeline_v1alpha1_ParamSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "ParamSpec defines arbitrary parameters needed beyond typed inputs (such as resources). Parameter values are provided by users as inputs on a TaskRun or PipelineRun.",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "name": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Name declares the name by which a parameter is referenced.",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "type": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Type is the user-specified type of the parameter. The possible types are currently \"string\", \"array\" and \"object\", and \"string\" is the default.",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "description": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Description is a user-facing description of the parameter that may be used to populate a UI.",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "properties": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Properties is the JSON Schema properties to support key-value pairs parameter.",
+ Type: []string{"object"},
+ AdditionalProperties: &spec.SchemaOrBool{
+ Allows: true,
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.PropertySpec"),
+ },
+ },
+ },
+ },
+ },
+ "default": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Default is the value a parameter takes if no input value is supplied. If default is set, a Task may be executed without a supplied value for the parameter.",
+ Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.ParamValue"),
+ },
+ },
+ },
+ Required: []string{"name"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.ParamValue", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.PropertySpec"},
+ }
+}
+
+func schema_pkg_apis_pipeline_v1alpha1_ParamValue(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "ResultValue is a type alias of ParamValue",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "Type": {
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "StringVal": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Represents the stored type of ParamValues.",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "ArrayVal": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-list-type": "atomic",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ },
+ },
+ },
+ "ObjectVal": {
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"object"},
+ AdditionalProperties: &spec.SchemaOrBool{
+ Allows: true,
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ },
+ },
+ },
+ },
+ Required: []string{"Type", "StringVal", "ArrayVal", "ObjectVal"},
+ },
+ },
+ }
+}
+
+func schema_pkg_apis_pipeline_v1alpha1_PropertySpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "PropertySpec defines the struct for object keys",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "type": {
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
func schema_pkg_apis_pipeline_v1alpha1_ResourcePattern(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -747,6 +892,59 @@ func schema_pkg_apis_pipeline_v1alpha1_StepActionList(ref common.ReferenceCallba
}
}
+func schema_pkg_apis_pipeline_v1alpha1_StepActionResult(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "StepActionResult used to describe the results of a task",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "name": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Name the given name",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "type": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Type is the user-specified type of the result. The possible type is currently \"string\" and will support \"array\" in following work.",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "properties": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Properties is the JSON Schema properties to support key-value pairs results.",
+ Type: []string{"object"},
+ AdditionalProperties: &spec.SchemaOrBool{
+ Allows: true,
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.PropertySpec"),
+ },
+ },
+ },
+ },
+ },
+ "description": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Description is a human-readable description of the result",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ },
+ Required: []string{"name"},
+ },
+ },
+ Dependencies: []string{
+ "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.PropertySpec"},
+ }
+}
+
func schema_pkg_apis_pipeline_v1alpha1_StepActionSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -829,11 +1027,50 @@ func schema_pkg_apis_pipeline_v1alpha1_StepActionSpec(ref common.ReferenceCallba
Format: "",
},
},
+ "Params": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-list-type": "atomic",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Description: "Params is a list of input parameters required to run the stepAction. Params must be supplied as inputs in Steps unless they declare a defaultvalue.",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.ParamSpec"),
+ },
+ },
+ },
+ },
+ },
+ "Results": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-list-type": "atomic",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Description: "Results are values that this StepAction can output",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.StepActionResult"),
+ },
+ },
+ },
+ },
+ },
},
+ Required: []string{"Results"},
},
},
Dependencies: []string{
- "k8s.io/api/core/v1.EnvVar"},
+ "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.ParamSpec", "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1.StepActionResult", "k8s.io/api/core/v1.EnvVar"},
}
}
diff --git a/pkg/apis/pipeline/v1alpha1/param_types.go b/pkg/apis/pipeline/v1alpha1/param_types.go
new file mode 100644
index 00000000000..9069c5f47b5
--- /dev/null
+++ b/pkg/apis/pipeline/v1alpha1/param_types.go
@@ -0,0 +1,256 @@
+/*
+Copyright 2019 The Tekton Authors
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+
+ "k8s.io/apimachinery/pkg/util/sets"
+ "knative.dev/pkg/apis"
+)
+
+// ParamsPrefix is the prefix used in $(...) expressions referring to parameters
+const ParamsPrefix = "params"
+
+// ParamSpec defines arbitrary parameters needed beyond typed inputs (such as
+// resources). Parameter values are provided by users as inputs on a TaskRun
+// or PipelineRun.
+type ParamSpec struct {
+ // Name declares the name by which a parameter is referenced.
+ Name string `json:"name"`
+ // Type is the user-specified type of the parameter. The possible types
+ // are currently "string", "array" and "object", and "string" is the default.
+ // +optional
+ Type ParamType `json:"type,omitempty"`
+ // Description is a user-facing description of the parameter that may be
+ // used to populate a UI.
+ // +optional
+ Description string `json:"description,omitempty"`
+ // Properties is the JSON Schema properties to support key-value pairs parameter.
+ // +optional
+ Properties map[string]PropertySpec `json:"properties,omitempty"`
+ // Default is the value a parameter takes if no input value is supplied. If
+ // default is set, a Task may be executed without a supplied value for the
+ // parameter.
+ // +optional
+ Default *ParamValue `json:"default,omitempty"`
+}
+
+// ParamSpecs is a list of ParamSpec
+type ParamSpecs []ParamSpec
+
+// PropertySpec defines the struct for object keys
+type PropertySpec struct {
+ Type ParamType `json:"type,omitempty"`
+}
+
+// SetDefaults set the default type
+func (pp *ParamSpec) SetDefaults(context.Context) {
+ if pp == nil {
+ return
+ }
+
+ // Propagate inferred type to the parent ParamSpec's type, and default type to the PropertySpec's type
+ // The sequence to look at is type in ParamSpec -> properties -> type in default -> array/string/object value in default
+ // If neither `properties` or `default` section is provided, ParamTypeString will be the default type.
+ switch {
+ case pp.Type != "":
+ // If param type is provided by the author, do nothing but just set default type for PropertySpec in case `properties` section is provided.
+ pp.setDefaultsForProperties()
+ case pp.Properties != nil:
+ pp.Type = ParamTypeObject
+ // Also set default type for PropertySpec
+ pp.setDefaultsForProperties()
+ case pp.Default == nil:
+ // ParamTypeString is the default value (when no type can be inferred from the default value)
+ pp.Type = ParamTypeString
+ case pp.Default.Type != "":
+ pp.Type = pp.Default.Type
+ case pp.Default.ArrayVal != nil:
+ pp.Type = ParamTypeArray
+ case pp.Default.ObjectVal != nil:
+ pp.Type = ParamTypeObject
+ default:
+ pp.Type = ParamTypeString
+ }
+}
+
+// getNames returns all the names of the declared parameters
+func (ps ParamSpecs) getNames() []string {
+ var names []string
+ for _, p := range ps {
+ names = append(names, p.Name)
+ }
+ return names
+}
+
+// sortByType splits the input params into string params, array params, and object params, in that order
+func (ps ParamSpecs) sortByType() (ParamSpecs, ParamSpecs, ParamSpecs) {
+ var stringParams, arrayParams, objectParams ParamSpecs
+ for _, p := range ps {
+ switch p.Type {
+ case ParamTypeArray:
+ arrayParams = append(arrayParams, p)
+ case ParamTypeObject:
+ objectParams = append(objectParams, p)
+ case ParamTypeString:
+ fallthrough
+ default:
+ stringParams = append(stringParams, p)
+ }
+ }
+ return stringParams, arrayParams, objectParams
+}
+
+// validateNoDuplicateNames returns an error if any of the params have the same name
+func (ps ParamSpecs) validateNoDuplicateNames() *apis.FieldError {
+ names := ps.getNames()
+ seen := sets.String{}
+ dups := sets.String{}
+ var errs *apis.FieldError
+ for _, n := range names {
+ if seen.Has(n) {
+ dups.Insert(n)
+ }
+ seen.Insert(n)
+ }
+ for n := range dups {
+ errs = errs.Also(apis.ErrGeneric("parameter appears more than once", "").ViaFieldKey("params", n))
+ }
+ return errs
+}
+
+// setDefaultsForProperties sets default type for PropertySpec (string) if it's not specified
+func (pp *ParamSpec) setDefaultsForProperties() {
+ for key, propertySpec := range pp.Properties {
+ if propertySpec.Type == "" {
+ pp.Properties[key] = PropertySpec{Type: ParamTypeString}
+ }
+ }
+}
+
+// ParamType indicates the type of an input parameter;
+// Used to distinguish between a single string and an array of strings.
+type ParamType string
+
+// Valid ParamTypes:
+const (
+ ParamTypeString ParamType = "string"
+ ParamTypeArray ParamType = "array"
+ ParamTypeObject ParamType = "object"
+)
+
+// AllParamTypes can be used for ParamType validation.
+var AllParamTypes = []ParamType{ParamTypeString, ParamTypeArray, ParamTypeObject}
+
+// ParamValues is modeled after IntOrString in kubernetes/apimachinery:
+
+// ParamValue is a type that can hold a single string or string array.
+// Used in JSON unmarshalling so that a single JSON field can accept
+// either an individual string or an array of strings.
+type ParamValue struct {
+ Type ParamType // Represents the stored type of ParamValues.
+ StringVal string
+ // +listType=atomic
+ ArrayVal []string
+ ObjectVal map[string]string
+}
+
+// ArrayOrString is deprecated, this is to keep backward compatibility
+//
+// Deprecated: Use ParamValue instead.
+type ArrayOrString = ParamValue
+
+// UnmarshalJSON implements the json.Unmarshaller interface.
+func (paramValues *ParamValue) UnmarshalJSON(value []byte) error {
+ // ParamValues is used for Results Value as well, the results can be any kind of
+ // data so we need to check if it is empty.
+ if len(value) == 0 {
+ paramValues.Type = ParamTypeString
+ return nil
+ }
+ if value[0] == '[' {
+ // We're trying to Unmarshal to []string, but for cases like []int or other types
+ // of nested array which we don't support yet, we should continue and Unmarshal
+ // it to String. If the Type being set doesn't match what it actually should be,
+ // it will be captured by validation in reconciler.
+ // if failed to unmarshal to array, we will convert the value to string and marshal it to string
+ var a []string
+ if err := json.Unmarshal(value, &a); err == nil {
+ paramValues.Type = ParamTypeArray
+ paramValues.ArrayVal = a
+ return nil
+ }
+ }
+ if value[0] == '{' {
+ // if failed to unmarshal to map, we will convert the value to string and marshal it to string
+ var m map[string]string
+ if err := json.Unmarshal(value, &m); err == nil {
+ paramValues.Type = ParamTypeObject
+ paramValues.ObjectVal = m
+ return nil
+ }
+ }
+
+ // By default we unmarshal to string
+ paramValues.Type = ParamTypeString
+ if err := json.Unmarshal(value, ¶mValues.StringVal); err == nil {
+ return nil
+ }
+ paramValues.StringVal = string(value)
+
+ return nil
+}
+
+// MarshalJSON implements the json.Marshaller interface.
+func (paramValues ParamValue) MarshalJSON() ([]byte, error) {
+ switch paramValues.Type {
+ case ParamTypeString:
+ return json.Marshal(paramValues.StringVal)
+ case ParamTypeArray:
+ return json.Marshal(paramValues.ArrayVal)
+ case ParamTypeObject:
+ return json.Marshal(paramValues.ObjectVal)
+ default:
+ return []byte{}, fmt.Errorf("impossible ParamValues.Type: %q", paramValues.Type)
+ }
+}
+
+// NewStructuredValues creates an ParamValues of type ParamTypeString or ParamTypeArray, based on
+// how many inputs are given (>1 input will create an array, not string).
+func NewStructuredValues(value string, values ...string) *ParamValue {
+ if len(values) > 0 {
+ return &ParamValue{
+ Type: ParamTypeArray,
+ ArrayVal: append([]string{value}, values...),
+ }
+ }
+ return &ParamValue{
+ Type: ParamTypeString,
+ StringVal: value,
+ }
+}
+
+// NewArrayOrString is the deprecated, this is to keep backward compatibility
+var NewArrayOrString = NewStructuredValues
+
+// NewObject creates an ParamValues of type ParamTypeObject using the provided key-value pairs
+func NewObject(pairs map[string]string) *ParamValue {
+ return &ParamValue{
+ Type: ParamTypeObject,
+ ObjectVal: pairs,
+ }
+}
diff --git a/pkg/apis/pipeline/v1alpha1/param_types_test.go b/pkg/apis/pipeline/v1alpha1/param_types_test.go
new file mode 100644
index 00000000000..ea72ec8ce69
--- /dev/null
+++ b/pkg/apis/pipeline/v1alpha1/param_types_test.go
@@ -0,0 +1,296 @@
+/*
+Copyright 2022 The Tekton Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1_test
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "reflect"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1"
+ "github.com/tektoncd/pipeline/test/diff"
+)
+
+func TestParamSpec_SetDefaults(t *testing.T) {
+ tests := []struct {
+ name string
+ before *v1alpha1.ParamSpec
+ defaultsApplied *v1alpha1.ParamSpec
+ }{{
+ name: "nil paramspec",
+ before: nil,
+ defaultsApplied: nil,
+ }, {
+ name: "inferred string type",
+ before: &v1alpha1.ParamSpec{
+ Name: "parametername",
+ },
+ defaultsApplied: &v1alpha1.ParamSpec{
+ Name: "parametername",
+ Type: v1alpha1.ParamTypeString,
+ },
+ }, {
+ name: "inferred type from default value - array",
+ before: &v1alpha1.ParamSpec{
+ Name: "parametername",
+ Default: &v1alpha1.ParamValue{
+ ArrayVal: []string{"array"},
+ },
+ },
+ defaultsApplied: &v1alpha1.ParamSpec{
+ Name: "parametername",
+ Type: v1alpha1.ParamTypeArray,
+ Default: &v1alpha1.ParamValue{
+ ArrayVal: []string{"array"},
+ },
+ },
+ }, {
+ name: "inferred type from default value - string",
+ before: &v1alpha1.ParamSpec{
+ Name: "parametername",
+ Default: &v1alpha1.ParamValue{
+ StringVal: "an",
+ },
+ },
+ defaultsApplied: &v1alpha1.ParamSpec{
+ Name: "parametername",
+ Type: v1alpha1.ParamTypeString,
+ Default: &v1alpha1.ParamValue{
+ StringVal: "an",
+ },
+ },
+ }, {
+ name: "inferred type from default value - object",
+ before: &v1alpha1.ParamSpec{
+ Name: "parametername",
+ Default: &v1alpha1.ParamValue{
+ ObjectVal: map[string]string{"url": "test", "path": "test"},
+ },
+ },
+ defaultsApplied: &v1alpha1.ParamSpec{
+ Name: "parametername",
+ Type: v1alpha1.ParamTypeObject,
+ Default: &v1alpha1.ParamValue{
+ ObjectVal: map[string]string{"url": "test", "path": "test"},
+ },
+ },
+ }, {
+ name: "inferred type from properties - PropertySpec type is not provided",
+ before: &v1alpha1.ParamSpec{
+ Name: "parametername",
+ Properties: map[string]v1alpha1.PropertySpec{"key1": {}},
+ },
+ defaultsApplied: &v1alpha1.ParamSpec{
+ Name: "parametername",
+ Type: v1alpha1.ParamTypeObject,
+ Properties: map[string]v1alpha1.PropertySpec{"key1": {Type: "string"}},
+ },
+ }, {
+ name: "inferred type from properties - PropertySpec type is provided",
+ before: &v1alpha1.ParamSpec{
+ Name: "parametername",
+ Properties: map[string]v1alpha1.PropertySpec{"key2": {Type: "string"}},
+ },
+ defaultsApplied: &v1alpha1.ParamSpec{
+ Name: "parametername",
+ Type: v1alpha1.ParamTypeObject,
+ Properties: map[string]v1alpha1.PropertySpec{"key2": {Type: "string"}},
+ },
+ }, {
+ name: "fully defined ParamSpec - array",
+ before: &v1alpha1.ParamSpec{
+ Name: "parametername",
+ Type: v1alpha1.ParamTypeArray,
+ Description: "a description",
+ Default: &v1alpha1.ParamValue{
+ ArrayVal: []string{"array"},
+ },
+ },
+ defaultsApplied: &v1alpha1.ParamSpec{
+ Name: "parametername",
+ Type: v1alpha1.ParamTypeArray,
+ Description: "a description",
+ Default: &v1alpha1.ParamValue{
+ ArrayVal: []string{"array"},
+ },
+ },
+ }, {
+ name: "fully defined ParamSpec - object",
+ before: &v1alpha1.ParamSpec{
+ Name: "parametername",
+ Type: v1alpha1.ParamTypeObject,
+ Description: "a description",
+ Default: &v1alpha1.ParamValue{
+ ObjectVal: map[string]string{"url": "test", "path": "test"},
+ },
+ },
+ defaultsApplied: &v1alpha1.ParamSpec{
+ Name: "parametername",
+ Type: v1alpha1.ParamTypeObject,
+ Description: "a description",
+ Default: &v1alpha1.ParamValue{
+ ObjectVal: map[string]string{"url": "test", "path": "test"},
+ },
+ },
+ }}
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ ctx := context.Background()
+ tc.before.SetDefaults(ctx)
+ if d := cmp.Diff(tc.defaultsApplied, tc.before); d != "" {
+ t.Error(diff.PrintWantGot(d))
+ }
+ })
+ }
+}
+
+type ParamValuesHolder struct {
+ AOrS v1alpha1.ParamValue `json:"val"`
+}
+
+func TestParamValues_UnmarshalJSON(t *testing.T) {
+ cases := []struct {
+ input map[string]interface{}
+ result v1alpha1.ParamValue
+ }{
+ {
+ input: map[string]interface{}{"val": 123},
+ result: *v1alpha1.NewStructuredValues("123"),
+ },
+ {
+ input: map[string]interface{}{"val": "123"},
+ result: *v1alpha1.NewStructuredValues("123"),
+ },
+ {
+ input: map[string]interface{}{"val": ""},
+ result: *v1alpha1.NewStructuredValues(""),
+ },
+ {
+ input: map[string]interface{}{"val": nil},
+ result: v1alpha1.ParamValue{Type: v1alpha1.ParamTypeString, ArrayVal: nil},
+ },
+ {
+ input: map[string]interface{}{"val": []string{}},
+ result: v1alpha1.ParamValue{Type: v1alpha1.ParamTypeArray, ArrayVal: []string{}},
+ },
+ {
+ input: map[string]interface{}{"val": []string{"oneelement"}},
+ result: v1alpha1.ParamValue{Type: v1alpha1.ParamTypeArray, ArrayVal: []string{"oneelement"}},
+ },
+ {
+ input: map[string]interface{}{"val": []string{"multiple", "elements"}},
+ result: v1alpha1.ParamValue{Type: v1alpha1.ParamTypeArray, ArrayVal: []string{"multiple", "elements"}},
+ },
+ {
+ input: map[string]interface{}{"val": map[string]string{"key1": "val1", "key2": "val2"}},
+ result: v1alpha1.ParamValue{Type: v1alpha1.ParamTypeObject, ObjectVal: map[string]string{"key1": "val1", "key2": "val2"}},
+ },
+ }
+
+ for _, c := range cases {
+ for _, opts := range []func(enc *json.Encoder){
+ // Default encoding
+ func(enc *json.Encoder) {},
+ // Multiline encoding
+ func(enc *json.Encoder) { enc.SetIndent("", " ") },
+ } {
+ b := new(bytes.Buffer)
+ enc := json.NewEncoder(b)
+ opts(enc)
+ if err := enc.Encode(c.input); err != nil {
+ t.Fatalf("error encoding json: %v", err)
+ }
+
+ var result ParamValuesHolder
+ if err := json.Unmarshal(b.Bytes(), &result); err != nil {
+ t.Errorf("Failed to unmarshal input '%v': %v", c.input, err)
+ }
+ if !reflect.DeepEqual(result.AOrS, c.result) {
+ t.Errorf("expected %+v, got %+v", c.result, result)
+ }
+ }
+ }
+}
+
+func TestParamValues_UnmarshalJSON_Directly(t *testing.T) {
+ cases := []struct {
+ desc string
+ input string
+ expected v1alpha1.ParamValue
+ }{
+ {desc: "empty value", input: ``, expected: *v1alpha1.NewStructuredValues("")},
+ {desc: "int value", input: `1`, expected: *v1alpha1.NewStructuredValues("1")},
+ {desc: "int array", input: `[1,2,3]`, expected: *v1alpha1.NewStructuredValues("[1,2,3]")},
+ {desc: "nested array", input: `[1,\"2\",3]`, expected: *v1alpha1.NewStructuredValues(`[1,\"2\",3]`)},
+ {desc: "string value", input: `hello`, expected: *v1alpha1.NewStructuredValues("hello")},
+ {desc: "array value", input: `["hello","world"]`, expected: *v1alpha1.NewStructuredValues("hello", "world")},
+ {desc: "object value", input: `{"hello":"world"}`, expected: *v1alpha1.NewObject(map[string]string{"hello": "world"})},
+ }
+
+ for _, c := range cases {
+ v := v1alpha1.ParamValue{}
+ if err := v.UnmarshalJSON([]byte(c.input)); err != nil {
+ t.Errorf("Failed to unmarshal input '%v': %v", c.input, err)
+ }
+ if !reflect.DeepEqual(v, c.expected) {
+ t.Errorf("Failed to unmarshal input '%v': expected %+v, got %+v", c.input, c.expected, v)
+ }
+ }
+}
+
+func TestParamValues_UnmarshalJSON_Error(t *testing.T) {
+ cases := []struct {
+ desc string
+ input string
+ }{
+ {desc: "empty value", input: "{\"val\": }"},
+ {desc: "wrong beginning value", input: "{\"val\": @}"},
+ }
+
+ for _, c := range cases {
+ var result ParamValuesHolder
+ if err := json.Unmarshal([]byte(c.input), &result); err == nil {
+ t.Errorf("Should return err but got nil '%v'", c.input)
+ }
+ }
+}
+
+func TestParamValues_MarshalJSON(t *testing.T) {
+ cases := []struct {
+ input v1alpha1.ParamValue
+ result string
+ }{
+ {*v1alpha1.NewStructuredValues("123"), "{\"val\":\"123\"}"},
+ {*v1alpha1.NewStructuredValues("123", "1234"), "{\"val\":[\"123\",\"1234\"]}"},
+ {*v1alpha1.NewStructuredValues("a", "a", "a"), "{\"val\":[\"a\",\"a\",\"a\"]}"},
+ {*v1alpha1.NewObject(map[string]string{"key1": "var1", "key2": "var2"}), "{\"val\":{\"key1\":\"var1\",\"key2\":\"var2\"}}"},
+ }
+
+ for _, c := range cases {
+ input := ParamValuesHolder{c.input}
+ result, err := json.Marshal(&input)
+ if err != nil {
+ t.Errorf("Failed to marshal input '%v': %v", input, err)
+ }
+ if string(result) != c.result {
+ t.Errorf("Failed to marshal input '%v': expected: %+v, got %q", input, c.result, string(result))
+ }
+ }
+}
diff --git a/pkg/apis/pipeline/v1alpha1/result_defaults_test.go b/pkg/apis/pipeline/v1alpha1/result_defaults_test.go
new file mode 100644
index 00000000000..ccf39579402
--- /dev/null
+++ b/pkg/apis/pipeline/v1alpha1/result_defaults_test.go
@@ -0,0 +1,95 @@
+/*
+Copyright 2022 The Tekton Authors
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1_test
+
+import (
+ "context"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1"
+ "github.com/tektoncd/pipeline/test/diff"
+)
+
+func TestStepActionResult_SetDefaults(t *testing.T) {
+ tests := []struct {
+ name string
+ before *v1alpha1.StepActionResult
+ after *v1alpha1.StepActionResult
+ }{{
+ name: "empty taskresult",
+ before: nil,
+ after: nil,
+ }, {
+ name: "inferred string type",
+ before: &v1alpha1.StepActionResult{
+ Name: "resultname",
+ },
+ after: &v1alpha1.StepActionResult{
+ Name: "resultname",
+ Type: v1alpha1.ResultsTypeString,
+ },
+ }, {
+ name: "string type specified not changed",
+ before: &v1alpha1.StepActionResult{
+ Name: "resultname",
+ Type: v1alpha1.ResultsTypeString,
+ },
+ after: &v1alpha1.StepActionResult{
+ Name: "resultname",
+ Type: v1alpha1.ResultsTypeString,
+ },
+ }, {
+ name: "array type specified not changed",
+ before: &v1alpha1.StepActionResult{
+ Name: "resultname",
+ Type: v1alpha1.ResultsTypeArray,
+ },
+ after: &v1alpha1.StepActionResult{
+ Name: "resultname",
+ Type: v1alpha1.ResultsTypeArray,
+ },
+ }, {
+ name: "inferred object type from properties - PropertySpec type is provided",
+ before: &v1alpha1.StepActionResult{
+ Name: "resultname",
+ Properties: map[string]v1alpha1.PropertySpec{"key1": {v1alpha1.ParamTypeString}},
+ },
+ after: &v1alpha1.StepActionResult{
+ Name: "resultname",
+ Type: v1alpha1.ResultsTypeObject,
+ Properties: map[string]v1alpha1.PropertySpec{"key1": {v1alpha1.ParamTypeString}},
+ },
+ }, {
+ name: "inferred type from properties - PropertySpec type is not provided",
+ before: &v1alpha1.StepActionResult{
+ Name: "resultname",
+ Properties: map[string]v1alpha1.PropertySpec{"key1": {}},
+ },
+ after: &v1alpha1.StepActionResult{
+ Name: "resultname",
+ Type: v1alpha1.ResultsTypeObject,
+ Properties: map[string]v1alpha1.PropertySpec{"key1": {v1alpha1.ParamTypeString}},
+ },
+ }}
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ ctx := context.Background()
+ tc.before.SetDefaults(ctx)
+ if d := cmp.Diff(tc.after, tc.before); d != "" {
+ t.Error(diff.PrintWantGot(d))
+ }
+ })
+ }
+}
diff --git a/pkg/apis/pipeline/v1alpha1/result_types.go b/pkg/apis/pipeline/v1alpha1/result_types.go
new file mode 100644
index 00000000000..380a1f751b9
--- /dev/null
+++ b/pkg/apis/pipeline/v1alpha1/result_types.go
@@ -0,0 +1,80 @@
+/*
+Copyright 2023 The Tekton Authors
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+ "context"
+)
+
+// StepActionResult used to describe the results of a task
+type StepActionResult struct {
+ // Name the given name
+ Name string `json:"name"`
+
+ // Type is the user-specified type of the result. The possible type
+ // is currently "string" and will support "array" in following work.
+ // +optional
+ Type ResultsType `json:"type,omitempty"`
+
+ // Properties is the JSON Schema properties to support key-value pairs results.
+ // +optional
+ Properties map[string]PropertySpec `json:"properties,omitempty"`
+
+ // Description is a human-readable description of the result
+ // +optional
+ Description string `json:"description,omitempty"`
+}
+
+// SetDefaults set the default type for StepActionResult
+func (sar *StepActionResult) SetDefaults(context.Context) {
+ if sar == nil {
+ return
+ }
+ if sar.Type == "" {
+ if sar.Properties != nil {
+ // Set type to object if `properties` is given
+ sar.Type = ResultsTypeObject
+ } else {
+ // ResultsTypeString is the default value
+ sar.Type = ResultsTypeString
+ }
+ }
+
+ // Set default type of object values to string
+ for key, propertySpec := range sar.Properties {
+ if propertySpec.Type == "" {
+ sar.Properties[key] = PropertySpec{Type: ParamType(ResultsTypeString)}
+ }
+ }
+}
+
+// ResultValue is a type alias of ParamValue
+type ResultValue = ParamValue
+
+// ResultsType indicates the type of a result;
+// Used to distinguish between a single string and an array of strings.
+// Note that there is ResultType used to find out whether a
+// RunResult is from a task result or not, which is different from
+// this ResultsType.
+type ResultsType string
+
+// Valid ResultsType:
+const (
+ ResultsTypeString ResultsType = "string"
+ ResultsTypeArray ResultsType = "array"
+ ResultsTypeObject ResultsType = "object"
+)
+
+// AllResultsTypes can be used for ResultsTypes validation.
+var AllResultsTypes = []ResultsType{ResultsTypeString, ResultsTypeArray, ResultsTypeObject}
diff --git a/pkg/apis/pipeline/v1alpha1/result_validation.go b/pkg/apis/pipeline/v1alpha1/result_validation.go
new file mode 100644
index 00000000000..eb9c5ebe31b
--- /dev/null
+++ b/pkg/apis/pipeline/v1alpha1/result_validation.go
@@ -0,0 +1,76 @@
+/*
+Copyright 2023 The Tekton Authors
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+ "context"
+ "fmt"
+ "regexp"
+
+ "knative.dev/pkg/apis"
+)
+
+const (
+ // resultNameFormat Constant used to define the regex Result.Name should follow
+ resultNameFormat = `^([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$`
+)
+
+var resultNameFormatRegex = regexp.MustCompile(resultNameFormat)
+
+// validate implements apis.Validatable
+func (sar StepActionResult) validate(ctx context.Context) (errs *apis.FieldError) {
+ if !resultNameFormatRegex.MatchString(sar.Name) {
+ return apis.ErrInvalidKeyName(sar.Name, "name", fmt.Sprintf("Name must consist of alphanumeric characters, '-', '_', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my-name', or 'my_name', regex used for validation is '%s')", resultNameFormat))
+ }
+
+ switch {
+ case sar.Type == ResultsTypeObject:
+ errs := validateObjectResult(sar)
+ return errs
+ case sar.Type == ResultsTypeArray:
+ return nil
+ // Resources created before the result. Type was introduced may not have Type set
+ // and should be considered valid
+ case sar.Type == "":
+ return nil
+ // By default, the result type is string
+ case sar.Type != ResultsTypeString:
+ return apis.ErrInvalidValue(sar.Type, "type", "type must be string")
+ }
+
+ return nil
+}
+
+// validateObjectResult validates the object result and check if the Properties is missing
+// for Properties values it will check if the type is string.
+func validateObjectResult(sar StepActionResult) (errs *apis.FieldError) {
+ if ParamType(sar.Type) == ParamTypeObject && sar.Properties == nil {
+ return apis.ErrMissingField(fmt.Sprintf("%s.properties", sar.Name))
+ }
+
+ invalidKeys := []string{}
+ for key, propertySpec := range sar.Properties {
+ if propertySpec.Type != ParamTypeString {
+ invalidKeys = append(invalidKeys, key)
+ }
+ }
+
+ if len(invalidKeys) != 0 {
+ return &apis.FieldError{
+ Message: fmt.Sprintf("The value type specified for these keys %v is invalid, the type must be string", invalidKeys),
+ Paths: []string{fmt.Sprintf("%s.properties", sar.Name)},
+ }
+ }
+ return nil
+}
diff --git a/pkg/apis/pipeline/v1alpha1/result_validation_test.go b/pkg/apis/pipeline/v1alpha1/result_validation_test.go
new file mode 100644
index 00000000000..81f667e963a
--- /dev/null
+++ b/pkg/apis/pipeline/v1alpha1/result_validation_test.go
@@ -0,0 +1,124 @@
+/*
+Copyright 2023 The Tekton Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+ "context"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/google/go-cmp/cmp/cmpopts"
+ "github.com/tektoncd/pipeline/test/diff"
+ "knative.dev/pkg/apis"
+)
+
+func TestResultsValidate(t *testing.T) {
+ tests := []struct {
+ name string
+ Result StepActionResult
+ }{{
+ name: "valid result type empty",
+ Result: StepActionResult{
+ Name: "MY-RESULT",
+ Description: "my great result",
+ },
+ }, {
+ name: "valid result type string",
+ Result: StepActionResult{
+ Name: "MY-RESULT",
+ Type: ResultsTypeString,
+ Description: "my great result",
+ },
+ }, {
+ name: "valid result type array",
+ Result: StepActionResult{
+ Name: "MY-RESULT",
+ Type: ResultsTypeArray,
+ Description: "my great result",
+ },
+ }, {
+ name: "valid result type object",
+ Result: StepActionResult{
+ Name: "MY-RESULT",
+ Type: ResultsTypeObject,
+ Description: "my great result",
+ Properties: map[string]PropertySpec{"hello": {Type: ParamTypeString}},
+ },
+ }}
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ ctx := context.Background()
+ if err := tt.Result.validate(ctx); err != nil {
+ t.Errorf("TaskSpec.Validate() = %v", err)
+ }
+ })
+ }
+}
+
+func TestResultsValidateError(t *testing.T) {
+ tests := []struct {
+ name string
+ Result StepActionResult
+ expectedError apis.FieldError
+ }{{
+ name: "invalid result type",
+ Result: StepActionResult{
+ Name: "MY-RESULT",
+ Type: "wrong",
+ Description: "my great result",
+ },
+ expectedError: apis.FieldError{
+ Message: `invalid value: wrong`,
+ Paths: []string{"type"},
+ Details: "type must be string",
+ },
+ }, {
+ name: "invalid object properties type",
+ Result: StepActionResult{
+ Name: "MY-RESULT",
+ Type: ResultsTypeObject,
+ Description: "my great result",
+ Properties: map[string]PropertySpec{"hello": {Type: "wrong type"}},
+ },
+ expectedError: apis.FieldError{
+ Message: "The value type specified for these keys [hello] is invalid, the type must be string",
+ Paths: []string{"MY-RESULT.properties"},
+ },
+ }, {
+ name: "invalid object properties empty",
+ Result: StepActionResult{
+ Name: "MY-RESULT",
+ Type: ResultsTypeObject,
+ Description: "my great result",
+ },
+ expectedError: apis.FieldError{
+ Message: "missing field(s)",
+ Paths: []string{"MY-RESULT.properties"},
+ },
+ }}
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ err := tt.Result.validate(context.Background())
+ if err == nil {
+ t.Fatalf("Expected an error, got nothing for %v", tt.Result)
+ }
+ if d := cmp.Diff(tt.expectedError.Error(), err.Error(), cmpopts.IgnoreUnexported(apis.FieldError{})); d != "" {
+ t.Errorf("TaskSpec.Validate() errors diff %s", diff.PrintWantGot(d))
+ }
+ })
+ }
+}
diff --git a/pkg/apis/pipeline/v1alpha1/stepaction_defaults.go b/pkg/apis/pipeline/v1alpha1/stepaction_defaults.go
index 8b30d937e9c..b0471f66488 100644
--- a/pkg/apis/pipeline/v1alpha1/stepaction_defaults.go
+++ b/pkg/apis/pipeline/v1alpha1/stepaction_defaults.go
@@ -23,4 +23,15 @@ var _ apis.Defaultable = (*StepAction)(nil)
// SetDefaults implements apis.Defaultable
func (s *StepAction) SetDefaults(ctx context.Context) {
+ s.Spec.SetDefaults(ctx)
+}
+
+// SetDefaults set any defaults for the StepAction spec
+func (ss *StepActionSpec) SetDefaults(ctx context.Context) {
+ for i := range ss.Params {
+ ss.Params[i].SetDefaults(ctx)
+ }
+ for i := range ss.Results {
+ ss.Results[i].SetDefaults(ctx)
+ }
}
diff --git a/pkg/apis/pipeline/v1alpha1/stepaction_types.go b/pkg/apis/pipeline/v1alpha1/stepaction_types.go
index 03bc5608642..27211040a0e 100644
--- a/pkg/apis/pipeline/v1alpha1/stepaction_types.go
+++ b/pkg/apis/pipeline/v1alpha1/stepaction_types.go
@@ -111,6 +111,14 @@ type StepActionSpec struct {
// If Script is not empty, the Step cannot have an Command and the Args will be passed to the Script.
// +optional
Script string `json:"script,omitempty"`
+ // Params is a list of input parameters required to run the stepAction.
+ // Params must be supplied as inputs in Steps unless they declare a defaultvalue.
+ // +optional
+ // +listType=atomic
+ Params ParamSpecs
+ // Results are values that this StepAction can output
+ // +listType=atomic
+ Results []StepActionResult
}
// StepActionObject is implemented by StepAction
diff --git a/pkg/apis/pipeline/v1alpha1/stepaction_validation.go b/pkg/apis/pipeline/v1alpha1/stepaction_validation.go
index 6209026c1b1..19de8d62fb8 100644
--- a/pkg/apis/pipeline/v1alpha1/stepaction_validation.go
+++ b/pkg/apis/pipeline/v1alpha1/stepaction_validation.go
@@ -15,15 +15,30 @@ package v1alpha1
import (
"context"
+ "fmt"
+ "regexp"
"strings"
"github.com/tektoncd/pipeline/pkg/apis/config"
"github.com/tektoncd/pipeline/pkg/apis/validate"
+ "github.com/tektoncd/pipeline/pkg/substitution"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
+ "k8s.io/apimachinery/pkg/util/sets"
"knative.dev/pkg/apis"
"knative.dev/pkg/webhook/resourcesemantics"
)
+const (
+ // stringAndArrayVariableNameFormat is the regex to validate if string/array variable name format follows the following rules.
+ // - Must only contain alphanumeric characters, hyphens (-), underscores (_), and dots (.)
+ // - Must begin with a letter or an underscore (_)
+ stringAndArrayVariableNameFormat = "^[_a-zA-Z][_a-zA-Z0-9.-]*$"
+
+ // objectVariableNameFormat is the regext used to validate object name and key names format
+ // The difference with the array or string name format is that object variable names shouldn't contain dots.
+ objectVariableNameFormat = "^[_a-zA-Z][_a-zA-Z0-9-]*$"
+)
+
var _ apis.Validatable = (*StepAction)(nil)
var _ resourcesemantics.VerbLimited = (*StepAction)(nil)
@@ -32,6 +47,9 @@ func (s *StepAction) SupportedVerbs() []admissionregistrationv1.OperationType {
return []admissionregistrationv1.OperationType{admissionregistrationv1.Create, admissionregistrationv1.Update}
}
+var stringAndArrayVariableNameFormatRegex = regexp.MustCompile(stringAndArrayVariableNameFormat)
+var objectVariableNameFormatRegex = regexp.MustCompile(objectVariableNameFormat)
+
// Validate implements apis.Validatable
func (s *StepAction) Validate(ctx context.Context) (errs *apis.FieldError) {
errs = validate.ObjectMetadata(s.GetObjectMeta()).ViaField("metadata")
@@ -58,5 +76,237 @@ func (ss *StepActionSpec) Validate(ctx context.Context) (errs *apis.FieldError)
errs = errs.Also(config.ValidateEnabledAPIFields(ctx, "windows script support", config.AlphaAPIFields).ViaField("script"))
}
}
+ errs = errs.Also(validateUsageOfDeclaredParameters(ctx, *ss))
+ errs = errs.Also(validateParameterTypes(ctx, ss.Params).ViaField("params"))
+ errs = errs.Also(validateParameterVariables(ctx, *ss, ss.Params))
+ errs = errs.Also(validateStepActionResultsVariables(ctx, *ss))
+ errs = errs.Also(validateResults(ctx, ss.Results).ViaField("results"))
+ return errs
+}
+
+// validateUsageOfDeclaredParameters validates that all parameters referenced in the Task are declared by the Task.
+func validateUsageOfDeclaredParameters(ctx context.Context, sas StepActionSpec) *apis.FieldError {
+ params := sas.Params
+ var errs *apis.FieldError
+ _, _, objectParams := params.sortByType()
+ allParameterNames := sets.NewString(params.getNames()...)
+ errs = errs.Also(validateStepActionVariables(ctx, sas, "params", allParameterNames))
+ errs = errs.Also(validateObjectUsage(ctx, sas, objectParams))
+ errs = errs.Also(validateObjectParamsHaveProperties(ctx, params))
+ return errs
+}
+
+// validateObjectParamsHaveProperties returns an error if any declared object params are missing properties
+func validateObjectParamsHaveProperties(ctx context.Context, params ParamSpecs) *apis.FieldError {
+ var errs *apis.FieldError
+ for _, p := range params {
+ if p.Type == ParamTypeObject && p.Properties == nil {
+ errs = errs.Also(apis.ErrMissingField(fmt.Sprintf("%s.properties", p.Name)))
+ }
+ }
+ return errs
+}
+
+func validateResults(ctx context.Context, results []StepActionResult) (errs *apis.FieldError) {
+ for index, result := range results {
+ errs = errs.Also(result.validate(ctx).ViaIndex(index))
+ }
+ return errs
+}
+
+// validateParameterTypes validates all the types within a slice of ParamSpecs
+func validateParameterTypes(ctx context.Context, params []ParamSpec) (errs *apis.FieldError) {
+ for _, p := range params {
+ errs = errs.Also(p.validateType(ctx))
+ }
+ return errs
+}
+
+// validateType checks that the type of a ParamSpec is allowed and its default value matches that type
+func (p ParamSpec) validateType(ctx context.Context) *apis.FieldError {
+ // Ensure param has a valid type.
+ validType := false
+ for _, allowedType := range AllParamTypes {
+ if p.Type == allowedType {
+ validType = true
+ }
+ }
+ if !validType {
+ return apis.ErrInvalidValue(p.Type, fmt.Sprintf("%s.type", p.Name))
+ }
+
+ // If a default value is provided, ensure its type matches param's declared type.
+ if (p.Default != nil) && (p.Default.Type != p.Type) {
+ return &apis.FieldError{
+ Message: fmt.Sprintf(
+ "\"%v\" type does not match default value's type: \"%v\"", p.Type, p.Default.Type),
+ Paths: []string{
+ fmt.Sprintf("%s.type", p.Name),
+ fmt.Sprintf("%s.default.type", p.Name),
+ },
+ }
+ }
+
+ // Check object type and its PropertySpec type
+ return p.validateObjectType(ctx)
+}
+
+// validateObjectType checks that object type parameter does not miss the
+// definition of `properties` section and the type of a PropertySpec is allowed.
+// (Currently, only string is allowed)
+func (p ParamSpec) validateObjectType(ctx context.Context) *apis.FieldError {
+ invalidKeys := []string{}
+ for key, propertySpec := range p.Properties {
+ if propertySpec.Type != ParamTypeString {
+ invalidKeys = append(invalidKeys, key)
+ }
+ }
+
+ if len(invalidKeys) != 0 {
+ return &apis.FieldError{
+ Message: fmt.Sprintf("The value type specified for these keys %v is invalid", invalidKeys),
+ Paths: []string{fmt.Sprintf("%s.properties", p.Name)},
+ }
+ }
+
+ return nil
+}
+
+// validateParameterVariables validates all variables within a slice of ParamSpecs against a StepAction
+func validateParameterVariables(ctx context.Context, sas StepActionSpec, params ParamSpecs) *apis.FieldError {
+ var errs *apis.FieldError
+ errs = errs.Also(params.validateNoDuplicateNames())
+ stringParams, arrayParams, objectParams := params.sortByType()
+ stringParameterNames := sets.NewString(stringParams.getNames()...)
+ arrayParameterNames := sets.NewString(arrayParams.getNames()...)
+ errs = errs.Also(validateNameFormat(stringParameterNames.Insert(arrayParameterNames.List()...), objectParams))
+ return errs.Also(validateStepActionArrayUsage(sas, "params", arrayParameterNames))
+}
+
+// validateObjectUsage validates the usage of individual attributes of an object param and the usage of the entire object
+func validateObjectUsage(ctx context.Context, sas StepActionSpec, params ParamSpecs) (errs *apis.FieldError) {
+ objectParameterNames := sets.NewString()
+ for _, p := range params {
+ // collect all names of object type params
+ objectParameterNames.Insert(p.Name)
+
+ // collect all keys for this object param
+ objectKeys := sets.NewString()
+ for key := range p.Properties {
+ objectKeys.Insert(key)
+ }
+
+ // check if the object's key names are referenced correctly i.e. param.objectParam.key1
+ errs = errs.Also(validateStepActionVariables(ctx, sas, fmt.Sprintf("params\\.%s", p.Name), objectKeys))
+ }
+
+ return errs.Also(validateStepActionObjectUsageAsWhole(sas, "params", objectParameterNames))
+}
+
+// validateStepActionObjectUsageAsWhole returns an error if the StepAction contains references to the entire input object params in fields where these references are prohibited
+func validateStepActionObjectUsageAsWhole(sas StepActionSpec, prefix string, vars sets.String) *apis.FieldError {
+ errs := substitution.ValidateNoReferencesToEntireProhibitedVariables(sas.Image, prefix, vars).ViaField("image")
+ errs = errs.Also(substitution.ValidateNoReferencesToEntireProhibitedVariables(sas.Script, prefix, vars).ViaField("script"))
+ for i, cmd := range sas.Command {
+ errs = errs.Also(substitution.ValidateNoReferencesToEntireProhibitedVariables(cmd, prefix, vars).ViaFieldIndex("command", i))
+ }
+ for i, arg := range sas.Args {
+ errs = errs.Also(substitution.ValidateNoReferencesToEntireProhibitedVariables(arg, prefix, vars).ViaFieldIndex("args", i))
+ }
+ for _, env := range sas.Env {
+ errs = errs.Also(substitution.ValidateNoReferencesToEntireProhibitedVariables(env.Value, prefix, vars).ViaFieldKey("env", env.Name))
+ }
+ return errs
+}
+
+// validateStepActionArrayUsage returns an error if the Step contains references to the input array params in fields where these references are prohibited
+func validateStepActionArrayUsage(sas StepActionSpec, prefix string, arrayParamNames sets.String) *apis.FieldError {
+ errs := substitution.ValidateNoReferencesToProhibitedVariables(sas.Image, prefix, arrayParamNames).ViaField("image")
+ errs = errs.Also(substitution.ValidateNoReferencesToProhibitedVariables(sas.Script, prefix, arrayParamNames).ViaField("script"))
+ for i, cmd := range sas.Command {
+ errs = errs.Also(substitution.ValidateVariableReferenceIsIsolated(cmd, prefix, arrayParamNames).ViaFieldIndex("command", i))
+ }
+ for i, arg := range sas.Args {
+ errs = errs.Also(substitution.ValidateVariableReferenceIsIsolated(arg, prefix, arrayParamNames).ViaFieldIndex("args", i))
+ }
+ for _, env := range sas.Env {
+ errs = errs.Also(substitution.ValidateNoReferencesToProhibitedVariables(env.Value, prefix, arrayParamNames).ViaFieldKey("env", env.Name))
+ }
+ return errs
+}
+
+// validateNameFormat validates that the name format of all param types follows the rules
+func validateNameFormat(stringAndArrayParams sets.String, objectParams []ParamSpec) (errs *apis.FieldError) {
+ // checking string or array name format
+ // ----
+ invalidStringAndArrayNames := []string{}
+ // Converting to sorted list here rather than just looping map keys
+ // because we want the order of items in vars to be deterministic for purpose of unit testing
+ for _, name := range stringAndArrayParams.List() {
+ if !stringAndArrayVariableNameFormatRegex.MatchString(name) {
+ invalidStringAndArrayNames = append(invalidStringAndArrayNames, name)
+ }
+ }
+
+ if len(invalidStringAndArrayNames) != 0 {
+ errs = errs.Also(&apis.FieldError{
+ Message: fmt.Sprintf("The format of following array and string variable names is invalid: %s", invalidStringAndArrayNames),
+ Paths: []string{"params"},
+ Details: "String/Array Names: \nMust only contain alphanumeric characters, hyphens (-), underscores (_), and dots (.)\nMust begin with a letter or an underscore (_)",
+ })
+ }
+
+ // checking object name and key name format
+ // -----
+ invalidObjectNames := map[string][]string{}
+ for _, obj := range objectParams {
+ // check object param name
+ if !objectVariableNameFormatRegex.MatchString(obj.Name) {
+ invalidObjectNames[obj.Name] = []string{}
+ }
+
+ // check key names
+ for k := range obj.Properties {
+ if !objectVariableNameFormatRegex.MatchString(k) {
+ invalidObjectNames[obj.Name] = append(invalidObjectNames[obj.Name], k)
+ }
+ }
+ }
+
+ if len(invalidObjectNames) != 0 {
+ errs = errs.Also(&apis.FieldError{
+ Message: fmt.Sprintf("Object param name and key name format is invalid: %s", invalidObjectNames),
+ Paths: []string{"params"},
+ Details: "Object Names: \nMust only contain alphanumeric characters, hyphens (-), underscores (_) \nMust begin with a letter or an underscore (_)",
+ })
+ }
+
+ return errs
+}
+
+// validateStepActionVariables returns an error if the StepAction contains references to any unknown variables
+func validateStepActionVariables(ctx context.Context, sas StepActionSpec, prefix string, vars sets.String) *apis.FieldError {
+ errs := substitution.ValidateNoReferencesToUnknownVariables(sas.Image, prefix, vars).ViaField("image")
+ errs = errs.Also(substitution.ValidateNoReferencesToUnknownVariables(sas.Script, prefix, vars).ViaField("script"))
+ for i, cmd := range sas.Command {
+ errs = errs.Also(substitution.ValidateNoReferencesToUnknownVariables(cmd, prefix, vars).ViaFieldIndex("command", i))
+ }
+ for i, arg := range sas.Args {
+ errs = errs.Also(substitution.ValidateNoReferencesToUnknownVariables(arg, prefix, vars).ViaFieldIndex("args", i))
+ }
+ for _, env := range sas.Env {
+ errs = errs.Also(substitution.ValidateNoReferencesToUnknownVariables(env.Value, prefix, vars).ViaFieldKey("env", env.Name))
+ }
+ return errs
+}
+
+// validateStepActionResultsVariables validates if the results referenced in step script are defined in task results
+func validateStepActionResultsVariables(ctx context.Context, sas StepActionSpec) (errs *apis.FieldError) {
+ results := sas.Results
+ resultsNames := sets.NewString()
+ for _, r := range results {
+ resultsNames.Insert(r.Name)
+ }
+ errs = errs.Also(substitution.ValidateNoReferencesToUnknownVariables(sas.Script, "results", resultsNames).ViaField("script"))
return errs
}
diff --git a/pkg/apis/pipeline/v1alpha1/stepaction_validation_test.go b/pkg/apis/pipeline/v1alpha1/stepaction_validation_test.go
index 72871f81555..fa4e7f147d9 100644
--- a/pkg/apis/pipeline/v1alpha1/stepaction_validation_test.go
+++ b/pkg/apis/pipeline/v1alpha1/stepaction_validation_test.go
@@ -15,6 +15,7 @@ package v1alpha1_test
import (
"context"
+ "fmt"
"testing"
"github.com/google/go-cmp/cmp"
@@ -64,6 +65,8 @@ func TestStepActionSpecValidate(t *testing.T) {
Args []string
Script string
Env []corev1.EnvVar
+ Params []v1alpha1.ParamSpec
+ Results []v1alpha1.StepActionResult
}
tests := []struct {
name string
@@ -91,6 +94,161 @@ func TestStepActionSpecValidate(t *testing.T) {
Value: "/tekton/home",
}},
},
+ }, {
+ name: "valid params type explicit",
+ fields: fields{
+ Image: "myimage",
+ Params: []v1alpha1.ParamSpec{{
+ Name: "stringParam",
+ Type: v1alpha1.ParamTypeString,
+ Description: "param",
+ Default: v1alpha1.NewStructuredValues("default"),
+ }, {
+ Name: "objectParam",
+ Type: v1alpha1.ParamTypeObject,
+ Description: "param",
+ Properties: map[string]v1alpha1.PropertySpec{
+ "key1": {},
+ "key2": {},
+ },
+ Default: v1alpha1.NewObject(map[string]string{
+ "key1": "var1",
+ "key2": "var2",
+ }),
+ }, {
+ Name: "objectParamWithoutDefault",
+ Type: v1alpha1.ParamTypeObject,
+ Description: "param",
+ Properties: map[string]v1alpha1.PropertySpec{
+ "key1": {},
+ "key2": {},
+ },
+ }, {
+ Name: "objectParamWithDefaultPartialKeys",
+ Type: v1alpha1.ParamTypeObject,
+ Description: "param",
+ Properties: map[string]v1alpha1.PropertySpec{
+ "key1": {},
+ "key2": {},
+ },
+ Default: v1alpha1.NewObject(map[string]string{
+ "key1": "default",
+ }),
+ }},
+ },
+ }, {
+ name: "valid string param usage",
+ fields: fields{
+ Image: "url",
+ Params: []v1alpha1.ParamSpec{{
+ Name: "baz",
+ }, {
+ Name: "foo-is-baz",
+ }},
+ Args: []string{"--flag=$(params.baz) && $(params.foo-is-baz)"},
+ },
+ }, {
+ name: "valid array param usage",
+ fields: fields{
+ Image: "url",
+ Params: []v1alpha1.ParamSpec{{
+ Name: "baz",
+ Type: v1alpha1.ParamTypeArray,
+ }, {
+ Name: "foo-is-baz",
+ Type: v1alpha1.ParamTypeArray,
+ }},
+ Command: []string{"$(params.foo-is-baz)"},
+ Args: []string{"$(params.baz)", "middle string", "$(params.foo-is-baz)"},
+ },
+ }, {
+ name: "valid object param usage",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "gitrepo",
+ Type: v1alpha1.ParamTypeObject,
+ Properties: map[string]v1alpha1.PropertySpec{
+ "url": {},
+ "commit": {},
+ },
+ }},
+ Image: "some-git-image",
+ Args: []string{"-url=$(params.gitrepo.url)", "-commit=$(params.gitrepo.commit)"},
+ },
+ }, {
+ name: "valid star array usage",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "baz",
+ Type: v1alpha1.ParamTypeArray,
+ }, {
+ Name: "foo-is-baz",
+ Type: v1alpha1.ParamTypeArray,
+ }},
+ Image: "myimage",
+ Command: []string{"$(params.foo-is-baz)"},
+ Args: []string{"$(params.baz[*])", "middle string", "$(params.foo-is-baz[*])"},
+ },
+ }, {
+ name: "valid step with parameterized script",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "baz",
+ }, {
+ Name: "foo-is-baz",
+ }},
+ Image: "my-image",
+ Script: `
+ #!/usr/bin/env bash
+ hello $(params.baz)`,
+ },
+ }, {
+ name: "valid result",
+ fields: fields{
+ Image: "my-image",
+ Args: []string{"arg"},
+ Results: []v1alpha1.StepActionResult{{
+ Name: "MY-RESULT",
+ Description: "my great result",
+ }},
+ },
+ }, {
+ name: "valid result type string",
+ fields: fields{
+ Image: "my-image",
+ Args: []string{"arg"},
+ Results: []v1alpha1.StepActionResult{{
+ Name: "MY-RESULT",
+ Type: "string",
+ Description: "my great result",
+ }},
+ },
+ }, {
+ name: "valid result type array",
+ fields: fields{
+ Image: "my-image",
+ Args: []string{"arg"},
+ Results: []v1alpha1.StepActionResult{{
+ Name: "MY-RESULT",
+ Type: v1alpha1.ResultsTypeArray,
+ Description: "my great result",
+ }},
+ },
+ }, {
+ name: "valid result type object",
+ fields: fields{
+ Image: "my-image",
+ Args: []string{"arg"},
+ Results: []v1alpha1.StepActionResult{{
+ Name: "MY-RESULT",
+ Type: v1alpha1.ResultsTypeObject,
+ Description: "my great result",
+ Properties: map[string]v1alpha1.PropertySpec{
+ "url": {"string"},
+ "commit": {"string"},
+ },
+ }},
+ },
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -100,8 +258,12 @@ func TestStepActionSpecValidate(t *testing.T) {
Args: tt.fields.Args,
Script: tt.fields.Script,
Env: tt.fields.Env,
+ Params: tt.fields.Params,
+ Results: tt.fields.Results,
}
- if err := sa.Validate(context.Background()); err != nil {
+ ctx := context.Background()
+ sa.SetDefaults(ctx)
+ if err := sa.Validate(ctx); err != nil {
t.Errorf("StepActionSpec.Validate() = %v", err)
}
})
@@ -115,6 +277,8 @@ func TestStepActionValidateError(t *testing.T) {
Args []string
Script string
Env []corev1.EnvVar
+ Params []v1alpha1.ParamSpec
+ Results []v1alpha1.StepActionResult
}
tests := []struct {
name string
@@ -123,12 +287,117 @@ func TestStepActionValidateError(t *testing.T) {
}{{
name: "inexistent image field",
fields: fields{
- Args: []string{"--flag=$(params.inexistent)"},
+ Args: []string{"flag"},
},
expectedError: apis.FieldError{
Message: `missing field(s)`,
Paths: []string{"spec.Image"},
},
+ }, {
+ name: "object used in a string field",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "gitrepo",
+ Type: v1alpha1.ParamTypeObject,
+ Properties: map[string]v1alpha1.PropertySpec{
+ "url": {},
+ "commit": {},
+ },
+ }},
+ Image: "$(params.gitrepo)",
+ Args: []string{"echo"},
+ },
+ expectedError: apis.FieldError{
+ Message: `variable type invalid in "$(params.gitrepo)"`,
+ Paths: []string{"spec.image"},
+ },
+ }, {
+ name: "object star used in a string field",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "gitrepo",
+ Type: v1alpha1.ParamTypeObject,
+ Properties: map[string]v1alpha1.PropertySpec{
+ "url": {},
+ "commit": {},
+ },
+ }},
+ Image: "$(params.gitrepo[*])",
+ Args: []string{"echo"},
+ },
+ expectedError: apis.FieldError{
+ Message: `variable type invalid in "$(params.gitrepo[*])"`,
+ Paths: []string{"spec.image"},
+ },
+ }, {
+ name: "object used in a field that can accept array type",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "gitrepo",
+ Type: v1alpha1.ParamTypeObject,
+ Properties: map[string]v1alpha1.PropertySpec{
+ "url": {},
+ "commit": {},
+ },
+ }},
+ Image: "myimage",
+ Args: []string{"$(params.gitrepo)"},
+ },
+ expectedError: apis.FieldError{
+ Message: `variable type invalid in "$(params.gitrepo)"`,
+ Paths: []string{"spec.args[0]"},
+ },
+ }, {
+ name: "object star used in a field that can accept array type",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "gitrepo",
+ Type: v1alpha1.ParamTypeObject,
+ Properties: map[string]v1alpha1.PropertySpec{
+ "url": {},
+ "commit": {},
+ },
+ }},
+ Image: "some-git-image",
+ Args: []string{"$(params.gitrepo[*])"},
+ },
+ expectedError: apis.FieldError{
+ Message: `variable type invalid in "$(params.gitrepo[*])"`,
+ Paths: []string{"spec.args[0]"},
+ },
+ }, {
+ name: "non-existent individual key of an object param is used in task step",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "gitrepo",
+ Type: v1alpha1.ParamTypeObject,
+ Properties: map[string]v1alpha1.PropertySpec{
+ "url": {},
+ "commit": {},
+ },
+ }},
+ Image: "some-git-image",
+ Args: []string{"$(params.gitrepo.non-exist-key)"},
+ },
+ expectedError: apis.FieldError{
+ Message: `non-existent variable in "$(params.gitrepo.non-exist-key)"`,
+ Paths: []string{"spec.args[0]"},
+ },
+ }, {
+ name: "Inexistent param variable with existing",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "foo",
+ Description: "param",
+ Default: v1alpha1.NewStructuredValues("default"),
+ }},
+ Image: "myimage",
+ Args: []string{"$(params.foo) && $(params.inexistent)"},
+ },
+ expectedError: apis.FieldError{
+ Message: `non-existent variable in "$(params.foo) && $(params.inexistent)"`,
+ Paths: []string{"spec.args[0]"},
+ },
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -140,9 +409,12 @@ func TestStepActionValidateError(t *testing.T) {
Args: tt.fields.Args,
Script: tt.fields.Script,
Env: tt.fields.Env,
+ Params: tt.fields.Params,
+ Results: tt.fields.Results,
},
}
ctx := context.Background()
+ sa.SetDefaults(ctx)
err := sa.Validate(ctx)
if err == nil {
t.Fatalf("Expected an error, got nothing for %v", sa)
@@ -161,6 +433,8 @@ func TestStepActionSpecValidateError(t *testing.T) {
Args []string
Script string
Env []corev1.EnvVar
+ Params []v1alpha1.ParamSpec
+ Results []v1alpha1.StepActionResult
}
tests := []struct {
name string
@@ -169,7 +443,7 @@ func TestStepActionSpecValidateError(t *testing.T) {
}{{
name: "inexistent image field",
fields: fields{
- Args: []string{"--flag=$(params.inexistent)"},
+ Args: []string{"flag"},
},
expectedError: apis.FieldError{
Message: `missing field(s)`,
@@ -196,6 +470,329 @@ func TestStepActionSpecValidateError(t *testing.T) {
Message: `windows script support requires "enable-api-fields" feature gate to be "alpha" but it is "beta"`,
Paths: []string{},
},
+ }, {
+ name: "step script refers to nonexistent result",
+ fields: fields{
+ Image: "my-image",
+ Script: `
+ #!/usr/bin/env bash
+ date | tee $(results.non-exist.path)`,
+ Results: []v1alpha1.StepActionResult{{Name: "a-result"}},
+ },
+ expectedError: apis.FieldError{
+ Message: `non-existent variable in "\n\t\t\t#!/usr/bin/env bash\n\t\t\tdate | tee $(results.non-exist.path)"`,
+ Paths: []string{"script"},
+ },
+ }, {
+ name: "invalid param name format",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "_validparam1",
+ Description: "valid param name format",
+ }, {
+ Name: "valid_param2",
+ Description: "valid param name format",
+ }, {
+ Name: "",
+ Description: "invalid param name format",
+ }, {
+ Name: "a^b",
+ Description: "invalid param name format",
+ }, {
+ Name: "0ab",
+ Description: "invalid param name format",
+ }, {
+ Name: "f oo",
+ Description: "invalid param name format",
+ }},
+ Image: "myImage",
+ },
+ expectedError: apis.FieldError{
+ Message: fmt.Sprintf("The format of following array and string variable names is invalid: %s", []string{"", "0ab", "a^b", "f oo"}),
+ Paths: []string{"params"},
+ Details: "String/Array Names: \nMust only contain alphanumeric characters, hyphens (-), underscores (_), and dots (.)\nMust begin with a letter or an underscore (_)",
+ },
+ }, {
+ name: "invalid object param format - object param name and key name shouldn't contain dots.",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "invalid.name1",
+ Description: "object param name contains dots",
+ Properties: map[string]v1alpha1.PropertySpec{
+ "invalid.key1": {},
+ "mykey2": {},
+ },
+ }},
+ Image: "myImage",
+ },
+ expectedError: apis.FieldError{
+ Message: fmt.Sprintf("Object param name and key name format is invalid: %v", map[string][]string{
+ "invalid.name1": {"invalid.key1"},
+ }),
+ Paths: []string{"params"},
+ Details: "Object Names: \nMust only contain alphanumeric characters, hyphens (-), underscores (_) \nMust begin with a letter or an underscore (_)",
+ },
+ }, {
+ name: "duplicated param names",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "foo",
+ Type: v1alpha1.ParamTypeString,
+ Description: "parameter",
+ Default: v1alpha1.NewStructuredValues("value1"),
+ }, {
+ Name: "foo",
+ Type: v1alpha1.ParamTypeString,
+ Description: "parameter",
+ Default: v1alpha1.NewStructuredValues("value2"),
+ }},
+ Image: "myImage",
+ },
+ expectedError: apis.FieldError{
+ Message: `parameter appears more than once`,
+ Paths: []string{"params[foo]"},
+ },
+ }, {
+ name: "invalid param type",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "validparam",
+ Type: v1alpha1.ParamTypeString,
+ Description: "parameter",
+ Default: v1alpha1.NewStructuredValues("default"),
+ }, {
+ Name: "param-with-invalid-type",
+ Type: "invalidtype",
+ Description: "invalidtypedesc",
+ Default: v1alpha1.NewStructuredValues("default"),
+ }},
+ Image: "myImage",
+ },
+ expectedError: apis.FieldError{
+ Message: `invalid value: invalidtype`,
+ Paths: []string{"params.param-with-invalid-type.type"},
+ },
+ }, {
+ name: "param mismatching default/type 1",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "task",
+ Type: v1alpha1.ParamTypeArray,
+ Description: "param",
+ Default: v1alpha1.NewStructuredValues("default"),
+ }},
+ Image: "myImage",
+ },
+ expectedError: apis.FieldError{
+ Message: `"array" type does not match default value's type: "string"`,
+ Paths: []string{"params.task.type", "params.task.default.type"},
+ },
+ }, {
+ name: "param mismatching default/type 2",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "task",
+ Type: v1alpha1.ParamTypeString,
+ Description: "param",
+ Default: v1alpha1.NewStructuredValues("default", "array"),
+ }},
+ Image: "myImage",
+ },
+ expectedError: apis.FieldError{
+ Message: `"string" type does not match default value's type: "array"`,
+ Paths: []string{"params.task.type", "params.task.default.type"},
+ },
+ }, {
+ name: "param mismatching default/type 3",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "task",
+ Type: v1alpha1.ParamTypeArray,
+ Description: "param",
+ Default: v1alpha1.NewObject(map[string]string{
+ "key1": "var1",
+ "key2": "var2",
+ }),
+ }},
+ Image: "myImage",
+ },
+ expectedError: apis.FieldError{
+ Message: `"array" type does not match default value's type: "object"`,
+ Paths: []string{"params.task.type", "params.task.default.type"},
+ },
+ }, {
+ name: "param mismatching default/type 4",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "task",
+ Type: v1alpha1.ParamTypeObject,
+ Description: "param",
+ Properties: map[string]v1alpha1.PropertySpec{"key1": {}},
+ Default: v1alpha1.NewStructuredValues("var"),
+ }},
+ Image: "myImage",
+ },
+ expectedError: apis.FieldError{
+ Message: `"object" type does not match default value's type: "string"`,
+ Paths: []string{"params.task.type", "params.task.default.type"},
+ },
+ }, {
+ name: "PropertySpec type is set with unsupported type",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "task",
+ Type: v1alpha1.ParamTypeObject,
+ Description: "param",
+ Properties: map[string]v1alpha1.PropertySpec{
+ "key1": {Type: "number"},
+ "key2": {Type: "string"},
+ },
+ }},
+ Image: "myImage",
+ },
+ expectedError: apis.FieldError{
+ Message: fmt.Sprintf("The value type specified for these keys %v is invalid", []string{"key1"}),
+ Paths: []string{"params.task.properties"},
+ },
+ }, {
+ name: "Properties is missing",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "task",
+ Type: v1alpha1.ParamTypeObject,
+ Description: "param",
+ }},
+ Image: "myImage",
+ },
+ expectedError: apis.FieldError{
+ Message: fmt.Sprintf("missing field(s)"),
+ Paths: []string{"task.properties"},
+ },
+ }, {
+ name: "array used in unaccepted field",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "baz",
+ Type: v1alpha1.ParamTypeArray,
+ }, {
+ Name: "foo-is-baz",
+ Type: v1alpha1.ParamTypeArray,
+ }},
+ Image: "$(params.baz)",
+ Command: []string{"$(params.foo-is-baz)"},
+ Args: []string{"$(params.baz)", "middle string", "url"},
+ },
+ expectedError: apis.FieldError{
+ Message: `variable type invalid in "$(params.baz)"`,
+ Paths: []string{"image"},
+ },
+ }, {
+ name: "array star used in unaccepted field",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "baz",
+ Type: v1alpha1.ParamTypeArray,
+ }, {
+ Name: "foo-is-baz",
+ Type: v1alpha1.ParamTypeArray,
+ }},
+ Image: "$(params.baz[*])",
+ Command: []string{"$(params.foo-is-baz)"},
+ Args: []string{"$(params.baz)", "middle string", "url"},
+ },
+ expectedError: apis.FieldError{
+ Message: `variable type invalid in "$(params.baz[*])"`,
+ Paths: []string{"image"},
+ },
+ }, {
+ name: "array star used illegaly in script field",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "baz",
+ Type: v1alpha1.ParamTypeArray,
+ }, {
+ Name: "foo-is-baz",
+ Type: v1alpha1.ParamTypeArray,
+ }},
+ Script: "$(params.baz[*])",
+ Image: "my-image",
+ },
+ expectedError: apis.FieldError{
+ Message: `variable type invalid in "$(params.baz[*])"`,
+ Paths: []string{"script"},
+ },
+ }, {
+ name: "array not properly isolated",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "baz",
+ Type: v1alpha1.ParamTypeArray,
+ }, {
+ Name: "foo-is-baz",
+ Type: v1alpha1.ParamTypeArray,
+ }},
+ Image: "someimage",
+ Command: []string{"$(params.foo-is-baz)"},
+ Args: []string{"not isolated: $(params.baz)", "middle string", "url"},
+ },
+ expectedError: apis.FieldError{
+ Message: `variable is not properly isolated in "not isolated: $(params.baz)"`,
+ Paths: []string{"args[0]"},
+ },
+ }, {
+ name: "array star not properly isolated",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "baz",
+ Type: v1alpha1.ParamTypeArray,
+ }, {
+ Name: "foo-is-baz",
+ Type: v1alpha1.ParamTypeArray,
+ }},
+ Image: "someimage",
+ Command: []string{"$(params.foo-is-baz)"},
+ Args: []string{"not isolated: $(params.baz[*])", "middle string", "url"},
+ },
+ expectedError: apis.FieldError{
+ Message: `variable is not properly isolated in "not isolated: $(params.baz[*])"`,
+ Paths: []string{"args[0]"},
+ },
+ }, {
+ name: "inferred array not properly isolated",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "baz",
+ Default: v1alpha1.NewStructuredValues("implied", "array", "type"),
+ }, {
+ Name: "foo-is-baz",
+ Default: v1alpha1.NewStructuredValues("implied", "array", "type"),
+ }},
+ Image: "someimage",
+ Command: []string{"$(params.foo-is-baz)"},
+ Args: []string{"not isolated: $(params.baz)", "middle string", "url"},
+ },
+ expectedError: apis.FieldError{
+ Message: `variable is not properly isolated in "not isolated: $(params.baz)"`,
+ Paths: []string{"args[0]"},
+ },
+ }, {
+ name: "inferred array star not properly isolated",
+ fields: fields{
+ Params: []v1alpha1.ParamSpec{{
+ Name: "baz",
+ Default: v1alpha1.NewStructuredValues("implied", "array", "type"),
+ }, {
+ Name: "foo-is-baz",
+ Default: v1alpha1.NewStructuredValues("implied", "array", "type"),
+ }},
+ Image: "someimage",
+ Command: []string{"$(params.foo-is-baz)"},
+ Args: []string{"not isolated: $(params.baz[*])", "middle string", "url"},
+ },
+ expectedError: apis.FieldError{
+ Message: `variable is not properly isolated in "not isolated: $(params.baz[*])"`,
+ Paths: []string{"args[0]"},
+ },
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -205,8 +802,11 @@ func TestStepActionSpecValidateError(t *testing.T) {
Args: tt.fields.Args,
Script: tt.fields.Script,
Env: tt.fields.Env,
+ Params: tt.fields.Params,
+ Results: tt.fields.Results,
}
ctx := context.Background()
+ sa.SetDefaults(ctx)
err := sa.Validate(ctx)
if err == nil {
t.Fatalf("Expected an error, got nothing for %v", sa)
diff --git a/pkg/apis/pipeline/v1alpha1/swagger.json b/pkg/apis/pipeline/v1alpha1/swagger.json
index 46e98e44f28..3797c5e7042 100644
--- a/pkg/apis/pipeline/v1alpha1/swagger.json
+++ b/pkg/apis/pipeline/v1alpha1/swagger.json
@@ -212,6 +212,85 @@
}
}
},
+ "v1alpha1.ParamSpec": {
+ "description": "ParamSpec defines arbitrary parameters needed beyond typed inputs (such as resources). Parameter values are provided by users as inputs on a TaskRun or PipelineRun.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "default": {
+ "description": "Default is the value a parameter takes if no input value is supplied. If default is set, a Task may be executed without a supplied value for the parameter.",
+ "$ref": "#/definitions/v1alpha1.ParamValue"
+ },
+ "description": {
+ "description": "Description is a user-facing description of the parameter that may be used to populate a UI.",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name declares the name by which a parameter is referenced.",
+ "type": "string",
+ "default": ""
+ },
+ "properties": {
+ "description": "Properties is the JSON Schema properties to support key-value pairs parameter.",
+ "type": "object",
+ "additionalProperties": {
+ "default": {},
+ "$ref": "#/definitions/v1alpha1.PropertySpec"
+ }
+ },
+ "type": {
+ "description": "Type is the user-specified type of the parameter. The possible types are currently \"string\", \"array\" and \"object\", and \"string\" is the default.",
+ "type": "string"
+ }
+ }
+ },
+ "v1alpha1.ParamValue": {
+ "description": "ResultValue is a type alias of ParamValue",
+ "type": "object",
+ "required": [
+ "Type",
+ "StringVal",
+ "ArrayVal",
+ "ObjectVal"
+ ],
+ "properties": {
+ "ArrayVal": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "default": ""
+ },
+ "x-kubernetes-list-type": "atomic"
+ },
+ "ObjectVal": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string",
+ "default": ""
+ }
+ },
+ "StringVal": {
+ "description": "Represents the stored type of ParamValues.",
+ "type": "string",
+ "default": ""
+ },
+ "Type": {
+ "type": "string",
+ "default": ""
+ }
+ }
+ },
+ "v1alpha1.PropertySpec": {
+ "description": "PropertySpec defines the struct for object keys",
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string"
+ }
+ }
+ },
"v1alpha1.ResourcePattern": {
"description": "ResourcePattern defines the pattern of the resource source",
"type": "object",
@@ -384,10 +463,61 @@
}
}
},
+ "v1alpha1.StepActionResult": {
+ "description": "StepActionResult used to describe the results of a task",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "description": {
+ "description": "Description is a human-readable description of the result",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name the given name",
+ "type": "string",
+ "default": ""
+ },
+ "properties": {
+ "description": "Properties is the JSON Schema properties to support key-value pairs results.",
+ "type": "object",
+ "additionalProperties": {
+ "default": {},
+ "$ref": "#/definitions/v1alpha1.PropertySpec"
+ }
+ },
+ "type": {
+ "description": "Type is the user-specified type of the result. The possible type is currently \"string\" and will support \"array\" in following work.",
+ "type": "string"
+ }
+ }
+ },
"v1alpha1.StepActionSpec": {
"description": "StepActionSpec contains the actionable components of a step.",
"type": "object",
+ "required": [
+ "Results"
+ ],
"properties": {
+ "Params": {
+ "description": "Params is a list of input parameters required to run the stepAction. Params must be supplied as inputs in Steps unless they declare a defaultvalue.",
+ "type": "array",
+ "items": {
+ "default": {},
+ "$ref": "#/definitions/v1alpha1.ParamSpec"
+ },
+ "x-kubernetes-list-type": "atomic"
+ },
+ "Results": {
+ "description": "Results are values that this StepAction can output",
+ "type": "array",
+ "items": {
+ "default": {},
+ "$ref": "#/definitions/v1alpha1.StepActionResult"
+ },
+ "x-kubernetes-list-type": "atomic"
+ },
"args": {
"description": "Arguments to the entrypoint. The image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell",
"type": "array",
diff --git a/pkg/apis/pipeline/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/pipeline/v1alpha1/zz_generated.deepcopy.go
index 494ace1360c..aba7f58ae09 100644
--- a/pkg/apis/pipeline/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/pipeline/v1alpha1/zz_generated.deepcopy.go
@@ -90,6 +90,100 @@ func (in *KeyRef) DeepCopy() *KeyRef {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ParamSpec) DeepCopyInto(out *ParamSpec) {
+ *out = *in
+ if in.Properties != nil {
+ in, out := &in.Properties, &out.Properties
+ *out = make(map[string]PropertySpec, len(*in))
+ for key, val := range *in {
+ (*out)[key] = val
+ }
+ }
+ if in.Default != nil {
+ in, out := &in.Default, &out.Default
+ *out = new(ParamValue)
+ (*in).DeepCopyInto(*out)
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ParamSpec.
+func (in *ParamSpec) DeepCopy() *ParamSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(ParamSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in ParamSpecs) DeepCopyInto(out *ParamSpecs) {
+ {
+ in := &in
+ *out = make(ParamSpecs, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ return
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ParamSpecs.
+func (in ParamSpecs) DeepCopy() ParamSpecs {
+ if in == nil {
+ return nil
+ }
+ out := new(ParamSpecs)
+ in.DeepCopyInto(out)
+ return *out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ParamValue) DeepCopyInto(out *ParamValue) {
+ *out = *in
+ if in.ArrayVal != nil {
+ in, out := &in.ArrayVal, &out.ArrayVal
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+ if in.ObjectVal != nil {
+ in, out := &in.ObjectVal, &out.ObjectVal
+ *out = make(map[string]string, len(*in))
+ for key, val := range *in {
+ (*out)[key] = val
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ParamValue.
+func (in *ParamValue) DeepCopy() *ParamValue {
+ if in == nil {
+ return nil
+ }
+ out := new(ParamValue)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PropertySpec) DeepCopyInto(out *PropertySpec) {
+ *out = *in
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PropertySpec.
+func (in *PropertySpec) DeepCopy() *PropertySpec {
+ if in == nil {
+ return nil
+ }
+ out := new(PropertySpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourcePattern) DeepCopyInto(out *ResourcePattern) {
*out = *in
@@ -277,6 +371,29 @@ func (in *StepActionList) DeepCopyObject() runtime.Object {
return nil
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *StepActionResult) DeepCopyInto(out *StepActionResult) {
+ *out = *in
+ if in.Properties != nil {
+ in, out := &in.Properties, &out.Properties
+ *out = make(map[string]PropertySpec, len(*in))
+ for key, val := range *in {
+ (*out)[key] = val
+ }
+ }
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StepActionResult.
+func (in *StepActionResult) DeepCopy() *StepActionResult {
+ if in == nil {
+ return nil
+ }
+ out := new(StepActionResult)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *StepActionSpec) DeepCopyInto(out *StepActionSpec) {
*out = *in
@@ -297,6 +414,20 @@ func (in *StepActionSpec) DeepCopyInto(out *StepActionSpec) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
+ if in.Params != nil {
+ in, out := &in.Params, &out.Params
+ *out = make(ParamSpecs, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+ if in.Results != nil {
+ in, out := &in.Results, &out.Results
+ *out = make([]StepActionResult, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
return
}