Skip to content

Commit

Permalink
refactor(pipeline_template): Support multiple MPT schema versions (#1621
Browse files Browse the repository at this point in the history
)

Adds the ability to parse different schema versions. This PR doesn't
actually change any functionality, except for switching to the new
handler API. All existing v1 schema code works the same way it did.

Additionally introduced Kotlin to module as new default.
  • Loading branch information
robzienert authored Sep 21, 2017
1 parent d0b3574 commit 59d9b1b
Show file tree
Hide file tree
Showing 21 changed files with 644 additions and 215 deletions.
3 changes: 3 additions & 0 deletions orca-pipelinetemplate/orca-pipelinetemplate.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
apply from: "$rootDir/gradle/kotlin.gradle"
apply from: "$rootDir/gradle/spek.gradle"

tasks.compileGroovy.enabled = false

dependencies {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@
@ConditionalOnExpression("${pipelineTemplates.enabled:true}")
@ComponentScan(
basePackageClasses = PipelineTemplateModule.class,
basePackages = {"com.netflix.spinnaker.orca.pipelinetemplate.tasks", "com.netflix.spinnaker.orca.pipelinetemplate.pipeline"}
basePackages = {
"com.netflix.spinnaker.orca.pipelinetemplate.tasks",
"com.netflix.spinnaker.orca.pipelinetemplate.pipeline",
"com.netflix.spinnaker.orca.pipelinetemplate.handler",
"com.netflix.spinnaker.orca.pipelinetemplate.v1schema.handler"
}
)
public class PipelineTemplateConfiguration {

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* 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

import com.fasterxml.jackson.databind.ObjectMapper
import com.netflix.spectator.api.BasicTag
import com.netflix.spectator.api.Registry
import com.netflix.spinnaker.orca.extensionpoint.pipeline.PipelinePreprocessor
import com.netflix.spinnaker.orca.pipelinetemplate.handler.*
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import javax.annotation.PostConstruct

@Component("pipelineTemplatePreprocessor")
class PipelineTemplatePreprocessor
@Autowired constructor(
private val pipelineTemplateObjectMapper: ObjectMapper,
private val schemaVersionHandler: SchemaVersionHandler,
private val errorHandler: PipelineTemplateErrorHandler,
private val registry: Registry
) : PipelinePreprocessor {

private val log = LoggerFactory.getLogger(javaClass)
private val requestsId = registry.createId("mpt.requests")

@PostConstruct fun confirmUsage() = log.info("Using ${javaClass.simpleName}")

override fun process(pipeline: MutableMap<String, Any>?): MutableMap<String, Any> {
if (pipeline == null) {
return mutableMapOf()
}

val request = pipelineTemplateObjectMapper.convertValue(pipeline, TemplatedPipelineRequest::class.java)
if (!request.isTemplatedPipelineRequest) {
return pipeline
}

log.debug("Starting handler chain")

val chain = DefaultHandlerChain()
val context = GlobalPipelineTemplateContext(chain, request)

chain.add(schemaVersionHandler)

while (!chain.isEmpty()) {
val handler = chain.removeFirst()
try {
handler.handle(chain, context)
} catch (t: Throwable) {
if (handler is PipelineTemplateErrorHandler) {
recordRequest(context, false)
throw IrrecoverableConditionException(t)
}

log.error("Unexpected error occurred while processing template: (requestId: {})", context.getRequestId(), t)
errorHandler.recordThrowable(t)
chain.clear()
}

// Ensure the error handler is always the last thing we run
if (chain.isEmpty() && handler !is PipelineTemplateErrorHandler) {
chain.add(errorHandler)
}
}

recordRequest(context, context.getErrors().hasErrors(false))

log.debug("Handler chain complete")
return context.getProcessedOutput()
}

private fun recordRequest(context: PipelineTemplateContext, success: Boolean) {
registry.counter(requestsId.withTags(listOf(
BasicTag("status", if (success) "success" else "failure"),
BasicTag("schema", context.getRequest().schema ?: "unknown"),
BasicTag("plan", context.getRequest().plan.toString())
))).increment()
}
}

private class IrrecoverableConditionException(t: Throwable) : RuntimeException("Could not recover from an error condition", t)
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,27 @@
*/
package com.netflix.spinnaker.orca.pipelinetemplate;

import com.netflix.spinnaker.orca.pipelinetemplate.v1schema.model.PipelineTemplate;
import com.netflix.spinnaker.orca.pipelinetemplate.v1schema.model.TemplateConfiguration;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.Map;

public class TemplatedPipelineRequest {
String id;
String schema;
String type;
Map<String, Object> trigger;
TemplateConfiguration config;
PipelineTemplate template;
Map<String, Object> config;
Map<String, Object> template;
Boolean plan = false;
boolean limitConcurrent = true;
boolean keepWaitingPipelines = false;

@JsonProperty("config")
private void unpackConfig(Map<String, Object> config) {
this.config = config;
schema = (String) config.get("schema");
}

public String getId() {
return id;
}
Expand All @@ -38,8 +44,12 @@ public void setId(String id) {
this.id = id;
}

public boolean isTemplatedPipelineRequest() {
return "templatedPipeline".equals(type);
public String getSchema() {
return schema;
}

public void setSchema(String schema) {
this.schema = schema;
}

public String getType() {
Expand All @@ -50,12 +60,8 @@ public void setType(String type) {
this.type = type;
}

public TemplateConfiguration getConfig() {
return config;
}

public void setConfig(TemplateConfiguration config) {
this.config = config;
public boolean isTemplatedPipelineRequest() {
return "templatedPipeline".equals(type);
}

public Map<String, Object> getTrigger() {
Expand All @@ -66,11 +72,19 @@ public void setTrigger(Map<String, Object> trigger) {
this.trigger = trigger;
}

public PipelineTemplate getTemplate() {
public Map<String, Object> getConfig() {
return config;
}

public void setConfig(Map<String, Object> config) {
this.config = config;
}

public Map<String, Object> getTemplate() {
return template;
}

public void setTemplate(PipelineTemplate template) {
public void setTemplate(Map<String, Object> template) {
this.template = template;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
*/
package com.netflix.spinnaker.orca.pipelinetemplate.exceptions;

import com.netflix.spinnaker.orca.pipelinetemplate.validator.Errors;
import com.netflix.spinnaker.orca.pipelinetemplate.validator.Errors.Error;

public class IllegalTemplateConfigurationException extends IllegalStateException {
public class IllegalTemplateConfigurationException extends IllegalStateException implements PipelineTemplateException {

private Error error;

Expand All @@ -33,4 +34,9 @@ public IllegalTemplateConfigurationException(Error error) {
public Error getError() {
return error;
}

@Override
public Errors getErrors() {
return new Errors().add(error);
}
}
Loading

0 comments on commit 59d9b1b

Please sign in to comment.