Skip to content

Commit

Permalink
feat(artifacts): Simplify expected artifacts (#4266)
Browse files Browse the repository at this point in the history
  • Loading branch information
lwander authored Oct 14, 2017
1 parent 0818340 commit 1ce9206
Show file tree
Hide file tree
Showing 15 changed files with 172 additions and 184 deletions.
11 changes: 11 additions & 0 deletions app/scripts/modules/core/src/domain/IArtifact.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface IArtifact {
type: string;
name: string;
version: string;
location: string;
reference: string;
metadata: any;
artifactAccount: string;
provenance: string;
uuid: string;
}
4 changes: 2 additions & 2 deletions app/scripts/modules/core/src/domain/IBuild.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export interface IArtifact {
export interface IBuildArtifact {
displayPath: string;
fileName: string;
relativePath: string;
Expand All @@ -12,5 +12,5 @@ export interface IBuild {
result: string;
timestamp: Date;
url: string;
artifacts: IArtifact[];
artifacts: IBuildArtifact[];
}
24 changes: 5 additions & 19 deletions app/scripts/modules/core/src/domain/IExpectedArtifact.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,8 @@
export interface IExpectedArtifact {
fields: IArtifactField[];
}

export interface IArtifactField {
fieldName: string;
fieldType: FieldType;
value?: string;
missingPolicy?: MissingArtifactPolicy;
expression?: string;
}
import { IArtifact } from 'core/domain/IArtifact';

export enum FieldType {
MustMatch = 'MUST_MATCH',
FindIfMissing = 'FIND_IF_MISSING'
export interface IExpectedArtifact {
matchArtifact: IArtifact;
usePriorArtifact: boolean;
defaultArtifact: IArtifact;
}

export enum MissingArtifactPolicy {
FailPipeline = 'FAIL_PIPELINE',
Ignore = 'IGNORE',
PriorPipeline = 'PRIOR_PIPELINE'
}
2 changes: 2 additions & 0 deletions app/scripts/modules/core/src/domain/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export * from '../search/searchResult/model/IApplicationSearchResult';

export * from './IArtifact'

export * from './IBuild';
export * from './IBuildDiffInfo';
export * from './IBuildInfo';
Expand Down
22 changes: 11 additions & 11 deletions app/scripts/modules/core/src/help/help.contents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,17 +85,17 @@ module(HELP_CONTENTS, [])
the configuration based on the newest server group in the cluster.</p>
<p>If you want to start from scratch, select "None".</p>
<p>You can always edit the cluster configuration after you've created it.</p>`,
'pipeline.config.expectedArtifact.fieldName': `
<p>The Artifact field name.</p>`,
'pipeline.config.expectedArtifact.fieldType': `
<p>The Artifact field type. This must be one of 'MUST_MATCH' or 'FIND_IF_MISSING'.</p>
<p><strong>MUST_MATCH</strong> means the incoming Artifact's field value must match exactly with the expected value.</p>
<p><strong>FIND_IF_MISSING</strong> means the field is optional and a 'Missing Policy' will fire if the value is not present.</p>`,
'pipeline.config.expectedArtifact.missingPolicy': `
<p>The policy if the field type is 'FIND_IF_MISSING' and the field value is not present.</p>
<p><strong>FAIL_PIPELINE</strong> fails the pipeline if the field value is missing.</p>
<p><strong>IGNORE</strong> ignores the fact that the value is missing and runs the pipeline anyway.</p>
<p><strong>PRIOR_PIPELINE</strong> looks up the field's value from a prior pipeline execution.</p>`,
'pipeline.config.expectedArtifact.custom.matchArtifact': `
<p>This specifies which fields in your incoming artifact to match against. Every field that you supply will be used to match against all incoming artifacts. If all fields specified match, the incoming artifact is bound to your pipeline context.</p>
<p>For example, if you want to match against any GCS object, only supply <b>type</b>=<b>gcs/object</b>. If you also want to restrict the matches by other fields, include those as well.</p>`,
'pipeline.config.expectedArtifact.custom.ifMissing': `
<p>If no artifact was supplied by your trigger to match against this expected artifact, you have a few options:
<ul>
<li>1. Attempt to match against an artifact in the prior pipeline execution's context. This ensures that you will always be using the most recently supplied artifact to this pipeline, and is generally a safe choice.</li>
<li>2. If option 1 fails, or isn't specified, you can provide a default artifact with all required fields specified to use instead.</li>
<li>3. Fail the pipeline if options 1 or 2 fail or aren't selected.</li>
</ul>
</p>`,
'loadBalancer.advancedSettings.healthTimeout': '<p>Configures the timeout, in seconds, for reaching the healthCheck target. Must be less than the interval.</p><p> Default: <b>5</b></p>',
'loadBalancer.advancedSettings.healthInterval': '<p>Configures the interval, in seconds, between ELB health checks. Must be greater than the timeout.</p><p>Default: <b>10</b></p>',
'loadBalancer.advancedSettings.healthyThreshold': '<p>Configures the number of healthy observations before reinstituting an instance into the ELB’s traffic rotation.</p><p>Default: <b>10</b></p>',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ triggers {
margin-top: 25px;
margin-bottom: 10px;
}
trigger, artifact {
trigger, expected-artifact {
display: block;
padding: 10px 20px;
background-color: var(--color-white);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,64 +1,17 @@
import { IComponentController, IComponentOptions, module } from 'angular';

import { PIPELINE_CONFIG_PROVIDER } from 'core/pipeline/config/pipelineConfigProvider';
import { FieldType, IArtifactField, IExpectedArtifact, MissingArtifactPolicy } from 'core/domain/IExpectedArtifact';
import { IPipeline } from 'core/domain/IPipeline';
import { IArtifact } from 'core/domain/IArtifact';

class ArtifactController implements IComponentController {
public artifact: IExpectedArtifact;
public pipeline: IPipeline;
public missingPolicies: string[] = Object.keys(MissingArtifactPolicy).map(key => MissingArtifactPolicy[key as any]);
public fieldNames: string[] = ['type', 'name', 'version', 'location', 'reference', 'artifactAccount', 'provenance', 'uuid'];
public fieldTypes: string[] = Object.keys(FieldType).map(key => FieldType[key as any]);

public removeArtifact(): void {
const artifactIndex = this.pipeline.expectedArtifacts.indexOf(this.artifact);
this.pipeline.expectedArtifacts.splice(artifactIndex, 1);

this.pipeline.triggers
.forEach(t => t.expectedArtifacts = t.expectedArtifacts.filter(a => !this.equals(a, this.artifact)));
}

public addField(): void {
const newField: IArtifactField = {
fieldName: '',
fieldType: FieldType.MustMatch,
value: '',
missingPolicy: MissingArtifactPolicy.FailPipeline
};

if (!this.artifact.fields) {
this.artifact.fields = [];
}
this.artifact.fields.push(newField);
}

public removeField(fieldIndex: number): void {
this.artifact.fields.splice(fieldIndex, 1);
}

private equals(first: IExpectedArtifact, other: IExpectedArtifact): boolean {
const fieldIsEqual = (firstField: IArtifactField, otherField: IArtifactField): boolean => {
return firstField.fieldName === otherField.fieldName
&& firstField.fieldType === otherField.fieldType
&& firstField.value === otherField.value
&& firstField.missingPolicy === otherField.missingPolicy
&& firstField.expression === otherField.expression;
};
if (first.fields.length !== other.fields.length) {
return false;
}

return first.fields.every(firstField => {
return other.fields.some(otherField => fieldIsEqual(firstField, otherField));
});
}
public artifact: IArtifact;
}

class ArtifactComponent implements IComponentOptions {
public bindings = { artifact: '<', pipeline: '<' };
public bindings = { artifact: '=' };
public templateUrl = require('./artifact.html');
public controller = ArtifactController;
public controllerAs = 'ctrl';
}

export const ARTIFACT = 'spinnaker.core.pipeline.trigger.artifact';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,87 +1,47 @@
<div class="row">
<div class="col-md-12">
<div class="form-horizontal panel-pipeline-phase">
<div class="row" ng-repeat="field in $ctrl.artifact.fields">
<div class="form-group row">
<div class="col-md-12">
<div class="form-group row">
<label class="col-md-3 sm-label-right">
Field Name
<help-field key="pipeline.config.expectedArtifact.fieldName"></help-field>
</label>
<div class="col-md-3">
<select class="input-sm"
required
ng-options="fieldName for fieldName in $ctrl.fieldNames"
ng-model="field.fieldName">
<option value="">Select...</option>
</select>
</div>
<label class="col-md-2 sm-label-right">
Field Type
<help-field key="pipeline.config.expectedArtifact.fieldType"></help-field>
</label>
<div class="col-md-3">
<select class="input-sm"
required
ng-options="fieldType for fieldType in $ctrl.fieldTypes"
ng-model="field.fieldType">
<option value="">Select...</option>
</select>
<button class="btn-link" ng-click="$ctrl.removeField($index)">
<span class="glyphicon glyphicon-trash" uib-tooltip="Delete Field"></span><span class="sr-only">Delete Field</span>
</button>
</div>
</div>
</div>
<div class="col-md-12">
<div class="form-group row">
<label class="col-md-3 sm-label-right">
Field Value
</label>
<div class="col-md-3">
<input type="text"
required
class="form-control input-sm"
ng-model="field.value"/>
</div>
<div ng-if="field.fieldType === 'FIND_IF_MISSING'">
<label class="col-md-2 sm-label-right">
Missing Policy
<help-field key="pipeline.config.expectedArtifact.missingPolicy"></help-field>
</label>
<div class="col-md-3">
<select class="input-sm"
required
ng-options="policy for policy in $ctrl.missingPolicies"
ng-model="field.missingPolicy">
<option value="">Select...</option>
</select>
</div>
</div>
</div>
</div>
</div>
<hr/>
</div>
<div class="row" ng-if="!$ctrl.artifact.fields.length">
<p class="col-md-12">
You don't have any fields declared for this artifact.
</p>
</div>
<div class="form-group row">
<div class="col-md-12">
<button class="btn btn-block btn-add-trigger add-new" ng-click="$ctrl.addField()">
<span class="glyphicon glyphicon-plus-sign"></span> Add Field
</button>
</div>
</div>
<div class="col-md-2 text-right">
<button class="btn btn-sm btn-default" ng-click="$ctrl.removeArtifact()">
<span class="glyphicon glyphicon-trash" uib-tooltip="Remove artifact"></span>
<span class="visible-xl-inline">Remove artifact</span>
</button>
</div>
<div class="col-md-12">
<div class="form-group row">
<label class="col-md-2 sm-label-right">
Type
</label>
<div class="col-md-3">
<input type="text"
class="form-control input-sm"
ng-model="ctrl.artifact.type"/>
</div>
<label class="col-md-2 sm-label-right">
Name
</label>
<div class="col-md-3">
<input type="text"
class="form-control input-sm"
ng-model="ctrl.artifact.name"/>
</div>
</div>
<div class="form-group row">
<label class="col-md-2 sm-label-right">
Version
</label>
<div class="col-md-3">
<input type="text"
class="form-control input-sm"
ng-model="ctrl.artifact.version"/>
</div>
<label class="col-md-2 sm-label-right">
Location
</label>
<div class="col-md-3">
<input type="text"
class="form-control input-sm"
ng-model="ctrl.artifact.location"/>
</div>
</div>
<div class="form-group row">
<label class="col-md-2 sm-label-right">
Reference
</label>
<div class="col-md-8">
<input type="text"
class="form-control input-sm"
ng-model="ctrl.artifact.reference"/>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { equals, IComponentController, IComponentOptions, module } from 'angular';

import { PIPELINE_CONFIG_PROVIDER } from 'core/pipeline/config/pipelineConfigProvider';
import { IExpectedArtifact } from 'core/domain/IExpectedArtifact';
import { IPipeline } from 'core/domain/IPipeline';

class ExpectedArtifactController implements IComponentController {
public expectedArtifact: IExpectedArtifact;
public pipeline: IPipeline;

private static expectedArtifactEquals(first: IExpectedArtifact, other: IExpectedArtifact): boolean {
// Interesting to point out that if two artifact's match artifacts equal, they match the same artifacts & are effectively equal
return equals(first.matchArtifact, other.matchArtifact);
}

public removeExpectedArtifact(): void {
this.pipeline.expectedArtifacts = this.pipeline.expectedArtifacts
.filter(a => !ExpectedArtifactController.expectedArtifactEquals(a, this.expectedArtifact));

this.pipeline.triggers
.forEach(t => t.expectedArtifacts = t.expectedArtifacts
.filter(a => !ExpectedArtifactController.expectedArtifactEquals(a, this.expectedArtifact)));
}
}

class ExpectedArtifactComponent implements IComponentOptions {
public bindings = { expectedArtifact: '=', pipeline: '=' };
public templateUrl = require('./expectedArtifact.html');
public controller = ExpectedArtifactController;
public controllerAs = 'ctrl';
}

export const EXPECTED_ARTIFACT = 'spinnaker.core.pipeline.trigger.expected.artifact';
module(EXPECTED_ARTIFACT, [
PIPELINE_CONFIG_PROVIDER,
]).component('expectedArtifact', new ExpectedArtifactComponent());
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<div class="row">
<div class="col-md-12">
<div class="form-horizontal panel-pipeline-phase">
<div class="form-group row">
<div class="col-md-3">
Match against
<help-field key="pipeline.config.expectedArtifact.custom.matchArtifact"></help-field>
</div>
<div class="col-md-2 col-md-offset-7">
<button class="btn btn-sm btn-default" ng-click="ctrl.removeExpectedArtifact()">
<span class="glyphicon glyphicon-trash" uib-tooltip="Remove expected artifact"></span>
<span class="visible-xl-inline">Remove artifact</span>
</button>
</div>
</div>
<artifact artifact="ctrl.expectedArtifact.matchArtifact"></artifact>
If missing
<help-field key="pipeline.config.expectedArtifact.custom.ifMissing"></help-field>
<div class="form-group row">
<label class="col-md-3 sm-label-right">
Use Prior Execution
</label>
<input class="col-md-1" type="checkbox" ng-model="ctrl.expectedArtifact.usePriorExecution">
</div>
<div class="form-group row">
<label class="col-md-3 sm-label-right">
Use Default Artifact
</label>
<input class="col-md-1" type="checkbox" ng-model="ctrl.expectedArtifact.useDefaultArtifact">
</div>
<div ng-show="ctrl.expectedArtifact.useDefaultArtifact">
<div class="form-group row">
<div class="col-md-3">
Default artifact
</div>
</div>
<div class="form-group row">
<artifact artifact="ctrl.expectedArtifact.defaultArtifact"></artifact>
</div>
</div>
</div>
</div>
</div>
Loading

0 comments on commit 1ce9206

Please sign in to comment.