@spinnaker/core 0.22.2 → 0.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,16 @@
1
+ import type { IController } from 'angular';
2
+ export interface IManagedImageOption {
3
+ id: string;
4
+ name: string;
5
+ osType: string;
6
+ }
7
+ export declare class BakeStageChooseManagedImageController implements IController {
8
+ model: any;
9
+ managedImageOptions: IManagedImageOption[];
10
+ onChange: () => any;
11
+ $onChanges(): void;
12
+ getManagedImageDescription(managedImageOption: IManagedImageOption): string;
13
+ getManagedImageDetailedDescription(managedImageOption: IManagedImageOption): string;
14
+ getManagedImageDisabled(managedImageOption: IManagedImageOption): boolean;
15
+ }
16
+ export declare const PIPELINE_BAKE_STAGE_CHOOSE_MANAGED_IMAGE = "spinnaker.core.pipeline.bake.chooseManagedImage.component";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@spinnaker/core",
3
3
  "license": "Apache-2.0",
4
- "version": "0.22.2",
4
+ "version": "0.23.1",
5
5
  "module": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
7
7
  "scripts": {
@@ -120,5 +120,5 @@
120
120
  "shx": "0.3.3",
121
121
  "typescript": "4.3.5"
122
122
  },
123
- "gitHead": "587b5a7a5fe2121cd93bed86e5e83795ee401be0"
123
+ "gitHead": "8abafe5c6a6680953aef7752b1a56cac093ec9ba"
124
124
  }
@@ -298,10 +298,12 @@ const helpContents: { [key: string]: string } = {
298
298
  '<p>Explicitly evaluate SpEL expressions in overrides just prior to manifest baking. Can be paired with the "Skip SpEL evaluation" option in the Deploy Manifest stage when baking a third-party manifest artifact with expressions not meant for Spinnaker to evaluate as SpEL.</p>',
299
299
  'pipeline.config.bake.manifest.templateRenderer': '<p>This is the engine used for rendering your manifest.</p>',
300
300
  'pipeline.config.bake.manifest.helm.chartFilePath': `
301
- <p>This is the relative path to the Chart.yaml file within your Git repo.</p>
302
- <p>e.g.: <b>helm/my-chart/Chart.yaml</b></p>`,
301
+ <p>This is the relative path to the directory containing the Chart.yaml file within your Git repo.</p>
302
+ <p>e.g.: <b>helm/my-chart</b></p>`,
303
303
  'pipeline.config.bake.manifest.helm.rawOverrides':
304
304
  'Use <i>--set</i> instead of <i>--set-string</i> when injecting override values. Values injected using <i>--set</i> will be converted to primitive types by Helm.',
305
+ 'pipeline.config.bake.manifest.helm.includeCRDs':
306
+ '<p>Include Custom Resource Definitions in the templated output.</p>',
305
307
  'pipeline.config.bake.manifest.kustomize.filePath': `
306
308
  <p>This is the relative path to the kustomization.yaml file within your Git repo.</p>
307
309
  <p>e.g.: <b>examples/wordpress/mysql/kustomization.yaml</b></p>`,
@@ -468,6 +470,8 @@ const helpContents: { [key: string]: string } = {
468
470
  Typing into this verification field is annoying! But it serves as a reminder that you are
469
471
  changing something in an account deemed important, and prevents you from accidentally changing something
470
472
  when you meant to click on the "Cancel" button.`,
473
+ 'pipeline.skipDownstreamOutput':
474
+ 'when checked, the output of the child pipeline is not added to the pipeline context',
471
475
  'pipeline.waitForCompletion':
472
476
  'if unchecked, marks the stage as successful right away without waiting for the pipeline to complete',
473
477
  'jenkins.waitForCompletion':
@@ -117,15 +117,22 @@ export class PipelineGraph extends React.Component<IPipelineGraphProps, IPipelin
117
117
  if (!node.children.length) {
118
118
  return node.phase;
119
119
  }
120
+ const checkedNodeIds = new Set<string | number>();
120
121
  const result: number[] = [];
121
- this.collect(node.children, result);
122
+ this.collect(node.children, result, checkedNodeIds);
122
123
  return max(result);
123
124
  }
124
125
 
