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

fix(pipelines): expectedArtifacts inheritance from dynamic parent templates #3023

Merged
merged 1 commit into from
Jul 26, 2019

Conversation

xabierlaiseca
Copy link
Contributor

When expected artifacts are defined in a template that is sourced dynamically,
expected artifacts are not added to the execution.

Expected artifacts can be defined in TemplatedPipelineRequest,
TemplateConfiguration and PipelineTemplate objects, so a merge strategy
is required. Given expected artifacts have a unique id, artifacts with
same id in these object will be overridden. The artifacts with higher
priority are the ones in TemplatedPipelineRequest, then TemplateConfiguration
and finally PipelineTemplate.

@xabierlaiseca
Copy link
Contributor Author

@spinnaker/reviewers

// result.stages.size() == 1
// result.stages[0].type == "wait"
// result.stages[0].refId == "wait1"
// result.stages[0].name == "Wait for 5 seconds"
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe remove these if not relevant anymore

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed

pipelineConfig = preprocessor.process(pipelineConfig)
}

pipelineConfig.trigger = trigger
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason you are moving this back? It breaks what I fixed in #2963.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Apparently it will be required to run resolve artifacts twice. I added a comment in code explaining why

@gardleopard
Copy link
Contributor

@spinnaker/google-reviewers PTAL

Copy link
Contributor

@ezimanyi ezimanyi left a comment

Choose a reason for hiding this comment

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

I've added a few comments inline to try to understand this better. I'm a bit nervous about this change as there have been a lot of bugs in artifact resolution for pipeline templates recently, and this code is very fragile so I want to make sure that fixing this particular edge case doesn't introduce more general issues. If there's any way to scope the change more specifically to this change rather than always resolving twice, that might be preferable.

I'm less familiar with the V1-template specific stuff, so would probably want someone who has more knowledge of that to take a look. (Perhaps @marchello2000?)

@@ -27,7 +26,7 @@
String id;
String schema;
String type;
List<ExpectedArtifact> expectedArtifacts;
List<HashMap<String, Object>> expectedArtifacts;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we moving from a typed object to a generic Map here? Are we expecting that expectedArtifacts contains a list of things that aren't actually expected artifacts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The only reason to move it to HashMap<String, Object> is that it's easier to merge artifacts from different sources given that the other sources are using HashMap<String, Object> (TemplateConfiguration.PipelineConfiguration and PipelineTemplate.Configuration).

I considered changing these 2 configuration classes to use ExpectedArtifact instead but many tests failed because of the lack of Artifact boundArtifact field in templates and configurations.

Given that orca is not using the expected artifacts in this class, I consider this change involves lower risk than doing a bigger refactoring in order to be able to use ExpectedArtifact in the other cases.

Copy link
Member

Choose a reason for hiding this comment

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

Method and property types should use an interface type, so these should change to Map<String, Object>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@robzienert map type comment addressed

