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

feat(artifacts): Simplify expected artifacts #4266

Merged
merged 1 commit into from
Oct 14, 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
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