Skip to content

Commit

Permalink
feat(pipeline_template): Support template-less configurations (#1636)
Browse files Browse the repository at this point in the history
  • Loading branch information
robzienert authored Sep 22, 2017
1 parent d7b8150 commit f46015b
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import com.netflix.spinnaker.orca.pipelinetemplate.v1schema.model.TemplateConfig
import com.netflix.spinnaker.orca.pipelinetemplate.v1schema.render.DefaultRenderContext
import com.netflix.spinnaker.orca.pipelinetemplate.v1schema.render.RenderContext
import com.netflix.spinnaker.orca.pipelinetemplate.v1schema.render.Renderer
import com.netflix.spinnaker.orca.pipelinetemplate.validator.Errors
import java.util.stream.Collectors

class V1TemplateLoaderHandler(
private val templateLoader: TemplateLoader,
Expand All @@ -44,25 +44,32 @@ class V1TemplateLoaderHandler(
return
}

if (config.pipeline.template == null) {
context.getErrors().add(Errors.Error().withMessage("configuration is missing a template"))
return
}

val trigger = context.getRequest().trigger as MutableMap<String, Any>?

setTemplateSourceWithJinja(config, trigger)
val templates = templateLoader.load(config.pipeline.template)

val mergedTemplate = TemplateMerge.merge(templates)
// If a template source isn't provided by the configuration, we're assuming that the configuration is fully-formed.
val template: PipelineTemplate
if (config.pipeline.template == null) {
template = PipelineTemplate().apply {
variables = config.pipeline.variables.entries.stream()
.map { PipelineTemplate.Variable().apply {
name = it.key
defaultValue = it.value
}}
.collect(Collectors.toList())
}
} else {
val templates = templateLoader.load(config.pipeline.template)
template = TemplateMerge.merge(templates)
}

// ensure that any expressions contained with template variables are rendered
val renderContext = DefaultRenderContext(config.pipeline.application, mergedTemplate, trigger)
renderTemplateVariables(renderContext, mergedTemplate)
val renderContext = DefaultRenderContext(config.pipeline.application, template, trigger)
renderTemplateVariables(renderContext, template)

context.setSchemaContext(V1PipelineTemplateContext(
config,
mergedTemplate
template
))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.netflix.spinnaker.orca.pipelinetemplate.v1schema.validator;

import com.netflix.spinnaker.orca.pipelinetemplate.v1schema.model.StageDefinition;
import com.netflix.spinnaker.orca.pipelinetemplate.v1schema.model.TemplateConfiguration;
import com.netflix.spinnaker.orca.pipelinetemplate.v1schema.model.TemplateConfiguration.PipelineDefinition;
import com.netflix.spinnaker.orca.pipelinetemplate.validator.Errors;
Expand Down Expand Up @@ -60,7 +61,7 @@ public void validate(VersionedSchema configuration, Errors errors, SchemaValidat
V1SchemaValidationHelper.validateStageDefinitions(config.getStages(), errors, V1TemplateConfigurationSchemaValidator::location);

config.getStages().forEach(s -> {
if (!context.stageIds.contains(s.getId()) && (s.getDependsOn() == null || s.getDependsOn().isEmpty()) && (s.getInject() == null || !s.getInject().hasAny())) {
if (shouldRequireDagRules(s, config, context.stageIds)) {
errors.add(new Error()
.withMessage("A configuration-defined stage should have either dependsOn or an inject rule defined")
.withLocation(location(String.format("stages.%s", s.getId())))
Expand All @@ -69,6 +70,13 @@ public void validate(VersionedSchema configuration, Errors errors, SchemaValidat
});
}

private static boolean shouldRequireDagRules(StageDefinition s, TemplateConfiguration config, List<String> stageIds) {
return config.getPipeline().getTemplate() != null &&
!stageIds.contains(s.getId()) &&
(s.getDependsOn() == null || s.getDependsOn().isEmpty()) &&
(s.getInject() == null || !s.getInject().hasAny());
}

private static String location(String location) {
return "configuration:" + location;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2017 Netflix, Inc.
*
* 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 com.netflix.spinnaker.orca.pipelinetemplate.v1schema.handler

import com.fasterxml.jackson.databind.ObjectMapper
import com.netflix.spinnaker.orca.pipelinetemplate.TemplatedPipelineRequest
import com.netflix.spinnaker.orca.pipelinetemplate.handler.DefaultHandlerChain
import com.netflix.spinnaker.orca.pipelinetemplate.handler.GlobalPipelineTemplateContext
import com.netflix.spinnaker.orca.pipelinetemplate.loader.TemplateLoader
import com.netflix.spinnaker.orca.pipelinetemplate.v1schema.render.Renderer
import spock.lang.Specification
import spock.lang.Subject

class V1TemplateLoaderHandlerSpec extends Specification {

Renderer renderer = Mock() {
renderGraph(_, _) >> { value, _ ->
return value
}
}

@Subject
def subject = new V1TemplateLoaderHandler(Mock(TemplateLoader), renderer, new ObjectMapper())

def 'should create stub template when no template is provided'() {
given:
def configuration = [
schema: '1',
pipeline: [
application: 'orca',
name: 'My Template',
variables: [
foo: 'bar'
]
],
stages: [
[
id: 'wait',
type: 'wait',
config: [
waitTime: 5
]
]
]
]

and:
def chain = new DefaultHandlerChain()
def context = new GlobalPipelineTemplateContext(chain, new TemplatedPipelineRequest(
config: configuration
))

when:
subject.handle(chain, context)

then:
noExceptionThrown()
context.schemaContext != null
(context.schemaContext as V1PipelineTemplateContext).template.variables*.name == ['foo']
(context.schemaContext as V1PipelineTemplateContext).template.variables*.defaultValue == ['bar']

}
}

0 comments on commit f46015b

Please sign in to comment.