-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
Declare resources in pipeline 😇 #415
Declare resources in pipeline 😇 #415
Conversation
fd6472f
to
b3262b7
Compare
@shashwathi is that test supposed to get skipped in CI? |
|
||
// providedBy should match future tasks | ||
// TODO(#168) when pipelines don't just execute linearly this will need to be more sophisticated | ||
for i, t := range ps.Tasks { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may want to extract that (or part of it 👼). Like validateProvidedBy
or something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh yeah, that's a good call!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think for now ill leave the testing at the level of the Validate
function 🤔 kinda wondering if we should be testing these functions directly instead 🤔🤔🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice this makes it much easier to have meaningful errors :D
docs/using.md
Outdated
|
||
We express this ordering by adding `providedBy` on `Resources` that our `Tasks` | ||
We express this dependency by adding `providedBy` on `Resources` that our `Tasks` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"This dependency is expressed by …" (if we want to avoid the use of we
– #k8s-docs-guidelines)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whoops, this is gonna be a hard habit to break for us me 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe "Express this dependency by adding..." - avoiding passive voice? :D
docs/using.md
Outdated
You can see that the `builtImage` output from `build-skaffold-app` is bound to | ||
the `skaffold-image-leeroy-app` `PipelineResource`, and the same | ||
`PipelineResource` is bound to `image` for `deploy-app`. | ||
The `my-image` resource is expected to be provided to the `deploy-app` `Task` from |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"The resource my-image
…"
docs/using.md
Outdated
### Resource shared between tasks | ||
##### Configure Entrypoint image | ||
|
||
To run a step needs to pull an `Entrypoint` image. Maybe the image is hard to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think something is missing in the first sentence
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol whoops 😅
docs/using.md
Outdated
mounted under path `/pvc`. | ||
### Resource sharing between tasks | ||
|
||
Pipeline Tasks are allowed to pass resources from previous tasks via the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/tasks/Tasks/
} | ||
|
||
func validateDeclaredResources(ps *PipelineSpec) error { | ||
needed := []string{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/needed/required/g
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
kk
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
now these lines are exactly the same length, very satisfying 😎
required := []string{"elsa", "anna", "olaf", "kristoff"}
provided := []string{"elsa", "anna", "olaf", "kristoff"}
fields: fields{ | ||
Resources: []PipelineDeclaredResource{{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're gonna need builder
s for those 🕺
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i actually did add builders for these! pipeline_validation_test
isn't using any builders cuz it's doing this odd (where odd = different from all the other tests) thing where it's declaring "fields" and using those to instantiate a Pipeline 🤔
ill see what i can do!
func GetResourcesFromBindings(p *v1alpha1.Pipeline, pr *v1alpha1.PipelineRun) (map[string]v1alpha1.PipelineResourceRef, error) { | ||
resources := map[string]v1alpha1.PipelineResourceRef{} | ||
|
||
needed := make([]string, 0, len(p.Spec.Resources)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same s/needed/required/g
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we move all the documentation changes that are not relevant to this change in another PR?
I know you mention it in "Also includes" however, i feel reviewers give more attention to code review then docs and it might be lost. Also will make review easier.
kk i'll give it a shot! |
Actually on second thought @tejal29 how strongly do you feel about this? It's going to be a bit hard to break up, and @vdemeester has already added a bunch of comments on the doc changes in this PR. The "PipelineResources in a Pipeline" section in
I'll try to break it up if you feel really strongly but it's gonna be tough - I'll try to be more aware in the future to create separate PRs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good to me! great job @bobcatfish
are you planning on doing the other items in #302 in a different PR? |
not sure why the test is not passing, I going to run it again to see if this was a flake /test pull-knative-build-pipeline-integration-tests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great PR. I have suggested couple of missing test cases and nil pointer check. Rest looks awesome! 👍 🎆
I am stressing unit test cases because we are relying on this to catch most use cases. (I just noticed my suggested test cases have "crazy" in them. Please change that :D )
// should be provided to a Task as its inputs and outputs. | ||
type PipelineTaskResources struct { | ||
// Inputs holds the mapping from the PipelineResources declared in | ||
// DeclaredPipelineResources to the input PipelineResources reuqired by the Task. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: reuqired
in both Inputs
, Outputs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😅
@@ -32,6 +33,39 @@ func (p *Pipeline) Validate() *apis.FieldError { | |||
return nil | |||
} | |||
|
|||
func isOutput(task PipelineTask, resource string) bool { | |||
for _, output := range task.Resources.Outputs { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
task.Resources
is pointer. We need to check here if its nil. An example test case
{
name: "providedBy resource isn't present in task",
fields: fields{
Resources: []PipelineDeclaredResource{{
Name: "wonderful-resource",
Type: "image",
}},
Tasks: []PipelineTask{{
Name: "bar",
// no resources used
}, {
Name: "foo",
Resources: &PipelineTaskResources{
Inputs: []PipelineTaskInputResource{{
Name: "wow-image",
Resource: "wonderful-resource",
ProvidedBy: []string{"bar"},
}},
},
}},
},
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
task.Resources is pointer
yeah i missed that a couple of times in my earlier iterations :S definitely had some panics 😅
An example test case
hmmm technically the "no duplicate tasks" test case cover this but I'm cool with adding another case explicitly!
ProvidedBy: []string{"bar"},
gonna remove that tho since that would mean bar
would have to have an output
}, | ||
}, | ||
{ | ||
name: "providedBy resource isn't output by task", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also add a test case for 2nd task referring resource from non existent task? (found
variable should catch that use case
{
name: "providedBy resource refers non existent task name",
fields: fields{
Resources: []PipelineDeclaredResource{{
Name: "wonderful-resource",
Type: "git",
}},
Tasks: []PipelineTask{{
Name: "bar",
}, {
Name: "foo",
Resources: &PipelineTaskResources{
Inputs: []PipelineTaskInputResource{{
Name: "wow-image",
Resource: "wonderful-resource",
ProvidedBy: []string{"bar-crazy"},
}},
},
}},
},
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think this is caught by providedby task doesnt exist
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
providedby task doesnt exist
test has resource dependency on a task that does not exist but it is the first task. The code path in this test case is
if i == 0 {
.. "first task cant depend on anything"
}
The test I have given above has 2 tasks and 2nd task refers a dependency that does not exist. It takes diff code paths in pipeline validation. In the code there is found
variable which is suppose to catch this test case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ohhh okay I think I finally see what you're saying!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry I was so slow to catch on, and thanks for noticing this!
@@ -149,7 +163,11 @@ func ResolvePipelineRun(getTask resources.GetTask, getClusterTask resources.GetC | |||
} | |||
|
|||
// Get all the resources that this task will be using, if any | |||
inputs, outputs := getPipelineRunTaskResources(pt.Name, pr) | |||
inputs, outputs, err := getPipelineRunTaskResources(pt, providedResources) | |||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am guessing that error code path is defined to have clean code but we do not expect to be in this code path right?
there are couple tests cases in pipeline task resources that are missing(like missing input/output resource for task) but I can understand that we do not expect to be here since webhook catch it way before. It makes sense
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah exactly! i have pretty mixed feelings about it, but i thought it was better to err on the safer side, just in case validation was changed later on to not catch this (would be very hard to remember to change logic here as a result!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i see what you mean tho, it feels weird not to have test coverage for this! ill try adding it and see how we feel about it
Name: "awesome-thing", | ||
ProvidedBy: []string{"quest"}, | ||
}}, | ||
}}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am unable to tag the test in this comment: TestValidateProvidedBy_Invalid
test is missing a case when ResolvedTaskResourceInput
refer non existent resource
{
name: "resolved task resource refers to non existent input resource ",
state: []*ResolvedPipelineRunTask{{
PipelineTask: &v1alpha1.PipelineTask{
Name: "winning",
Resources: &v1alpha1.PipelineTaskResources{
Inputs: []v1alpha1.PipelineTaskInputResource{{
Name: "sweet-artifact",
ProvidedBy: []string{"quest"},
}},
}},
ResolvedTaskResources: tb.ResolvedTaskResources(
tb.ResolvedTaskResourcesTaskSpec(
tb.TaskInputs(tb.InputsResource("sweet-artifact", v1alpha1.PipelineResourceTypeImage)),
),
tb.ResolvedTaskResourcesInputs("sweet-artifact-crazy", r), // non existent resource
),
}},
errContains: "declare dependency",
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm this is weird, i feel like this is another case that shouldnt actually happen - now that "providedBy" must be on inputs and its not possible to accidentally add it to outputs 🤔 ill try to add a test case for it tho!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
after thinking about it more i really dont think this if statement makes sense anymore and im going to remove it - thanks for catching this @shashwathi !!
// contain the same values. | ||
func IsSame(needed, provided []string) error { | ||
missing := DiffLeft(needed, provided) | ||
if len(missing) > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably another implementation would be to sort needed and provided array and pass them to cmp
lib. Only benefit I can think of suggested approach is cmp
checks both length and content of array. Just a suggestion. I don't have strong opinion about this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes sense to me! do you mind if we table this for a future PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure 👍
I keep thinking about this and realizing what a good suggestion this is - I should have done this from the start @tejal29 ! I could have gotten a lot of those doc changes up for review way sooner 🤦♀️ Next time for sure! |
@pivotal-nader-ziada yes! i want to tackle the
boo I guess it's not a flake :) |
Definitely appreciated @shashwathi 🙏 ❤️ !! |
Now resources will be bound to a "name" which is used in the `Pipeline` to refer to them. This will simplify the binding declarations in `PipelineRun` and also make the `Pipeline` definition solely responsible for linking resources to Tasks. Toward tektoncd#320
a61fb48
to
104a2cd
Compare
Nice, there's actually a pretty good error message :D
|
104a2cd
to
7b10398
Compare
Okay should be ready for another look @shashwathi @pivotal-nader-ziada @vdemeester @tejal29 ! We'll see if I fixed the e2e test 🤞 |
A Googler has manually verified that the CLAs look good. (Googler, please make sure the reason for overriding the CLA status is clearly documented in these comments.) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/lgtm
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: bobcatfish, shashwathi 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 |
@@ -155,7 +156,7 @@ func getCreateImageTask(namespace string, t *testing.T) *v1alpha1.Task { | |||
} | |||
|
|||
imageName = fmt.Sprintf("%s/%s", dockerRepo, AppendRandomString(sourceImageName)) | |||
t.Logf("Image to be pusblished: %s", imageName) | |||
logger.Infof("Image to be pusblished: %s", imageName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
o no @jonjohnsonjr would be very upset about this
@bobcatfish : Looks like yaml tests are failing. |
So there's good news and bad news. 👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there. 😕 The bad news is that it appears that one or more commits were authored or co-authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that here in the pull request. Note to project maintainer: This is a terminal state, meaning the |
whoops, thanks @shashwathi ! looks like I completely missed updating the output yaml examples 😅 |
A Googler has manually verified that the CLAs look good. (Googler, please make sure the reason for overriding the CLA status is clearly documented in these comments.) |
The following is the coverage report on pkg/.
|
/lgtm |
Lets have a party to celebrate this PR getting merged! 🎆 Great work @bobcatfish |
awesome job on this PR @bobcatfish |
Thanks @shashwathi and @pivotal-nader-ziada ! Thanks to everyone's careful reviewing!! @shashwathi you caught a lot of things I missed, thanks!! 🤗 |
Now resources will be bound to a "name" which is used in the
Pipeline
to refer to them. This will simplify the binding declarations in
PipelineRun
and also make thePipeline
definition solely responsiblefor linking resources to Tasks.
This will let users effectively say something like:
Instead of previously, where it was more like:
This also has some side benefits in allowing us to do some extra validation at creation time for
Pipelines
.Also includes:
Pipelines
to verify thatprovidedBy
constraints make sense (i.e. the order can be satisfied) - since we only have linear execution, this meanscanTaskRun
is no longer needed 😇Question for reviewers:
Pipeline
? @sebgoa had suggested removing in thetype
, in which case we could just declare a list of names of resources. At the moment this doesn't really give us a lot in the way of extra validation b/c we'd still have to fetchTask
s to see if the types match up, which we only do at runtime. I do feel like there is value in telling the user what types these should be so they know what to bind though 🤔I think the end to end and sample tests may fail but I wanted to get this PR out and start the review process asap! 😅
(This fixes most of #320! 🎉 )
p.s. sorry this is so huge! couldn't figure out how to break it up 😅