Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand parameters to support the Array type. #1080

Merged
merged 4 commits into from
Jul 30, 2019

Conversation

EliZucker
Copy link
Member

@EliZucker EliZucker commented Jul 16, 2019

Changes

Introduction:

This change was a lot harder than I initially thought it would be! There were a lot of unexpected design considerations and edge cases to catch.

I'm not sure how to logically divide this PR, since all of the test refactoring depends on the new ArrayOrString type so can't be in a separate commit.

Please let me know if you have any suggestions, since this is my first PR on Tekton of this size!

This PR is technically incomplete because I want to make the user documentation as clear as possible. However, the actual code for funtionality/tests is in a separate commit and ready for review. PR is completely ready for review. Thanks!

Commit message:

Expand parameters to support the Array type.

Instead of just strings, add support for users to supply parameters in the form of an array. Also include appropriate type validation and defaulting to support backwards-compatibility. Fixes #207.

The size of a user-supplied array is not specified anywhere in the corresponding ParamSpec, which is useful for defining tasks that require a dynamic number of strings used as distinct elements in an array. For instance, one might use an array parameter in an image-building task that feeds in a dynamic number of flags via the args field for a particular step.

The implementation is defined such that an Array parameter can only be used if the replacement string is completely isolated and within a field that is an array of strings (currently eligible fields are command and args). For instance, the webhook will prevent an array parameter from being used in the image field. Similarly, an array parameter used in a composite string, like "value=${array}", is invalid and will be caught. The decision was made to completely prevent any use of array parameters as strings because it clutters Tekton with unnecessary functionality and is naturally extensible beyond the intended scope. For instance, if the behavior was defined such that array parameters used in the context of strings were automatically converted by separating them with a space, then that relatively arbitrary implementation could be questioned and eventually lead to needing to specify a custom delimiter (like commas), and so on.

Another implementation detail to note is that the ArrayOrString type was necessary, in combination with implementing json.Marshaller, to support backwards-compatibility with strings. This is based off IntOrString from kubernetes/apimachinery, and allows the value-type to be immediately parsed and decided through the webhook.

Lastly, a possibly unintuitive design decision is that if an EMPTY array parameter properly replaces an array element, then that string in the original array will be completely removed.

Example YAML (not in commit message):

Feel free to deploy pipelines with this PR's code changes and try out array parameters for yourself! Here is an example YAML that should run:

apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
  name: build-push
  namespace: default
spec:
  inputs:
    params:
      - name: pathToContext
        type: array
  steps:
    - name: printparam
      image: ubuntu
      command: [echo]
      args: ["${inputs.params.pathToContext}"]
---
apiVersion: tekton.dev/v1alpha1
kind: Pipeline
metadata:
  name: pipeline-with-array
spec:
  params:
    - name: context
      description: Path to context
      default: 
        - "this"
        - "should"
        - "be overridden"
  tasks:
    - name: build-skaffold-web
      taskRef:
        name: build-push
      params:
        - name: pathToContext
          value: 
            - "1"
            - "${params.context}"
            - "2"
---
apiVersion: tekton.dev/v1alpha1
kind: PipelineRun
metadata:
  name: pipelinerun-with-array
spec:
  pipelineRef:
    name: pipeline-with-array
  params:
    - name: context
      value: 
        - "element1"
        - "element2"

After running this, the step's pod reads the following args:

echo
      --
      1
      element1
      element2
      2

Submitter Checklist

These are the criteria that every PR should meet, please check them off as you
review them:

See the contribution guide for more details.

Double check this list of stuff that's easy to miss:

Release Notes

Parameters now support the Array type.

@googlebot googlebot added the cla: yes Trying to make the CLA bot happy with ppl from different companies work on one commit label Jul 16, 2019
@tekton-robot tekton-robot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. labels Jul 16, 2019
@EliZucker EliZucker force-pushed the newarrayparams branch 2 times, most recently from 801ddf8 to 85037a0 Compare July 16, 2019 06:46
@EliZucker
Copy link
Member Author

/retest

@ghost
Copy link

ghost commented Jul 16, 2019

This may need corollary work in the CLI tool, tkn, or perhaps just a new version to be cut with this updated code? I just installed your controller and applied your example YAML, then ran tkn pipelineruns list. Received the following error:

~/dev/go/src/github.com/tektoncd/manual-tests/array-or-string                                                                                   [12/118]
 λ tkn pipelinerun list                                                                                                                                             
