Skip to content

Commit

Permalink
feat(artifacts): better default artifact behavior (#4817)
Browse files Browse the repository at this point in the history
  • Loading branch information
lwander committed Feb 7, 2018
1 parent 6af1dd5 commit 7391f45
Show file tree
Hide file tree
Showing 13 changed files with 227 additions and 10 deletions.
2 changes: 2 additions & 0 deletions app/scripts/modules/core/src/domain/IArtifactKindConfig.ts
Expand Up @@ -2,6 +2,8 @@ export interface IArtifactKindConfig {
label: string;
description: string;
key: string;
isDefault: boolean;
isMatch: boolean;
template: string;
controller: Function;
controllerAs?: string;
Expand Down
22 changes: 17 additions & 5 deletions app/scripts/modules/core/src/help/help.contents.ts
Expand Up @@ -98,23 +98,35 @@ module(HELP_CONTENTS, [])
the pub/sub -> Spinnaker artifact translation.
</p>
<p>For example, if you want to match against any GCS object, only supply <b>type</b> = gcs/object. If you also want to restrict the matches by other fields, include those as well.</p>
<p>Regex is accepted, so you could for example match on a filepath like so <b>name</b> = .*\\.yaml to match all incoming YAML files.</p>`,
<p>Regex is accepted, so you could for example match on a filepath like so <b>name</b> = .*\\.yaml to match all incoming YAML files.</p>
<p>See the <a href="https://www.spinnaker.io/reference/artifacts/">reference</a> for more information.</p>`,
'pipeline.config.expectedArtifact.ifMissing': `
<p>If no artifact was supplied by your trigger to match against this expected artifact, you have a few options:
<ol>
<li>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>If option 1 fails, or isn't specified, you can provide a default artifact with the required fields to use instead.</li>
<li>Fail the pipeline if options 1 or 2 fail or aren't selected.</li>
</ol>
</p>`,
</p>
<p>See the <a href="https://www.spinnaker.io/reference/artifacts/in-pipelines">reference</a> for more information.</p>`,
'pipeline.config.expectedArtifact.defaultArtifact': `
<p>If your artifact either wasn't supplied from a trigger, or it wasn't found in a prior execution, the artifact specified below will end up in your pipeline's execution context.</p>`,
<p>If your artifact either wasn't supplied from a trigger, or it wasn't found in a prior execution, the artifact specified below will end up in your pipeline's execution context.</p>
<p>See the <a href="https://www.spinnaker.io/reference/artifacts/in-pipelines">reference</a> for more information.</p>`,
'pipeline.config.expectedArtifact.gcs.name': `
<p>The GCS object name, in the form 'gs://bucket/path/to/file.yml'.</p>`,
<p>The GCS object name, in the form <code>gs://bucket/path/to/file.yml</code>.</p>`,
'pipeline.config.expectedArtifact.defaultGcs.reference': `
<p>The GCS object name, <i>optionally</i> appending the version. An example: <code>gs://bucket/file.yml#123948581</code></p>`,
'pipeline.config.expectedArtifact.docker.name': `
<p>The Docker image name you want to trigger on changes to. If you are treating a tag as a release stream, you should include the image tag in the name. An example is 'gcr.io/project/image:stable', where all changes to the 'stable' tag will result in triggering this pipeline.</p>`,
<p>The Docker image name you want to trigger on changes to. By default, this does <i>not</i> include the image tag or digest, only the registry and image repository.</p>`,
'pipeline.config.expectedArtifact.defaultDocker.reference': `
<p>The fully-qualified docker image to deploy. An example: <code>gcr.io/project/image@sha256:59bb771c86</code></p>`,
'pipeline.config.expectedArtifact.git.name': `
<p>The file's path from the git root, in the form 'path/to/file.json'</p>`,
'pipeline.config.expectedArtifact.defaultGithub.version': `
<p>Either the commit or branch to checkout.</p>`,
'pipeline.config.expectedArtifact.defaultGithub.reference': `
<p>The GitHub API content url the artifact lives under. The domain name may change if you're running GHE.</p>
<p>An example is <code>https://api.github.com/repos/$ORG/$REPO/contents/$FILEPATH</code>. See <a href="https://www.spinnaker.io/reference/artifacts/types/github-file/#fields">our docs</a> for more info.</p>`,
'pipeline.config.trigger.webhook.source': `
<p>Determines the target URL required to trigger this pipeline, as well as how the payload can be transformed into artifacts.</p>
`,
Expand Down
Expand Up @@ -50,5 +50,5 @@ <h4>Execution Options</h4>
</div>
</div>
<h4>Match Artifact</h4>
<artifact artifact="ctrl.stage.expectedArtifact.matchArtifact"></artifact>
<artifact is-match artifact="ctrl.stage.expectedArtifact.matchArtifact"></artifact>
</div>
@@ -1,4 +1,5 @@
import {
IAttributes,
ICompileService,
IComponentOptions,
IController,
Expand All @@ -14,17 +15,32 @@ class ArtifactCtrl implements IController {
public artifact: IArtifact;
public options: IArtifactKindConfig[];
public description: string;
private isDefault: boolean;
private isMatch: boolean;

constructor(private pipelineConfig: PipelineConfigProvider,
private $attrs: IAttributes,
private $controller: IControllerService,
private $compile: ICompileService,
private $element: IRootElementService,
private $rootScope: IRootScopeService) {
'ngInject';
if (this.$attrs.$attr.hasOwnProperty('isDefault')) {
this.isDefault = true;
}

if (this.$attrs.$attr.hasOwnProperty('isMatch')) {
this.isMatch = true;
}

this.options = this.pipelineConfig.getArtifactKinds();
this.loadArtifactKind();
}

public getOptions(): IArtifactKindConfig[] {
return this.options.filter(o => o.isDefault === this.isDefault || o.isMatch === this.isMatch);
}

public loadArtifactKind(): void {
const kind = this.artifact.kind;
if (!kind) {
Expand Down Expand Up @@ -65,7 +81,7 @@ class ArtifactComponent implements IComponentOptions {
<select class="input-sm"
required
ng-change="ctrl.loadArtifactKind()"
ng-options="option.key as option.label for option in ctrl.options"
ng-options="option.key as option.label for option in ctrl.getOptions()"
ng-model="ctrl.artifact.kind">
<option style="display:none" value="">Select a kind</option>
</select>
Expand Down
Expand Up @@ -4,6 +4,9 @@ import { EXPECTED_ARTIFACT } from './expectedArtifact.component';
import { CUSTOM_ARTIFACT } from './custom/custom.artifact';
import { GCS_ARTIFACT } from './gcs/gcs.artifact';
import { DOCKER_ARTIFACT } from './docker/docker.artifact';
import { DEFAULT_DOCKER_ARTIFACT } from './docker/defaultDocker.artifact';
import { DEFAULT_GCS_ARTIFACT } from './gcs/defaultGcs.artifact';
import { DEFAULT_GITHUB_ARTIFACT } from './github/defaultGithub.artifact';
import { ARTIFACT } from './artifact.component';
import { GITHUB_ARTIFACT } from 'core/pipeline/config/triggers/artifacts/github/github.artifact';

Expand All @@ -15,5 +18,8 @@ module(ARTIFACT_MODULE, [
GCS_ARTIFACT,
GITHUB_ARTIFACT,
DOCKER_ARTIFACT,
DEFAULT_DOCKER_ARTIFACT,
DEFAULT_GCS_ARTIFACT,
DEFAULT_GITHUB_ARTIFACT,
ARTIFACT,
]);
Expand Up @@ -18,6 +18,8 @@ module(CUSTOM_ARTIFACT, [
label: 'Custom',
description: 'A custom-defined artifact.',
key: 'custom',
isDefault: true,
isMatch: true,
controller(artifact: IArtifact) {
'ngInject';
this.artifact = artifact;
Expand Down
@@ -0,0 +1,62 @@
import { module } from 'angular';

import { PIPELINE_CONFIG_PROVIDER } from 'core/pipeline/config/pipelineConfigProvider';
import { IArtifact } from 'core/domain/IArtifact';
import { PipelineConfigProvider } from 'core/pipeline';
import { isNil } from 'lodash';

export const DEFAULT_DOCKER_ARTIFACT = 'spinnaker.core.pipeline.trigger.artifact.defaultDocker';
module(DEFAULT_DOCKER_ARTIFACT, [
PIPELINE_CONFIG_PROVIDER,
]).config((pipelineConfigProvider: PipelineConfigProvider) => {
pipelineConfigProvider.registerArtifactKind({
label: 'Docker',
isDefault: true,
isMatch: false,
description: 'A Docker image to be deployed.',
key: 'default.docker',
controller(artifact: IArtifact) {
'ngInject';
this.artifact = artifact;
this.artifact.type = 'docker/image';

this.onReferenceChange = () => {
const ref = this.artifact.reference;
if (isNil(ref)) {
return;
}

if (ref.indexOf('@') >= 0) {
const split = ref.split('@');
this.artifact.name = split[0];
this.artifact.version = split[1];
} else if (ref.indexOf(':') >= 0) {
const split = ref.split(':');
this.artifact.name = split[0];
this.artifact.version = split[1];
} else {
this.artifact.name = ref;
}
};
},
controllerAs: 'ctrl',
template: `
<div class="col-md-12">
<div class="form-group row">
<label class="col-md-2 sm-label-right">
Docker image
<help-field key="pipeline.config.expectedArtifact.defaultDocker.reference"></help-field>
</label>
<div class="col-md-8">
<input type="text"
placeholder="gcr.io/project/image@sha256:9efcc2818c9..."
class="form-control input-sm"
ng-change="ctrl.onReferenceChange()"
ng-model="ctrl.artifact.reference"/>
</div>
</div>
</div>
`,
});
});

Expand Up @@ -10,6 +10,8 @@ module(DOCKER_ARTIFACT, [
]).config((pipelineConfigProvider: PipelineConfigProvider) => {
pipelineConfigProvider.registerArtifactKind({
label: 'Docker',
isDefault: false,
isMatch: true,
description: 'A Docker image to be deployed.',
key: 'docker',
controller(artifact: IArtifact) {
Expand All @@ -27,7 +29,7 @@ module(DOCKER_ARTIFACT, [
</label>
<div class="col-md-8">
<input type="text"
placeholder="gcr.io/project/image:release"
placeholder="gcr.io/project/image"
class="form-control input-sm"
ng-model="ctrl.artifact.name"/>
</div>
Expand Down
Expand Up @@ -42,7 +42,7 @@ class ExpectedArtifactComponent implements IComponentOptions {
</button>
</div>
</div>
<artifact artifact="ctrl.expectedArtifact.matchArtifact"></artifact>
<artifact is-match artifact="ctrl.expectedArtifact.matchArtifact"></artifact>
If missing
<help-field key="pipeline.config.expectedArtifact.ifMissing"></help-field>
<div class="form-group row">
Expand All @@ -65,7 +65,7 @@ class ExpectedArtifactComponent implements IComponentOptions {
</div>
</div>
<div class="form-group row">
<artifact artifact="ctrl.expectedArtifact.defaultArtifact"></artifact>
<artifact is-default artifact="ctrl.expectedArtifact.defaultArtifact"></artifact>
</div>
</div>
</div>
Expand Down
@@ -0,0 +1,58 @@
import { module } from 'angular';

import { PIPELINE_CONFIG_PROVIDER } from 'core/pipeline/config/pipelineConfigProvider';
import { IArtifact } from 'core/domain/IArtifact';
import { PipelineConfigProvider } from 'core/pipeline';
import { isNil } from 'lodash';

export const DEFAULT_GCS_ARTIFACT = 'spinnaker.core.pipeline.trigger.artifact.defaultGcs';
module(DEFAULT_GCS_ARTIFACT, [
PIPELINE_CONFIG_PROVIDER,
]).config((pipelineConfigProvider: PipelineConfigProvider) => {
pipelineConfigProvider.registerArtifactKind({
label: 'GCS',
description: 'A GCS object.',
key: 'default.gcs',
isDefault: true,
isMatch: false,
controller(artifact: IArtifact) {
'ngInject';
this.artifact = artifact;
this.artifact.type = 'gcs/object';

this.onReferenceChange = () => {
const ref = this.artifact.reference;
if (isNil(ref)) {
return;
}

if (ref.indexOf('#') >= 0) {
const split = ref.split('#');
this.artifact.name = split[0];
this.artifact.version = split[1];
} else {
this.artifact.name = ref;
}
};
},
controllerAs: 'ctrl',
template: `
<div class="col-md-12">
<div class="form-group row">
<label class="col-md-2 sm-label-right">
Object path
<help-field key="pipeline.config.expectedArtifact.defaultGcs.reference"></help-field>
</label>
<div class="col-md-8">
<input type="text"
placeholder="gs://bucket/path/to/file"
class="form-control input-sm"
ng-change="ctrl.onReferenceChange()"
ng-model="ctrl.artifact.reference"/>
</div>
</div>
</div>
`,
});
});

Expand Up @@ -12,6 +12,8 @@ module(GCS_ARTIFACT, [
label: 'GCS',
description: 'A GCS object.',
key: 'gcs',
isDefault: false,
isMatch: true,
controller(artifact: IArtifact) {
'ngInject';
this.artifact = artifact;
Expand Down
@@ -0,0 +1,53 @@
import { module } from 'angular';

import { PIPELINE_CONFIG_PROVIDER } from 'core/pipeline/config/pipelineConfigProvider';
import { IArtifact } from 'core/domain/IArtifact';
import { PipelineConfigProvider } from 'core/pipeline';

export const DEFAULT_GITHUB_ARTIFACT = 'spinnaker.core.pipeline.trigger.artifact.defaultGithub';
module(DEFAULT_GITHUB_ARTIFACT, [
PIPELINE_CONFIG_PROVIDER,
]).config((pipelineConfigProvider: PipelineConfigProvider) => {
pipelineConfigProvider.registerArtifactKind({
label: 'GitHub',
description: 'A file stored in git, hosted by GitHub.',
key: 'default.github',
isDefault: true,
isMatch: false,
controller(artifact: IArtifact) {
'ngInject';
this.artifact = artifact;
this.artifact.type = 'github/file';
},
controllerAs: 'ctrl',
template: `
<div class="col-md-12">
<div class="form-group row">
<label class="col-md-3 sm-label-right">
Content URL
<help-field key="pipeline.config.expectedArtifact.defaultGithub.reference"></help-field>
</label>
<div class="col-md-8">
<input type="text"
placeholder="https://api.github.com/repos/$ORG/$REPO/contents/$FILEPATH"
class="form-control input-sm"
ng-model="ctrl.artifact.reference"/>
</div>
</div>
<div class="form-group row">
<label class="col-md-3 sm-label-right">
Commit/Branch
<help-field key="pipeline.config.expectedArtifact.defaultGithub.version"></help-field>
</label>
<div class="col-md-3">
<input type="text"
placeholder="master"
class="form-control input-sm"
ng-model="ctrl.artifact.version"/>
</div>
</div>
</div>
`,
});
});

Expand Up @@ -12,6 +12,8 @@ module(GITHUB_ARTIFACT, [
label: 'GitHub',
description: 'A file stored in git, hosted by GitHub.',
key: 'github',
isDefault: false,
isMatch: true,
controller(artifact: IArtifact) {
'ngInject';
this.artifact = artifact;
Expand Down

0 comments on commit 7391f45

Please sign in to comment.