@@ -100,14 +100,18 @@ class DependentPipelineStarter implements ApplicationContextAware {

def trigger = pipelineConfig.trigger
//keep the trigger as the preprocessor removes it.
def expectedArtifacts = pipelineConfig.expectedArtifacts
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like this is reverting the fix that was made in #2430. Have you tested that you haven't re-introduced the bug that was fixed there?


if (parentPipelineStageId != null) {
pipelineConfig.receivedArtifacts = artifactResolver?.getArtifacts(parentPipeline.stageById(parentPipelineStageId))
} else {
pipelineConfig.receivedArtifacts = artifactResolver?.getAllArtifacts(parentPipeline)
}

// For pipelines with dynamic source, it is required to perform the following steps:
// 1. Resolve source template artifact so the dynamic source expression can be evaluated.
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure that I fully understand why we need to resolve artifacts twice here, and have some concerns about now always resolving artifacts twice. I think partly it's that I don't fully understand what is a "pipeline with dynamic source" is. Can you please give an example (or point to a test case) for what such a pipeline looks like and how it would get resolved?

Copy link
Contributor Author

@xabierlaiseca xabierlaiseca Jul 12, 2019

Choose a reason for hiding this comment

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

with pipeline with dynamic source I meant to have an expression to fetch source pipeline from artifacts such as

"source": "{% for artifact in trigger.artifacts %}{% if artifact.name == 'pipeline-template' && artifact.type == 'http/file' %}{{artifact.reference}}{% endif %}{% endfor %}"

here a test with dynamic parent:

def "should trigger v1 templated pipelines with dynamic source using prior artifact"() {

On why we need to resolve artifacts twice, the first time we need it in order to load parent pipeline, once we have loaded the parent pipeline, we need to resolve artifacts again as new artifacts could have been found in parent pipeline.

@xabierlaiseca
Copy link
Contributor Author

@ezimanyi I hope my comments give you some clarification on this. Let me know if more explanation is needed.

@ezimanyi
Copy link
Contributor

@xabierlaiseca : Thanks for the explanation, and the pointer to the test. Looking at that example, it seems like the only effect of the first resolveExpectedArtifacts is just to copy the contents of pipeline.recievedArtifacts to pipeline.trigger.artifacts.

It seems like it might be less complex and less risky of a change to leave a single pass of artifact resolution (happening after running the pre-processors as you have here) and have the template refer to {% for artifact in receivedArtifacts %}. If I understand how this works (which it's possible I don't fully yet) I think that should work.

Alternately I could also imagine just directly setting trigger.artifacts where we set pipelineConfig.receivedArtifacts a few lines above if for some reason we need to read these from the trigger. That would avoid actually resolving twice (which has caused issues in the past).

@ezimanyi
Copy link
Contributor

The context behind my extreme caution around this is that there were a lot of very subtle bugs in artifact resolution for templated pipelines in the numerous comments on spinnaker/spinnaker#2654. I spent a bunch of time fixing those, but the code is still more complex and not as well tested than I'd like so I want to be careful about introducing increased complexity.

I have less direct experience with how the V1 template processing works (which is why my comments are more around the artifact resolution in DependentPipelineStarter). Would definitely appreciate a look from @robzienert or @marchello2000 who would be in a better position to comment on that part of it.

return Optional.ofNullable(eas).orElse(Collections.emptyList());
}

private List<HashMap<String, Object>> mergeExpectedArtifact(
Copy link
Contributor

Choose a reason for hiding this comment

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

just confirming this is the intent (which, looking at that usages, I think it is) if there are duplicates, the artifacts in the eas2 will "win", right?

Copy link
Contributor Author

@xabierlaiseca xabierlaiseca Jul 19, 2019

Choose a reason for hiding this comment

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

yes, that is the intent, this way an spinnaker configuration will be able to override an expected artifact in a template

@marchello2000
Copy link
Contributor

Sorry, just saw this.
I agree with @ezimanyi comments. I don't have context on prior subtleties of failures other than they existed and were painful... @robzienert maybe you can look at this too?

@marchello2000
Copy link
Contributor

@xabierlaiseca : Thanks for the explanation, and the pointer to the test. Looking at that example, it seems like the only effect of the first resolveExpectedArtifacts is just to copy the contents of pipeline.recievedArtifacts to pipeline.trigger.artifacts.

It seems like it might be less complex and less risky of a change to leave a single pass of artifact resolution (happening after running the pre-processors as you have here) and have the template refer to {% for artifact in receivedArtifacts %}. If I understand how this works (which it's possible I don't fully yet) I think that should work.

Alternately I could also imagine just directly setting trigger.artifacts where we set pipelineConfig.receivedArtifacts a few lines above if for some reason we need to read these from the trigger. That would avoid actually resolving twice (which has caused issues in the past).

I agree, seems like this should work, @xabierlaiseca what do you think?

@xabierlaiseca
Copy link
Contributor Author

@ezimanyi @marchello2000 I tried {% for artifact in receivedArtifacts %} but receivedArtifacts could not be resolved when rendering the template.

I'll try the alternative way that was mentioned

@xabierlaiseca
Copy link
Contributor Author

xabierlaiseca commented Jul 22, 2019

@ezimanyi @marchello2000 can you please have a look to this commit 6f9408f , I believe this is what you suggested instead of resolving artifacts twice but I would like to get some feedback about it from you

@marchello2000
Copy link
Contributor

Thanks, @xabierlaiseca, for reworking this. Unfortunately, I don't understand the subtleties of artifact resolution so I will let @ezimanyi review this

@ezimanyi
Copy link
Contributor

@xabierlaiseca : Yes, the latest commit (6f9408f) is indeed what I was suggesting to avoid resolving artifacts twice. If that works for your use case then I think that would be more robust than always resolving twice.

I think the only remaining question from my perspective is still that it appears this reverts #2430 (which stored and re-set the expected artifacts). Was that a deliberate change, or will this re-introduce the issue there?

@PerGon
Copy link
Member

PerGon commented Jul 25, 2019

@ezimanyi regarding #2430 , that fix is covered in line https://github.com/spinnaker/orca/pull/3023/files#diff-4f3d52b85acf51b2ed51955fdbbb78e3R165
so that is covered

…plates

When expected artifacts are defined in a template that is sourced dynamically,
expected artifacts are not added to the execution.

Expected artifacts can be defined in `TemplatedPipelineRequest`,
`TemplateConfiguration` and `PipelineTemplate` objects, so a merge strategy
is required. Given expected artifacts have a unique `id`, artifacts with
same `id` in these object will be overridden. The artifacts with higher
priority are the ones in `TemplatedPipelineRequest`, then `TemplateConfiguration`
and finally `PipelineTemplate`.
@xabierlaiseca
Copy link
Contributor Author

@ezimanyi @marchello2000 given @PerGon's comment, I believe the PR is ready to be merged, I've just squashed my commits. Could you please re-review and merge it you consider is now ready?

@ezimanyi
Copy link
Contributor

OK, this final version looks good to me! Thanks for addressing all of the comments!

@ezimanyi ezimanyi merged commit 511bff2 into spinnaker:master Jul 26, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
8 participants