125
- private collect(nodes: IPipelineGraphNode[], result: number[]) {
126
+ private collect(nodes: IPipelineGraphNode[], result: number[], checkedNodeIds: Set<string | number>) {
126
127
  nodes.forEach((node) => {
128
+ if (checkedNodeIds.has(node.id)) {
129
+ return;
130
+ } else {
131
+ checkedNodeIds.add(node.id);
132
+ }
133
+
127
134
  if (node.children.length) {
128
- this.collect(node.children, result);
135
+ this.collect(node.children, result, checkedNodeIds);
129
136
  } else {
130
137
  result.push(node.phase);
131
138
  }
@@ -3,6 +3,7 @@
3
3
  import { module } from 'angular';
4
4
 
5
5
  import { CORE_PIPELINE_CONFIG_STAGES_BAKE_BAKESTAGE } from './bakeStage';
6
+ import { PIPELINE_BAKE_STAGE_CHOOSE_MANAGED_IMAGE } from './bakeStageChooseManagedImage.component';
6
7
  import { PIPELINE_BAKE_STAGE_CHOOSE_OS } from './bakeStageChooseOs.component';
7
8
  import { CORE_PIPELINE_CONFIG_STAGES_BAKE_MODAL_ADDEXTENDEDATTRIBUTE_CONTROLLER_MODAL } from './modal/addExtendedAttribute.controller.modal';
8
9
 
@@ -12,4 +13,5 @@ module(CORE_PIPELINE_CONFIG_STAGES_BAKE_BAKESTAGE_MODULE, [
12
13
  CORE_PIPELINE_CONFIG_STAGES_BAKE_BAKESTAGE,
13
14
  CORE_PIPELINE_CONFIG_STAGES_BAKE_MODAL_ADDEXTENDEDATTRIBUTE_CONTROLLER_MODAL,
14
15
  PIPELINE_BAKE_STAGE_CHOOSE_OS,
16
+ PIPELINE_BAKE_STAGE_CHOOSE_MANAGED_IMAGE,
15
17
  ]);
@@ -0,0 +1,12 @@
1
+ <ui-select ng-model="$ctrl.model" on-select="$ctrl.onChange()" class="form-control input-sm">
2
+ <ui-select-match placeholder="Select a Managed Image">
3
+ <span ng-bind-html="$ctrl.getManagedImageDescription($select.selected)"></span>
4
+ </ui-select-match>
5
+ <ui-select-choices
6
+ repeat="managedImageOption.id as managedImageOption in $ctrl.managedImageOptions | filter: $select.search"
7
+ ui-disable-choice="$ctrl.getManagedImageDisabled(managedImageOption)"
8
+ >
9
+ <strong ng-bind-html="$ctrl.getManagedImageDescription(managedImageOption)"></strong>
10
+ <div ng-bind-html="$ctrl.getManagedImageDetailedDescription(managedImageOption)"></div>
11
+ </ui-select-choices>
12
+ </ui-select>
@@ -0,0 +1,49 @@
1
+ import type { IComponentOptions, IController } from 'angular';
2
+ import { module } from 'angular';
3
+ import { SETTINGS } from '../../../../config/settings';
4
+
5
+ export interface IManagedImageOption {
6
+ id: string;
7
+ name: string;
8
+ osType: string;
9
+ }
10
+
11
+ export class BakeStageChooseManagedImageController implements IController {
12
+ public model: any;
13
+ public managedImageOptions: IManagedImageOption[];
14
+ public onChange: () => any;
15
+
16
+ public $onChanges(): void {}
17
+
18
+ public getManagedImageDescription(managedImageOption: IManagedImageOption): string {
19
+ return managedImageOption?.name || '';
20
+ }
21
+
22
+ public getManagedImageDetailedDescription(managedImageOption: IManagedImageOption): string {
23
+ if (managedImageOption?.osType) {
24
+ return `${managedImageOption.name} (${managedImageOption.osType})`;
25
+ }
26
+ return `${managedImageOption.name}`;
27
+ }
28
+
29
+ public getManagedImageDisabled(managedImageOption: IManagedImageOption): boolean {
30
+ const disabledImages = SETTINGS.disabledImages || [];
31
+ return disabledImages.includes(managedImageOption.id);
32
+ }
33
+ }
34
+
35
+ const bakeStageChooseManagedImageComponent: IComponentOptions = {
36
+ bindings: {
37
+ managedImageOptions: '<',
38
+ model: '=',
39
+ onChange: '=',
40
+ },
41
+ controller: BakeStageChooseManagedImageController,
42
+ templateUrl: require('./bakeStageChooseManagedImage.component.html'),
43
+ };
44
+
45
+ export const PIPELINE_BAKE_STAGE_CHOOSE_MANAGED_IMAGE = 'spinnaker.core.pipeline.bake.chooseManagedImage.component';
46
+ module(PIPELINE_BAKE_STAGE_CHOOSE_MANAGED_IMAGE, []).component(
47
+ 'bakeStageChooseManagedImage',
48
+ bakeStageChooseManagedImageComponent,
49
+ );
@@ -2,7 +2,6 @@
2
2
  ng-if="!$ctrl.showRadioButtons"
3
3
  ng-model="$ctrl.model"
4
4
  on-select="$ctrl.onChange()"
5
- required
6
5
  class="form-control input-sm"
7
6
  >
8
7
  <ui-select-match placeholder="Select a base OS">
@@ -129,4 +129,48 @@ describe('<BakeHelmConfigForm />', () => {
129
129
  expect(component.find('.Select-value-label > span').text().includes(expectedArtifactDisplayName)).toBe(true);
130
130
  expect(component.find(StageConfigField).findWhere((x) => x.text() === helmChartFilePathFieldName).length).toBe(1);
131
131
  });
132
+
133
+ it('render the include crds checkbox if the template render is HELM3', async () => {
134
+ const stage = ({
135
+ templateRenderer: 'HELM3',
136
+ } as unknown) as IStage;
137
+
138
+ const props = getProps();
139
+
140
+ const component = mount(
141
+ <SpinFormik
142
+ initialValues={stage}
143
+ onSubmit={() => null}
144
+ validate={() => null}
145
+ render={(formik) => <BakeHelmConfigForm {...props} formik={formik} />}
146
+ />,
147
+ );
148
+
149
+ await new Promise((resolve) => setTimeout(resolve)); // wait one js tick for promise to resolve
150
+ component.setProps({}); // force a re-render
151
+
152
+ expect(component.find('span.label-text').findWhere((x) => x.text() === 'Include CRDs').length).toBe(1);
153
+ });
154
+
155
+ it('does not render the include crds checkbox if the template render is HELM2', async () => {
156
+ const stage = ({
157
+ templateRenderer: 'HELM2',
158
+ } as unknown) as IStage;
159
+
160
+ const props = getProps();
161
+
162
+ const component = mount(
163
+ <SpinFormik
164
+ initialValues={stage}
165
+ onSubmit={() => null}
166
+ validate={() => null}
167
+ render={(formik) => <BakeHelmConfigForm {...props} formik={formik} />}
168
+ />,
169
+ );
170
+
171
+ await new Promise((resolve) => setTimeout(resolve)); // wait one js tick for promise to resolve
172
+ component.setProps({}); // force a re-render
173
+
174
+ expect(component.find('span.label-text').findWhere((x) => x.text() === 'Include CRDs').length).toBe(0);
175
+ });
132
176
  });
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
 
3
3
  import type { IFormikStageConfigInjectedProps } from '../../FormikStageConfig';
4
+ import { ManifestRenderers } from '../ManifestRenderers';
4
5
  import { AccountService } from '../../../../../account';
5
6
  import {
6
7
  ArtifactTypePatterns,
@@ -257,6 +258,19 @@ export class BakeHelmConfigForm extends React.Component<IFormikStageConfigInject
257
258
  onChange={() => this.props.formik.setFieldValue('rawOverrides', !stage.rawOverrides)}
258
259
  />
259
260
  </StageConfigField>
261
+ {stage.templateRenderer === ManifestRenderers.HELM3 && (
262
+ <StageConfigField
263
+ fieldColumns={6}
264
+ helpKey={'pipeline.config.bake.manifest.helm.includeCRDs'}
265
+ label="Include CRDs"
266
+ >
267
+ <CheckboxInput
268
+ value={stage.includeCRDs}
269
+ text={''}
270
+ onChange={() => this.props.formik.setFieldValue('includeCRDs', !stage.includeCRDs)}
271
+ />
272
+ </StageConfigField>
273
+ )}
260
274
  <StageConfigField
261
275
  fieldColumns={6}
262
276
  helpKey={'pipeline.config.bake.manifest.overrideExpressionEvaluation'}
@@ -105,6 +105,9 @@
105
105
  </div>
106
106
  </div>
107
107
  </div>
108
+ <stage-config-field label="Skip downstream output" help-key="pipeline.skipDownstreamOutput">
109
+ <input type="checkbox" class="input-sm" name="skipDownstreamOutput" ng-model="stage.skipDownstreamOutput" />
110
+ </stage-config-field>
108
111
  <stage-config-field label="Wait for results" help-key="pipeline.waitForCompletion">
109
112
  <input type="checkbox" class="input-sm" name="waitForCompletion" ng-model="stage.waitForCompletion" />
110
113
  </stage-config-field>
@@ -95,18 +95,24 @@ export class ExecutionMarker extends React.Component<IExecutionMarkerProps, IExe
95
95
  public render() {
96
96
  const { stage, application, execution, active, previousStageActive, width } = this.props;
97
97
  let stageType = (stage.activeStageType || stage.type).toLowerCase(); // support groups
98
- stage.stages.forEach((childStage: IStage) => {
99
- if (childStage.type == 'pipeline') {
100
- const childPipeline = application.executions.data.find((p: any) => p.id === childStage.context.executionId);
101
- if (childPipeline != undefined) {
102
- childPipeline.stages.forEach((stageToCheck: IStage) => {
103
- if (stageToCheck.type == 'manualJudgment' && stageToCheck.status == 'RUNNING') {
104
- stageType = 'manualjudgment';
105
- }
106
- });
98
+ if (SETTINGS.feature.manualJudgmentParentPipeline) {
99
+ stage.stages.forEach((childStage: IStage) => {
100
+ if (
101
+ childStage.type == 'pipeline' &&
102
+ application.executions != undefined &&
103
+ application.executions.data != undefined
104
+ ) {
105
+ const childPipeline = application.executions.data.find((p: any) => p.id === childStage.context.executionId);
106
+ if (childPipeline != undefined) {
107
+ childPipeline.stages.forEach((stageToCheck: IStage) => {
108
+ if (stageToCheck.type == 'manualJudgment' && stageToCheck.status == 'RUNNING') {
109
+ stageType = 'manualjudgment';
110
+ }
111
+ });
112
+ }
107
113
  }
108
- }
109
- });
114
+ });
115
+ }
110
116
  const pipelineStatus = this.stageStatus(stage.status.toLowerCase());
111
117
  const markerClassName = [
112
118
  stage.type !== 'group' ? 'clickable' : '',
@@ -22,6 +22,16 @@ class V2InstanceArchetypeSelectorController implements IComponentController {
22
22
  const { $scope } = this;
23
23
  this.instanceTypeService.getCategories(this.command.selectedProvider).then((categories) => {
24
24
  $scope.instanceProfiles = categories;
25
+ // Resetting the 'unavailable' properties to avoid caching initially.
26
+ categories.forEach((profile) => {
27
+ if (profile.type === this.command.viewState.instanceProfile) {
28
+ profile.families.forEach((family) => {
29
+ family.instanceTypes.forEach((instanceType) => {
30
+ instanceType.unavailable = false;
31
+ });
32
+ });
33
+ }
34
+ });
25
35
  if ($scope.instanceProfiles.length % 3 === 0) {
26
36
  $scope.columns = 3;
27
37
  }
@@ -25,74 +25,76 @@ module(CORE_WIDGETS_ACCOUNTNAMESPACECLUSTERSELECTOR_COMPONENT, []).directive(
25
25
  templateUrl: require('./accountNamespaceClusterSelector.component.html'),
26
26
  controllerAs: 'vm',
27
27
  controller: function controller() {
28
- this.clusterField = this.clusterField || 'cluster';
28
+ this.$onInit = () => {
29
+ this.clusterField = this.clusterField || 'cluster';
29
30
 
30
- const vm = this;
31
- let isTextInputForClusterFiled;
31
+ const vm = this;
32
+ let isTextInputForClusterFiled;
32
33
 
33
- let namespaces;
34
+ let namespaces;
34
35
 
35
- const setNamespaceList = () => {
36
- const accountFilter = (cluster) => (cluster ? cluster.account === vm.component.credentials : true);
37
- // TODO(lwander): Move away from regions to namespaces here.
38
- const namespaceList = AppListExtractor.getRegions([vm.application], accountFilter);
39
- vm.namespaces = namespaceList.length ? namespaceList : namespaces;
40
- };
36
+ const setNamespaceList = () => {
37
+ const accountFilter = (cluster) => (cluster ? cluster.account === vm.component.credentials : true);
38
+ // TODO(lwander): Move away from regions to namespaces here.
39
+ const namespaceList = AppListExtractor.getRegions([vm.application], accountFilter);
40
+ vm.namespaces = namespaceList.length ? namespaceList : namespaces;
41
+ };
41
42
 
42
- const setClusterList = () => {
43
- const namespaceField = vm.component.namespaces;
44
- // TODO(lwander): Move away from regions to namespaces here.
45
- const clusterFilter = AppListExtractor.clusterFilterForCredentialsAndRegion(
46
- vm.component.credentials,
47
- namespaceField,
48
- );
49
- vm.clusterList = AppListExtractor.getClusters([vm.application], clusterFilter);
50
- };
43
+ const setClusterList = () => {
44
+ const namespaceField = vm.component.namespaces;
45
+ // TODO(lwander): Move away from regions to namespaces here.
46
+ const clusterFilter = AppListExtractor.clusterFilterForCredentialsAndRegion(
47
+ vm.component.credentials,
48
+ namespaceField,
49
+ );
50
+ vm.clusterList = AppListExtractor.getClusters([vm.application], clusterFilter);
51
+ };
51
52
 
52
- vm.namespaceChanged = () => {
53
- setClusterList();
54
- if (!isTextInputForClusterFiled && !_.includes(vm.clusterList, vm.component[this.clusterField])) {
55
- vm.component[this.clusterField] = undefined;
56
- }
57
- };
53
+ vm.namespaceChanged = () => {
54
+ setClusterList();
55
+ if (!isTextInputForClusterFiled && !_.includes(vm.clusterList, vm.component[this.clusterField])) {
56
+ vm.component[this.clusterField] = undefined;
57
+ }
58
+ };
58
59
 
59
- const setToggledState = () => {
60
- vm.namespaces = namespaces;
61
- isTextInputForClusterFiled = true;
62
- };
60
+ const setToggledState = () => {
61
+ vm.namespaces = namespaces;
62
+ isTextInputForClusterFiled = true;
63
+ };
63
64
 
64
- const setUnToggledState = () => {
65
- vm.component[this.clusterField] = undefined;
66
- isTextInputForClusterFiled = false;
67
- setNamespaceList();
68
- };
65
+ const setUnToggledState = () => {
66
+ vm.component[this.clusterField] = undefined;
67
+ isTextInputForClusterFiled = false;
68
+ setNamespaceList();
69
+ };
69
70
 
70
- vm.clusterSelectInputToggled = (isToggled) => {
71
- isToggled ? setToggledState() : setUnToggledState();
72
- };
71
+ vm.clusterSelectInputToggled = (isToggled) => {
72
+ isToggled ? setToggledState() : setUnToggledState();
73
+ };
73
74
 
74
- vm.accountUpdated = () => {
75
- vm.component[this.clusterField] = undefined;
76
- setNamespaceList();
77
- setClusterList();
78
- };
75
+ vm.accountUpdated = () => {
76
+ vm.component[this.clusterField] = undefined;
77
+ setNamespaceList();
78
+ setClusterList();
79
+ };
79
80
 
80
- const init = () => {
81
- AccountService.getUniqueAttributeForAllAccounts(vm.component.cloudProviderType, 'namespaces')
82
- .then((allNamespaces) => {
83
- namespaces = allNamespaces;
84
- return allNamespaces;
85
- })
86
- .then((allNamespaces) => {
87
- setNamespaceList();
88
- setClusterList();
89
- vm.namespaces = _.includes(vm.clusterList, vm.component[this.clusterField])
90
- ? vm.namespaces
91
- : allNamespaces;
92
- });
93
- };
81
+ const init = () => {
82
+ AccountService.getUniqueAttributeForAllAccounts(vm.component.cloudProviderType, 'namespaces')
83
+ .then((allNamespaces) => {
84
+ namespaces = allNamespaces;
85
+ return allNamespaces;
86
+ })
87
+ .then((allNamespaces) => {
88
+ setNamespaceList();
89
+ setClusterList();
90
+ vm.namespaces = _.includes(vm.clusterList, vm.component[this.clusterField])
91
+ ? vm.namespaces
92
+ : allNamespaces;
93
+ });
94
+ };
94
95
 
95
- init();
96
+ init();
97
+ };
96
98
  },
97
99
  };
98
100
  },