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(pipeline_template): config level stage replacement #1622

Merged
merged 1 commit into from
Sep 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.netflix.spinnaker.orca.pipelinetemplate.v1schema.V1SchemaExecutionGenerator;
import com.netflix.spinnaker.orca.pipelinetemplate.v1schema.graph.GraphMutator;
import com.netflix.spinnaker.orca.pipelinetemplate.v1schema.model.PipelineTemplate;
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.render.DefaultRenderContext;
import com.netflix.spinnaker.orca.pipelinetemplate.v1schema.render.RenderContext;
Expand All @@ -44,6 +45,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* highlevel lifecycle
Expand Down Expand Up @@ -100,13 +102,20 @@ private Map<String, Object> processInternal(Map<String, Object> pipeline) {
Errors validationErrors = new Errors();

TemplateConfiguration templateConfiguration = request.getConfig();
new V1TemplateConfigurationSchemaValidator().validate(templateConfiguration, validationErrors);

PipelineTemplate template = getPipelineTemplate(request, templateConfiguration);

new V1TemplateConfigurationSchemaValidator().validate(
templateConfiguration,
validationErrors,
new V1TemplateConfigurationSchemaValidator.SchemaValidatorContext(
template.getStages().stream().map(StageDefinition::getId).collect(Collectors.toList())
)
);
if (validationErrors.hasErrors(request.plan)) {
return validationErrors.toResponse();
}

PipelineTemplate template = getPipelineTemplate(request, templateConfiguration);

new V1TemplateSchemaValidator().validate(template, validationErrors, new SchemaValidatorContext(!templateConfiguration.getStages().isEmpty()));
if (validationErrors.hasErrors(request.plan)) {
return validationErrors.toResponse();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,14 @@
import com.netflix.spinnaker.orca.pipelinetemplate.validator.ValidatorContext;
import com.netflix.spinnaker.orca.pipelinetemplate.validator.VersionedSchema;

public class V1TemplateConfigurationSchemaValidator implements SchemaValidator {
import java.util.List;

private static final String SUPPORTED_VERSION = "1";
public class V1TemplateConfigurationSchemaValidator<T extends V1TemplateConfigurationSchemaValidator.SchemaValidatorContext> implements SchemaValidator<T> {

public void validate(VersionedSchema configuration, Errors errors) {
validate(configuration, errors, new EmptyValidatorContext());
}
private static final String SUPPORTED_VERSION = "1";

@Override
public void validate(VersionedSchema configuration, Errors errors, ValidatorContext context) {
public void validate(VersionedSchema configuration, Errors errors, SchemaValidatorContext context) {
if (!(configuration instanceof TemplateConfiguration)) {
throw new IllegalArgumentException("Expected TemplateConfiguration");
}
Expand Down Expand Up @@ -63,7 +61,7 @@ public void validate(VersionedSchema configuration, Errors errors, ValidatorCont
V1SchemaValidationHelper.validateStageDefinitions(config.getStages(), errors, V1TemplateConfigurationSchemaValidator::location);

config.getStages().forEach(s -> {
if ((s.getDependsOn() == null || s.getDependsOn().isEmpty()) && (s.getInject() == null || !s.getInject().hasAny())) {
if (!context.stageIds.contains(s.getId()) && (s.getDependsOn() == null || s.getDependsOn().isEmpty()) && (s.getInject() == null || !s.getInject().hasAny())) {
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 @@ -77,4 +75,12 @@ public void validate(VersionedSchema configuration, Errors errors, ValidatorCont
private static String location(String location) {
return "configuration:" + location;
}

public static class SchemaValidatorContext implements ValidatorContext {
List<String> stageIds;

public SchemaValidatorContext(List<String> stageIds) {
this.stageIds = stageIds;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class V1TemplateConfigurationSchemaValidatorSpec extends Specification {
def templateConfiguration = new TemplateConfiguration(schema: schema)

when:
subject.validate(templateConfiguration, errors)
subject.validate(templateConfiguration, errors, new V1TemplateConfigurationSchemaValidator.SchemaValidatorContext([]))

then:
if (hasErrors) {
Expand All @@ -56,7 +56,7 @@ class V1TemplateConfigurationSchemaValidatorSpec extends Specification {
def templateConfiguration = new TemplateConfiguration(schema: "1", pipeline: new PipelineDefinition(application: application))

when:
subject.validate(templateConfiguration, errors)
subject.validate(templateConfiguration, errors, new V1TemplateConfigurationSchemaValidator.SchemaValidatorContext([]))

then:
if (hasErrors) {
Expand Down Expand Up @@ -89,7 +89,7 @@ class V1TemplateConfigurationSchemaValidatorSpec extends Specification {
)

when:
subject.validate(templateConfiguration, errors)
subject.validate(templateConfiguration, errors, new V1TemplateConfigurationSchemaValidator.SchemaValidatorContext(templateStageIds))

then:
if (hasErrors) {
Expand All @@ -100,9 +100,10 @@ class V1TemplateConfigurationSchemaValidatorSpec extends Specification {
}

where:
dependsOn | injectFirst | hasErrors
null | true | false
['bar'] | false | false
null | null | true
dependsOn | injectFirst | templateStageIds | hasErrors
null | true | [] | false
['bar'] | false | [] | false
null | null | [] | true
null | null | ["foo"] | false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
schema: "1"
pipeline:
application: orca
name: MPT Stage Replacement Test
template:
source: stageReplacement-template.yml
variables:
myCustomFirstStageName: "HELLO ROB"
stages:
- id: bake1
type: findImageFromTags
name: "{{ myCustomFirstStageName }}"
config:
cloudProvider: aws
cloudProviderType: aws
packageName: "{{ application }}"
regions:
- us-east-1
- us-west-1
- us-west-2
- eu-west-1
tags:
stack: test
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
{
"id": "unknown",
"keepWaitingPipelines": false,
"limitConcurrent": true,
"application": "orca",
"name": "MPT Stage Replacement Test",
"notifications": [],
"stages": [
{
"cloudProvider": "aws",
"cloudProviderType": "aws",
"name": "HELLO ROB",
"packageName": "orca",
"refId": "bake1",
"regions": [
"us-east-1",
"us-west-1",
"us-west-2",
"eu-west-1"
],
"tags": {
"stack": "test"
},
"requisiteStageRefIds": [],
"type": "findImageFromTags",
"id": null
},
{
"id": null,
"clusters": [
{
"account": "test",
"application": "spindemo",
"availabilityZones": {
"us-west-1": [
"us-west-1a",
"us-west-1c"
]
},
"capacity": {
"desired": 1,
"max": 1,
"min": 1
},
"cloudProvider": "aws",
"cooldown": 10,
"copySourceCustomBlockDeviceMappings": true,
"ebsOptimized": false,
"enabledMetrics": [],
"freeFormDetails": "demo",
"healthCheckGracePeriod": 600,
"healthCheckType": "EC2",
"iamRole": "myIAMRole",
"instanceMonitoring": false,
"instanceType": "m3.large",
"interestingHealthProviderNames": [
"Amazon"
],
"keyPair": "keypair",
"loadBalancers": [
"spindemo-demo-frontend"
],
"maxRemainingAsgs": 2,
"preferSourceCapacity": true,
"provider": "aws",
"scaleDown": true,
"securityGroups": [],
"stack": "test",
"strategy": "redblack",
"subnetType": "mySubnet",
"suspendedProcesses": [],
"tags": {},
"targetGroups": [],
"targetHealthyDeployPercentage": 100,
"terminationPolicies": [
"Default"
],
"useAmiBlockDeviceMappings": false,
"useSourceCapacity": true
},
{
"account": "test",
"application": "spindemo",
"availabilityZones": {
"us-east-1": [
"us-east-1c",
"us-east-1d",
"us-east-1e"
]
},
"capacity": {
"desired": 0,
"max": 0,
"min": 0
},
"cloudProvider": "aws",
"cooldown": 10,
"ebsOptimized": false,
"freeFormDetails": "demo",
"healthCheckGracePeriod": 600,
"healthCheckType": "EC2",
"iamRole": "myIAMRole",
"instanceMonitoring": false,
"instanceType": "m3.large",
"interestingHealthProviderNames": [
"Amazon"
],
"keyPair": "keypair",
"provider": "aws",
"securityGroups": [],
"stack": "test",
"strategy": "highlander",
"subnetType": "mySubnet",
"suspendedProcesses": [],
"tags": {},
"targetHealthyDeployPercentage": 100,
"terminationPolicies": [
"Default"
],
"useSourceCapacity": false
}
],
"name": "Deploy",
"refId": "deploy2",
"requisiteStageRefIds": [
"bake1"
],
"type": "deploy"
}
],
"parameterConfig": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
schema: "1"
id: stageReplacement
metadata:
name: Stage Replacement
description: Example for replacing a stage
variables:
- name: myCustomFirstStageName
stages:
- id: bake1
type: bake
name: "{{ myCustomFirstStageName }}"
config:
baseLabel: release
baseOs: trusty
cloudProviderType: aws
enhancedNetworking: false
extendedAttributes: {}
overrideTimeout: true
package: orca
regions:
- us-east-1
- us-west-1
- us-west-2
- eu-west-1
sendNotifications: true
showAdvancedOptions: true
stageTimeoutMs: 900000
storeType: ebs
user: example@example.com
vmType: hvm
- id: deploy2
type: deploy
dependsOn:
- bake1
name: Deploy
config:
clusters:
- account: test
application: spindemo
availabilityZones:
us-west-1:
- us-west-1a
- us-west-1c
capacity:
desired: 1
max: 1
min: 1
cloudProvider: aws
cooldown: 10
copySourceCustomBlockDeviceMappings: true
ebsOptimized: false
enabledMetrics: []
freeFormDetails: demo
healthCheckGracePeriod: 600
healthCheckType: EC2
iamRole: myIAMRole
instanceMonitoring: false
instanceType: m3.large
interestingHealthProviderNames:
- Amazon
keyPair: keypair
loadBalancers:
- spindemo-demo-frontend
maxRemainingAsgs: 2
preferSourceCapacity: true
provider: aws
scaleDown: true
securityGroups: []
stack: test
strategy: redblack
subnetType: mySubnet
suspendedProcesses: []
tags: {}
targetGroups: []
targetHealthyDeployPercentage: 100
terminationPolicies:
- Default
useAmiBlockDeviceMappings: false
useSourceCapacity: true
- account: test
application: spindemo
availabilityZones:
us-east-1:
- us-east-1c
- us-east-1d
- us-east-1e
capacity:
desired: 0
max: 0
min: 0
cloudProvider: aws
cooldown: 10
ebsOptimized: false
freeFormDetails: demo
healthCheckGracePeriod: 600
healthCheckType: EC2
iamRole: myIAMRole
instanceMonitoring: false
instanceType: m3.large
interestingHealthProviderNames:
- Amazon
keyPair: keypair
provider: aws
securityGroups: []
stack: test
strategy: highlander
subnetType: mySubnet
suspendedProcesses: []
tags: {}
targetHealthyDeployPercentage: 100
terminationPolicies:
- Default
useSourceCapacity: false