Failed to list pipelineruns from default namespace                                                                                                                  
Error: v1alpha1.PipelineRunList.Items: []v1alpha1.PipelineRun: v1alpha1.PipelineRun.Spec: v1alpha1.PipelineRunSpec.Params: []v1alpha1.Param: v1alpha1.Param.Value: R
eadString: expects " or n, but found [, error found in #10 byte of ...|,"value":["element1"|..., bigger context ...|da"},"spec":{"params":[{"name":"context","value$
:["element1","element2"]}],"pipelineRef":{"name":"pi|...                                                                                                            
Usage:                                                                                                                                                              
  tkn pipelinerun list [flags]

So I think whatever tkn is using to unmarshall the JSON response from the kube api needs to be updated to take your array support into account.

Edit: Also, note, this issue is not a blocker for this PR. Just something to keep an eye one when this feature gets merged.

@vdemeester
Copy link
Member

This may need corollary work in the CLI tool, tkn, or perhaps just a new version to be cut with this updated code? I just installed your controller and applied your example YAML, then ran tkn pipelineruns list. Received the following error:

~/dev/go/src/github.com/tektoncd/manual-tests/array-or-string                                                                                   [12/118]
 λ tkn pipelinerun list                                                                                                                                             
Failed to list pipelineruns from default namespace                                                                                                                  
Error: v1alpha1.PipelineRunList.Items: []v1alpha1.PipelineRun: v1alpha1.PipelineRun.Spec: v1alpha1.PipelineRunSpec.Params: []v1alpha1.Param: v1alpha1.Param.Value: R
eadString: expects " or n, but found [, error found in #10 byte of ...|,"value":["element1"|..., bigger context ...|da"},"spec":{"params":[{"name":"context","value$
:["element1","element2"]}],"pipelineRef":{"name":"pi|...                                                                                                            
Usage:                                                                                                                                                              
  tkn pipelinerun list [flags]

So I think whatever tkn is using to unmarshall the JSON response from the kube api needs to be updated to take your array support into account.

Indeed, I'll create an issue in the cli 👼

@EliZucker
Copy link
Member Author

/assign @imjasonh

Copy link
Member

@afrittoli afrittoli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this. I've not done a deep review yet, but it looks good.
It's a lot of code and complexity to support parameters of type array, but I feel it's worth the effort.

@tekton-robot
Copy link
Collaborator

The following is the coverage report on pkg/.
Say /test pull-tekton-pipeline-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/pipeline/v1alpha1/param_types.go Do not exist 45.5%
pkg/apis/pipeline/v1alpha1/pipeline_validation.go 98.5% 97.7% -0.8
pkg/apis/pipeline/v1alpha1/task_defaults.go 83.3% 90.0% 6.7
pkg/apis/pipeline/v1alpha1/task_validation.go 78.8% 78.2% -0.6
pkg/reconciler/v1alpha1/pipelinerun/pipelinerun.go 79.6% 80.0% 0.4
pkg/reconciler/v1alpha1/pipelinerun/resources/validate_params.go Do not exist 0.0%
pkg/reconciler/v1alpha1/taskrun/resources/apply.go 98.2% 100.0% 1.8
pkg/reconciler/v1alpha1/taskrun/validate_resources.go 98.0% 94.7% -3.2
pkg/templating/templating.go 81.8% 57.8% -24.0
test/builder/param.go Do not exist 100.0%
test/builder/pipeline.go 84.0% 84.4% 0.4
test/builder/task.go 91.6% 91.7% 0.1

@tekton-robot
Copy link
Collaborator

The following is the coverage report on pkg/.
Say /test pull-tekton-pipeline-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/pipeline/v1alpha1/param_types.go Do not exist 45.5%
pkg/apis/pipeline/v1alpha1/pipeline_validation.go 98.5% 97.7% -0.8
pkg/apis/pipeline/v1alpha1/task_defaults.go 83.3% 90.0% 6.7
pkg/apis/pipeline/v1alpha1/task_validation.go 78.8% 78.2% -0.6
pkg/reconciler/v1alpha1/pipelinerun/pipelinerun.go 79.6% 80.0% 0.4
pkg/reconciler/v1alpha1/pipelinerun/resources/validate_params.go Do not exist 0.0%
pkg/reconciler/v1alpha1/taskrun/resources/apply.go 98.2% 100.0% 1.8
pkg/reconciler/v1alpha1/taskrun/validate_resources.go 98.0% 94.7% -3.2
pkg/templating/templating.go 81.8% 57.8% -24.0
test/builder/param.go Do not exist 100.0%
test/builder/pipeline.go 84.0% 84.4% 0.4
test/builder/task.go 91.6% 91.7% 0.1

@tekton-robot
Copy link
Collaborator

The following is the coverage report on pkg/.
Say /test pull-tekton-pipeline-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/pipeline/v1alpha1/param_types.go Do not exist 45.5%
pkg/apis/pipeline/v1alpha1/pipeline_validation.go 98.5% 97.7% -0.8
pkg/apis/pipeline/v1alpha1/task_defaults.go 83.3% 90.0% 6.7
pkg/apis/pipeline/v1alpha1/task_validation.go 78.8% 78.2% -0.6
pkg/reconciler/v1alpha1/pipelinerun/pipelinerun.go 79.6% 80.0% 0.4
pkg/reconciler/v1alpha1/pipelinerun/resources/validate_params.go Do not exist 0.0%
pkg/reconciler/v1alpha1/taskrun/resources/apply.go 98.2% 100.0% 1.8
pkg/reconciler/v1alpha1/taskrun/validate_resources.go 98.0% 94.7% -3.2
pkg/templating/templating.go 81.8% 57.8% -24.0
test/builder/param.go Do not exist 100.0%
test/builder/pipeline.go 84.0% 84.4% 0.4
test/builder/task.go 91.6% 91.7% 0.1

@tekton-robot
Copy link
Collaborator

The following is the coverage report on pkg/.
Say /test pull-tekton-pipeline-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/pipeline/v1alpha1/param_types.go Do not exist 45.5%
pkg/apis/pipeline/v1alpha1/pipeline_validation.go 98.5% 97.7% -0.8
pkg/apis/pipeline/v1alpha1/task_defaults.go 83.3% 90.0% 6.7
pkg/apis/pipeline/v1alpha1/task_validation.go 78.8% 78.2% -0.6
pkg/reconciler/v1alpha1/pipelinerun/pipelinerun.go 79.6% 80.0% 0.4
pkg/reconciler/v1alpha1/pipelinerun/resources/validate_params.go Do not exist 0.0%
pkg/reconciler/v1alpha1/taskrun/resources/apply.go 98.2% 100.0% 1.8
pkg/reconciler/v1alpha1/taskrun/validate_resources.go 98.0% 94.7% -3.2
pkg/templating/templating.go 81.8% 57.8% -24.0
test/builder/param.go Do not exist 100.0%
test/builder/pipeline.go 84.0% 84.4% 0.4
test/builder/task.go 91.6% 91.7% 0.1

@tekton-robot
Copy link
Collaborator

The following is the coverage report on pkg/.
Say /test pull-tekton-pipeline-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/pipeline/v1alpha1/param_types.go Do not exist 52.6%
pkg/apis/pipeline/v1alpha1/pipeline_validation.go 98.5% 97.7% -0.8
pkg/apis/pipeline/v1alpha1/task_defaults.go 83.3% 90.0% 6.7
pkg/apis/pipeline/v1alpha1/task_validation.go 78.8% 78.2% -0.6
pkg/reconciler/v1alpha1/pipelinerun/pipelinerun.go 79.6% 80.0% 0.4
pkg/reconciler/v1alpha1/pipelinerun/resources/validate_params.go Do not exist 0.0%
pkg/reconciler/v1alpha1/taskrun/resources/apply.go 98.2% 100.0% 1.8
pkg/reconciler/v1alpha1/taskrun/validate_resources.go 98.0% 94.7% -3.2
pkg/templating/templating.go 81.8% 57.8% -24.0
test/builder/param.go Do not exist 100.0%
test/builder/pipeline.go 84.0% 84.4% 0.4
test/builder/task.go 91.6% 91.7% 0.1

@tekton-robot
Copy link
Collaborator

The following is the coverage report on pkg/.
Say /test pull-tekton-pipeline-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/pipeline/v1alpha1/param_types.go Do not exist 94.7%
pkg/apis/pipeline/v1alpha1/pipeline_validation.go 98.5% 97.7% -0.8
pkg/apis/pipeline/v1alpha1/task_defaults.go 83.3% 90.0% 6.7
pkg/apis/pipeline/v1alpha1/task_validation.go 78.8% 78.2% -0.6
pkg/reconciler/v1alpha1/pipelinerun/pipelinerun.go 79.6% 80.0% 0.4
pkg/reconciler/v1alpha1/pipelinerun/resources/validate_params.go Do not exist 100.0%
pkg/reconciler/v1alpha1/taskrun/resources/apply.go 98.2% 100.0% 1.8
pkg/reconciler/v1alpha1/taskrun/validate_resources.go 98.0% 94.7% -3.2
pkg/templating/templating.go 81.8% 57.8% -24.0
test/builder/param.go Do not exist 100.0%
test/builder/pipeline.go 84.0% 84.4% 0.4
test/builder/task.go 91.6% 91.7% 0.1

@EliZucker
Copy link
Member Author

Tagging @dlorenc because I heard you set up the code coverage reports: I'm not sure if it's something I'm doing wrong from my end, but the coverage reports clearly contradict what IntelliJ is reporting (and I think IntelliJ is right?). Specifically templating.go is reported for having much lower coverage than intellij reports (see my comparison here).

@tekton-robot tekton-robot added the lgtm Indicates that a PR is ready to be merged. label Jul 26, 2019
@tekton-robot tekton-robot removed the lgtm Indicates that a PR is ready to be merged. label Jul 26, 2019
@tekton-robot
Copy link
Collaborator

tekton-robot commented Jul 26, 2019

The following is the coverage report on pkg/.
Say /test pull-tekton-pipeline-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/pipeline/v1alpha1/param_types.go Do not exist 10000%
pkg/apis/pipeline/v1alpha1/pipeline_validation.go 98.5% 97.7% -0.8
pkg/apis/pipeline/v1alpha1/task_defaults.go 83.3% 90.0% 6.7
pkg/apis/pipeline/v1alpha1/task_validation.go 78.8% 78.2% -0.6
pkg/reconciler/v1alpha1/pipelinerun/pipelinerun.go 80.5% 80.9% 0.4
pkg/reconciler/v1alpha1/pipelinerun/resources/validate_params.go Do not exist 100.0%
pkg/reconciler/v1alpha1/taskrun/resources/apply.go 98.2% 100.0% 1.8
pkg/reconciler/v1alpha1/taskrun/taskrun.go 68.6% 71.0% 2.4
pkg/reconciler/v1alpha1/taskrun/validate_resources.go Do not exist 94.7%
pkg/templating/templating.go 81.8% 57.8% -24.0
test/builder/param.go Do not exist 100.0%
test/builder/pipeline.go 84.0% 84.4% 0.4
test/builder/task.go 91.6% 91.7% 0.1

@@ -118,16 +118,25 @@ func TestPipelineSpec_Validate(t *testing.T) {
}, {
name: "valid parameter variables",
p: tb.Pipeline("pipeline", "namespace", tb.PipelineSpec(
tb.PipelineParam("baz"),
tb.PipelineParam("foo-is-baz"),
tb.PipelineParamSpec("baz", v1alpha1.ParamTypeString),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@imjasonh Just renamed this from PipelineParam to PipelineParamSpec to match changes in #1037 (I forgot to rename the builder functions).

Thinking more about trying to make the type optional here (disregarding complexity of implementing an optional parameter), we encourage users to explicitly mark their parameter types in ParamSpec but default to string partially for backwards compatibility. Is it that bad to have the types explicit for this builder function? We also don't have to worry about backwards compatibility here.

Edit: Also should mention that PipelineResources also follow a similar builder pattern: func PipelineResourceSpec(resourceType v1alpha1.PipelineResourceType, ops ...PipelineResourceSpecOp)

@@ -227,6 +231,21 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1alpha1.PipelineRun) er
return nil
}

// Ensure that the parameters from the PipelineRun are overriding Pipeline parameters with the same type.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh interesting! In my head, I see webhook validation as validation that only uses information on that isolated YAML file being submitted (e.g invalid types, invalid variable interpolation strings), while reconciler validation brings in outside resources and evaluates the collective context.

In this case, since the validation requires finding the referenced pipeline resource, I put it in the reconciler -- this particular test verifies that, for example, a pipelinerun parameter string value is not overriding a corresponding pipeline's parameter array value. This seems really similar to the validation done with GetResourcesFromBindings (will validate that all PipelineResources declared in Pipeline p are bound in PipelineRun pr), which is also in the reconciler.

Writing this up, I think my decision makes sense, but let me know if it doesn't!

@imjasonh
Copy link
Member

/lgtm
/approve

@tekton-robot tekton-robot added lgtm Indicates that a PR is ready to be merged. approved Indicates a PR has been approved by an approver from all required OWNERS files. labels Jul 29, 2019
@dibyom
Copy link
Member

dibyom commented Jul 29, 2019

/retest

Instead of just strings, add support for users to supply parameters in
the form of an array. Also include appropriate type validation and
defaulting to support backwards-compatibility. Fixes tektoncd#207.

The size of a user-supplied array is not specified anywhere in the
corresponding ParamSpec, which is useful for defining tasks that
require a dynamic number of strings used as distinct elements in
an array. For instance, one might use an array parameter in an
image-building task that feeds in a dynamic number of flags via the
args field for a particular step.

The implementation is defined such that an Array parameter can only be
used if the replacement string is completely isolated and within a field
that is an array of strings (currently eligible fields are 'command'
and 'args'). For instance, the webhook will prevent an array parameter
from being used in the 'image' field. Similarly, an array parameter used
in a composite string, like 'value=${array}', is invalid and will be
caught. The decision was made to completely prevent any use of array
parameters as strings because it clutters Tekton with unnecessary
functionality and is naturally extensible beyond the intended scope.
For instance, if the behavior was defined such that array parameters
used in the context of strings were automatically converted by
separating them with a space, then that relatively arbitrary
implementation could be questioned and eventually lead to needing to
specify a custom delimiter (like commas), and so on.

Another implementation detail to note is that the ArrayOrString type was
necessary, in combination with implementing json.Marshaller, to support
backwards-compatibility with strings. This is based off IntOrString from
kubernetes/apimachinery, and allows the value-type to be immediately
parsed and decided through the webhook.

Lastly, a possibly unintuitive design decision is that if an EMPTY array
parameter properly replaces an array element, then that string in the
original array will be completely removed.
Following feedback from @imjasonh, rename ArrayOrStringParam to Param
and rename Param to ResourceParam. This makes sense becase
ArrayOrStringParam represented a more versatile type, while Param referred
to a specific string-only type exclusively used for PipelineResources
(like a git URL).
Change conditional tests to reflect the new ArrayOrString type. Also
condense some builders that repeat the same ParamSpec building logic;
the same ParamSpec building methods were repeated for Tasks, Pipelines,
and Conditions, just with different names.
@tekton-robot tekton-robot removed the lgtm Indicates that a PR is ready to be merged. label Jul 30, 2019
@tekton-robot
Copy link
Collaborator

The following is the coverage report on pkg/.
Say /test pull-tekton-pipeline-go-coverage to re-run this coverage report

File Old Coverage New Coverage Delta
pkg/apis/pipeline/v1alpha1/param_types.go Do not exist 94.7%
pkg/apis/pipeline/v1alpha1/pipeline_validation.go 98.5% 97.7% -0.8
pkg/apis/pipeline/v1alpha1/task_defaults.go 83.3% 90.0% 6.7
pkg/apis/pipeline/v1alpha1/task_validation.go 78.8% 78.2% -0.6
pkg/reconciler/v1alpha1/pipelinerun/pipelinerun.go 80.5% 80.9% 0.4
pkg/reconciler/v1alpha1/pipelinerun/resources/validate_params.go Do not exist 100.0%
pkg/reconciler/v1alpha1/taskrun/resources/apply.go 98.2% 100.0% 1.8
pkg/reconciler/v1alpha1/taskrun/validate_resources.go 98.0% 94.7% -3.2
pkg/templating/templating.go 81.8% 57.8% -24.0
test/builder/param.go Do not exist 100.0%
test/builder/pipeline.go 84.0% 83.8% -0.3
test/builder/task.go 91.6% 91.4% -0.1

@EliZucker
Copy link
Member Author

@imjasonh @afrittoli If one of you could give an lgtm that would be great! I had to rebase/refactor a bit to merge correctly with the initial conditional stuff, so the label was removed.

Copy link
Member

@dibyom dibyom left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

@tekton-robot tekton-robot added the lgtm Indicates that a PR is ready to be merged. label Jul 30, 2019
@tekton-robot
Copy link
Collaborator

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: afrittoli, dibyom, EliZucker, ImJasonH

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@tekton-robot tekton-robot merged commit e2786ac into tektoncd:master Jul 30, 2019
@ghost
Copy link

ghost commented Jul 30, 2019

🎉 Niiiiiiiiiice

@EliZucker EliZucker deleted the newarrayparams branch July 30, 2019 17:32
EliZucker added a commit to EliZucker/pipeline that referenced this pull request Jul 31, 2019
@vdemeester pointed out that tektoncd#1080 fixes a previous issue where default
empty string parameters weren't working properly. Add a test to ensure
this new functionality remains.
tekton-robot pushed a commit that referenced this pull request Jul 31, 2019
@vdemeester pointed out that #1080 fixes a previous issue where default
empty string parameters weren't working properly. Add a test to ensure
this new functionality remains.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cla: yes Trying to make the CLA bot happy with ppl from different companies work on one commit lgtm Indicates that a PR is ready to be merged. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

TaskSpec Params needs an Array type
7 participants