@spinnaker/kubernetes 0.0.0-main-2
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.
- package/CHANGELOG.md +1663 -0
- package/LICENSE.txt +203 -0
- package/dist/help/kubernetes.help.d.ts +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +6024 -0
- package/dist/index.js.map +1 -0
- package/dist/instance/details/details.controller.d.ts +1 -0
- package/dist/instance/index.d.ts +1 -0
- package/dist/interfaces/index.d.ts +1 -0
- package/dist/interfaces/infrastructure.types.d.ts +24 -0
- package/dist/kubernetes.module.d.ts +6 -0
- package/dist/loadBalancer/details/details.controller.d.ts +1 -0
- package/dist/loadBalancer/index.d.ts +1 -0
- package/dist/loadBalancer/transformer.d.ts +1 -0
- package/dist/manifest/IManifestCoordinates.d.ts +5 -0
- package/dist/manifest/ManifestImageDetails.d.ts +18 -0
- package/dist/manifest/ManifestKindSearch.d.ts +13 -0
- package/dist/manifest/ManifestLabels.d.ts +15 -0
- package/dist/manifest/ManifestQos.d.ts +15 -0
- package/dist/manifest/ManifestResources.d.ts +35 -0
- package/dist/manifest/ManifestSource.d.ts +4 -0
- package/dist/manifest/annotationCustomSections.component.d.ts +1 -0
- package/dist/manifest/artifact/artifact.component.d.ts +1 -0
- package/dist/manifest/delete/delete.controller.d.ts +12 -0
- package/dist/manifest/delete/deleteOptionsForm.component.d.ts +1 -0
- package/dist/manifest/editor/json/JsonEditor.d.ts +12 -0
- package/dist/manifest/editor/json/jsonEditor.component.d.ts +1 -0
- package/dist/manifest/index.d.ts +10 -0
- package/dist/manifest/manifest.service.d.ts +25 -0
- package/dist/manifest/manifestCommandBuilder.service.d.ts +30 -0
- package/dist/manifest/manifestEvents.component.d.ts +1 -0
- package/dist/manifest/manifestImageDetails.component.d.ts +1 -0
- package/dist/manifest/manifestLabels.component.d.ts +1 -0
- package/dist/manifest/manifestQos.component.d.ts +1 -0
- package/dist/manifest/manifestResources.component.d.ts +1 -0
- package/dist/manifest/rollout/RollingRestart.d.ts +1 -0
- package/dist/manifest/rollout/pause.controller.d.ts +1 -0
- package/dist/manifest/rollout/resume.controller.d.ts +1 -0
- package/dist/manifest/rollout/undo.controller.d.ts +1 -0
- package/dist/manifest/scale/ScaleSettingsForm.d.ts +10 -0
- package/dist/manifest/scale/scale.controller.d.ts +8 -0
- package/dist/manifest/scale/scaleSettingsForm.component.d.ts +1 -0
- package/dist/manifest/selector/IManifestLabelSelector.d.ts +9 -0
- package/dist/manifest/selector/IManifestSelector.d.ts +30 -0
- package/dist/manifest/selector/ManifestSelector.d.ts +46 -0
- package/dist/manifest/selector/labelEditor/LabelEditor.d.ts +15 -0
- package/dist/manifest/selector/selector.component.d.ts +1 -0
- package/dist/manifest/status/ManifestCondition.d.ts +13 -0
- package/dist/manifest/status/condition.component.d.ts +1 -0
- package/dist/manifest/status/status.component.d.ts +1 -0
- package/dist/manifest/traffic/ManifestTrafficService.d.ts +12 -0
- package/dist/manifest/wizard/BasicSettings.d.ts +17 -0
- package/dist/manifest/wizard/ManifestEntry.d.ts +16 -0
- package/dist/manifest/wizard/ManifestWizard.d.ts +22 -0
- package/dist/pipelines/stages/ManifestCoordinates.d.ts +31 -0
- package/dist/pipelines/stages/ManifestExecutionDetails.d.ts +69 -0
- package/dist/pipelines/stages/deleteManifest/DeleteManifestOptionsForm.d.ts +18 -0
- package/dist/pipelines/stages/deleteManifest/DeleteManifestStageConfig.d.ts +14 -0
- package/dist/pipelines/stages/deleteManifest/deleteManifestStage.d.ts +1 -0
- package/dist/pipelines/stages/deployManifest/CopyFromTemplateButton.d.ts +15 -0
- package/dist/pipelines/stages/deployManifest/DeployManifestStageConfig.d.ts +15 -0
- package/dist/pipelines/stages/deployManifest/DeployManifestStageForm.d.ts +31 -0
- package/dist/pipelines/stages/deployManifest/ManifestBindArtifactsSelector.d.ts +17 -0
- package/dist/pipelines/stages/deployManifest/ManifestCopier.d.ts +36 -0
- package/dist/pipelines/stages/deployManifest/ManifestDeploymentOptions.d.ts +34 -0
- package/dist/pipelines/stages/deployManifest/NamespaceSelector.d.ts +16 -0
- package/dist/pipelines/stages/deployManifest/deployManifest.validator.d.ts +2 -0
- package/dist/pipelines/stages/deployManifest/deployManifestStage.d.ts +1 -0
- package/dist/pipelines/stages/deployManifest/manifestStatus/DeployStatus.d.ts +23 -0
- package/dist/pipelines/stages/deployManifest/manifestStatus/DeployStatusPills.d.ts +8 -0
- package/dist/pipelines/stages/deployManifest/manifestStatus/ManifestDetailsLink.d.ts +20 -0
- package/dist/pipelines/stages/deployManifest/manifestStatus/ManifestEvents.d.ts +9 -0
- package/dist/pipelines/stages/deployManifest/manifestStatus/ManifestStatus.d.ts +8 -0
- package/dist/pipelines/stages/findArtifactsFromResource/FindArtifactsFromResourceConfig.d.ts +3 -0
- package/dist/pipelines/stages/findArtifactsFromResource/FindArtifactsFromResourceStageForm.d.ts +7 -0
- package/dist/pipelines/stages/findArtifactsFromResource/findArtifactsFromResourceStage.d.ts +1 -0
- package/dist/pipelines/stages/index.d.ts +8 -0
- package/dist/pipelines/stages/patchManifest/PatchManifestOptionsForm.d.ts +8 -0
- package/dist/pipelines/stages/patchManifest/PatchManifestStageConfig.d.ts +7 -0
- package/dist/pipelines/stages/patchManifest/PatchManifestStageForm.d.ts +21 -0
- package/dist/pipelines/stages/patchManifest/patchManifestStage.d.ts +4 -0
- package/dist/pipelines/stages/rolloutRestartManifest/RolloutRestartManifestStageConfig.d.ts +13 -0
- package/dist/pipelines/stages/rolloutRestartManifest/rolloutRestartManifestStage.d.ts +4 -0
- package/dist/pipelines/stages/runJob/KubernetesV2RunJobStageConfig.d.ts +31 -0
- package/dist/pipelines/stages/runJob/RunJobExecutionDetails.d.ts +7 -0
- package/dist/pipelines/stages/runJob/runJobStage.d.ts +1 -0
- package/dist/pipelines/stages/scaleManifest/ScaleManifestConfig.d.ts +3 -0
- package/dist/pipelines/stages/scaleManifest/ScaleManifestStageForm.d.ts +7 -0
- package/dist/pipelines/stages/scaleManifest/scaleManifestStage.d.ts +1 -0
- package/dist/pipelines/stages/traffic/ManifestTrafficStageConfig.d.ts +11 -0
- package/dist/pipelines/stages/traffic/disableManifest.stage.d.ts +1 -0
- package/dist/pipelines/stages/traffic/enableManifest.stage.d.ts +1 -0
- package/dist/pipelines/stages/undoRolloutManifest/UndoRolloutManifestConfig.d.ts +3 -0
- package/dist/pipelines/stages/undoRolloutManifest/UndoRolloutManifestStageForm.d.ts +7 -0
- package/dist/pipelines/stages/undoRolloutManifest/undoRolloutManifestStage.d.ts +1 -0
- package/dist/pipelines/stages/validators/manifestSelectorValidators.d.ts +2 -0
- package/dist/pipelines/validation/manifestSelector.validator.d.ts +8 -0
- package/dist/rawResource/component/K8sResources.d.ts +25 -0
- package/dist/rawResource/component/K8sResourcesFilters.d.ts +20 -0
- package/dist/rawResource/component/RawResourceUtils.d.ts +5 -0
- package/dist/rawResource/component/group/RawResouceGroup.d.ts +15 -0
- package/dist/rawResource/component/group/RawResource.d.ts +12 -0
- package/dist/rawResource/component/group/RawResourceDetails.d.ts +32 -0
- package/dist/rawResource/component/group/RawResourceGroups.d.ts +14 -0
- package/dist/rawResource/controller/FiltersPubSub.d.ts +12 -0
- package/dist/rawResource/index.d.ts +1 -0
- package/dist/rawResource/model/resource.d.ts +17 -0
- package/dist/rawResource/rawResource.dataSource.d.ts +2 -0
- package/dist/rawResource/rawResource.module.d.ts +1 -0
- package/dist/rawResource/rawResource.states.d.ts +6 -0
- package/dist/resources/ResourceDetails.d.ts +17 -0
- package/dist/resources/resources.state.d.ts +7 -0
- package/dist/rolloutStrategy/bluegreen.strategy.d.ts +2 -0
- package/dist/rolloutStrategy/highlander.strategy.d.ts +2 -0
- package/dist/rolloutStrategy/index.d.ts +1 -0
- package/dist/rolloutStrategy/none.strategy.d.ts +2 -0
- package/dist/rolloutStrategy/redblack.strategy.d.ts +2 -0
- package/dist/securityGroup/details/details.controller.d.ts +1 -0
- package/dist/securityGroup/index.d.ts +1 -0
- package/dist/securityGroup/securityGroup.reader.d.ts +4 -0
- package/dist/securityGroup/transformer.d.ts +1 -0
- package/dist/serverGroup/details/details.controller.d.ts +1 -0
- package/dist/serverGroup/details/resize/resize.controller.d.ts +1 -0
- package/dist/serverGroup/index.d.ts +4 -0
- package/dist/serverGroup/serverGroupCommandBuilder.service.d.ts +6 -0
- package/dist/serverGroup/serverGroupTransformer.service.d.ts +6 -0
- package/dist/serverGroupManager/details/details.controller.d.ts +1 -0
- package/dist/serverGroupManager/index.d.ts +1 -0
- package/dist/validation/applicationName.validator.d.ts +9 -0
- package/package.json +58 -0
- package/src/help/kubernetes.help.ts +236 -0
- package/src/index.ts +7 -0
- package/src/instance/details/details.controller.ts +186 -0
- package/src/instance/details/details.html +80 -0
- package/src/instance/index.ts +1 -0
- package/src/interfaces/index.ts +1 -0
- package/src/interfaces/infrastructure.types.ts +35 -0
- package/src/kubernetes.module.ts +144 -0
- package/src/loadBalancer/details/details.controller.ts +101 -0
- package/src/loadBalancer/details/details.html +187 -0
- package/src/loadBalancer/index.ts +1 -0
- package/src/loadBalancer/transformer.ts +60 -0
- package/src/logo/kubernetes.icon.svg +102 -0
- package/src/logo/kubernetes.logo.less +6 -0
- package/src/logo/kubernetes.logo.svg +1 -0
- package/src/manifest/IManifestCoordinates.ts +5 -0
- package/src/manifest/ManifestImageDetails.spec.tsx +76 -0
- package/src/manifest/ManifestImageDetails.tsx +64 -0
- package/src/manifest/ManifestKindSearch.tsx +31 -0
- package/src/manifest/ManifestLabels.tsx +31 -0
- package/src/manifest/ManifestQos.tsx +68 -0
- package/src/manifest/ManifestResources.tsx +69 -0
- package/src/manifest/ManifestSource.ts +4 -0
- package/src/manifest/annotationCustomSections.component.ts +114 -0
- package/src/manifest/artifact/artifact.component.ts +23 -0
- package/src/manifest/delete/delete.controller.ts +80 -0
- package/src/manifest/delete/delete.html +42 -0
- package/src/manifest/delete/deleteOptionsForm.component.ts +52 -0
- package/src/manifest/editor/json/JsonEditor.tsx +56 -0
- package/src/manifest/editor/json/jsonEditor.component.ts +12 -0
- package/src/manifest/index.ts +10 -0
- package/src/manifest/manifest.service.ts +90 -0
- package/src/manifest/manifestCommandBuilder.service.ts +126 -0
- package/src/manifest/manifestEvents.component.ts +12 -0
- package/src/manifest/manifestImageDetails.component.ts +12 -0
- package/src/manifest/manifestLabels.component.ts +12 -0
- package/src/manifest/manifestLabels.less +3 -0
- package/src/manifest/manifestQos.component.ts +12 -0
- package/src/manifest/manifestResources.component.ts +12 -0
- package/src/manifest/rollout/RollingRestart.tsx +54 -0
- package/src/manifest/rollout/pause.controller.ts +66 -0
- package/src/manifest/rollout/pause.html +28 -0
- package/src/manifest/rollout/resume.controller.ts +66 -0
- package/src/manifest/rollout/resume.html +28 -0
- package/src/manifest/rollout/undo.controller.ts +74 -0
- package/src/manifest/rollout/undo.html +44 -0
- package/src/manifest/scale/ScaleSettingsForm.tsx +43 -0
- package/src/manifest/scale/scale.controller.ts +75 -0
- package/src/manifest/scale/scale.html +32 -0
- package/src/manifest/scale/scaleSettingsForm.component.ts +11 -0
- package/src/manifest/selector/IManifestLabelSelector.ts +19 -0
- package/src/manifest/selector/IManifestSelector.ts +64 -0
- package/src/manifest/selector/ManifestSelector.spec.tsx +414 -0
- package/src/manifest/selector/ManifestSelector.tsx +424 -0
- package/src/manifest/selector/labelEditor/LabelEditor.spec.tsx +81 -0
- package/src/manifest/selector/labelEditor/LabelEditor.tsx +126 -0
- package/src/manifest/selector/labelEditor/labelEditor.less +3 -0
- package/src/manifest/selector/selector.component.ts +17 -0
- package/src/manifest/status/ManifestCondition.tsx +32 -0
- package/src/manifest/status/condition.component.ts +12 -0
- package/src/manifest/status/status.component.ts +36 -0
- package/src/manifest/traffic/ManifestTrafficService.spec.ts +30 -0
- package/src/manifest/traffic/ManifestTrafficService.ts +67 -0
- package/src/manifest/wizard/BasicSettings.tsx +57 -0
- package/src/manifest/wizard/ManifestEntry.tsx +46 -0
- package/src/manifest/wizard/ManifestWizard.tsx +94 -0
- package/src/pipelines/stages/ManifestCoordinates.spec.tsx +90 -0
- package/src/pipelines/stages/ManifestCoordinates.tsx +128 -0
- package/src/pipelines/stages/ManifestExecutionDetails.tsx +58 -0
- package/src/pipelines/stages/deleteManifest/DeleteManifestOptionsForm.spec.tsx +85 -0
- package/src/pipelines/stages/deleteManifest/DeleteManifestOptionsForm.tsx +87 -0
- package/src/pipelines/stages/deleteManifest/DeleteManifestStageConfig.tsx +62 -0
- package/src/pipelines/stages/deleteManifest/deleteManifestOptionsForm.less +9 -0
- package/src/pipelines/stages/deleteManifest/deleteManifestStage.ts +21 -0
- package/src/pipelines/stages/deployManifest/CopyFromTemplateButton.tsx +45 -0
- package/src/pipelines/stages/deployManifest/DeployManifestStageConfig.tsx +77 -0
- package/src/pipelines/stages/deployManifest/DeployManifestStageForm.tsx +265 -0
- package/src/pipelines/stages/deployManifest/ManifestBindArtifactsSelector.tsx +69 -0
- package/src/pipelines/stages/deployManifest/ManifestCopier.spec.ts +110 -0
- package/src/pipelines/stages/deployManifest/ManifestCopier.tsx +222 -0
- package/src/pipelines/stages/deployManifest/ManifestDeploymentOptions.spec.tsx +75 -0
- package/src/pipelines/stages/deployManifest/ManifestDeploymentOptions.tsx +203 -0
- package/src/pipelines/stages/deployManifest/NamespaceSelector.tsx +40 -0
- package/src/pipelines/stages/deployManifest/deployManifest.validator.ts +38 -0
- package/src/pipelines/stages/deployManifest/deployManifestStage.ts +28 -0
- package/src/pipelines/stages/deployManifest/manifestStatus/DeployStatus.less +11 -0
- package/src/pipelines/stages/deployManifest/manifestStatus/DeployStatus.tsx +120 -0
- package/src/pipelines/stages/deployManifest/manifestStatus/DeployStatusPills.tsx +47 -0
- package/src/pipelines/stages/deployManifest/manifestStatus/ManifestDetailsLink.tsx +92 -0
- package/src/pipelines/stages/deployManifest/manifestStatus/ManifestEvents.tsx +87 -0
- package/src/pipelines/stages/deployManifest/manifestStatus/ManifestStatus.less +24 -0
- package/src/pipelines/stages/deployManifest/manifestStatus/ManifestStatus.tsx +46 -0
- package/src/pipelines/stages/findArtifactsFromResource/FindArtifactsFromResourceConfig.tsx +35 -0
- package/src/pipelines/stages/findArtifactsFromResource/FindArtifactsFromResourceStageForm.tsx +37 -0
- package/src/pipelines/stages/findArtifactsFromResource/findArtifactsFromResourceStage.ts +21 -0
- package/src/pipelines/stages/index.ts +8 -0
- package/src/pipelines/stages/patchManifest/PatchManifestOptionsForm.tsx +35 -0
- package/src/pipelines/stages/patchManifest/PatchManifestStageConfig.tsx +57 -0
- package/src/pipelines/stages/patchManifest/PatchManifestStageForm.tsx +157 -0
- package/src/pipelines/stages/patchManifest/patchManifestStage.ts +30 -0
- package/src/pipelines/stages/rolloutRestartManifest/RolloutRestartManifestStageConfig.tsx +46 -0
- package/src/pipelines/stages/rolloutRestartManifest/rolloutRestartManifestStage.ts +20 -0
- package/src/pipelines/stages/runJob/KubernetesV2RunJobStageConfig.tsx +242 -0
- package/src/pipelines/stages/runJob/RunJobExecutionDetails.tsx +67 -0
- package/src/pipelines/stages/runJob/runJobStage.ts +39 -0
- package/src/pipelines/stages/scaleManifest/ScaleManifestConfig.tsx +38 -0
- package/src/pipelines/stages/scaleManifest/ScaleManifestStageForm.tsx +46 -0
- package/src/pipelines/stages/scaleManifest/scaleManifestStage.ts +24 -0
- package/src/pipelines/stages/traffic/ManifestTrafficStageConfig.tsx +44 -0
- package/src/pipelines/stages/traffic/disableManifest.stage.ts +26 -0
- package/src/pipelines/stages/traffic/enableManifest.stage.ts +26 -0
- package/src/pipelines/stages/undoRolloutManifest/UndoRolloutManifestConfig.tsx +37 -0
- package/src/pipelines/stages/undoRolloutManifest/UndoRolloutManifestStageForm.tsx +60 -0
- package/src/pipelines/stages/undoRolloutManifest/undoRolloutManifestStage.ts +21 -0
- package/src/pipelines/stages/validators/manifestSelectorValidators.ts +43 -0
- package/src/pipelines/validation/manifestSelector.validator.ts +36 -0
- package/src/rawResource/component/K8sResources.less +9 -0
- package/src/rawResource/component/K8sResources.tsx +112 -0
- package/src/rawResource/component/K8sResourcesFilters.tsx +112 -0
- package/src/rawResource/component/RawResourceUtils.ts +19 -0
- package/src/rawResource/component/group/RawResouceGroup.tsx +41 -0
- package/src/rawResource/component/group/RawResource.less +79 -0
- package/src/rawResource/component/group/RawResource.tsx +51 -0
- package/src/rawResource/component/group/RawResourceDetails.tsx +236 -0
- package/src/rawResource/component/group/RawResourceGroups.tsx +53 -0
- package/src/rawResource/controller/FiltersPubSub.ts +35 -0
- package/src/rawResource/index.ts +1 -0
- package/src/rawResource/model/resource.ts +18 -0
- package/src/rawResource/rawResource.dataSource.ts +39 -0
- package/src/rawResource/rawResource.module.ts +8 -0
- package/src/rawResource/rawResource.states.ts +64 -0
- package/src/resources/ResourceDetails.tsx +121 -0
- package/src/resources/resources.state.ts +45 -0
- package/src/rolloutStrategy/bluegreen.strategy.ts +7 -0
- package/src/rolloutStrategy/highlander.strategy.ts +7 -0
- package/src/rolloutStrategy/index.ts +6 -0
- package/src/rolloutStrategy/none.strategy.ts +8 -0
- package/src/rolloutStrategy/redblack.strategy.ts +7 -0
- package/src/securityGroup/details/details.controller.ts +118 -0
- package/src/securityGroup/details/details.html +91 -0
- package/src/securityGroup/index.ts +1 -0
- package/src/securityGroup/securityGroup.reader.ts +11 -0
- package/src/securityGroup/transformer.ts +19 -0
- package/src/serverGroup/details/details.controller.ts +207 -0
- package/src/serverGroup/details/details.html +153 -0
- package/src/serverGroup/details/resize/resize.controller.ts +73 -0
- package/src/serverGroup/details/resize/resize.html +57 -0
- package/src/serverGroup/index.ts +4 -0
- package/src/serverGroup/serverGroupCommandBuilder.service.ts +18 -0
- package/src/serverGroup/serverGroupTransformer.service.spec.ts +67 -0
- package/src/serverGroup/serverGroupTransformer.service.ts +34 -0
- package/src/serverGroupManager/details/details.controller.ts +191 -0
- package/src/serverGroupManager/details/details.html +136 -0
- package/src/serverGroupManager/index.ts +1 -0
- package/src/validation/applicationName.validator.spec.ts +19 -0
- package/src/validation/applicationName.validator.ts +44 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import { get, isEmpty } from 'lodash';
|
|
2
|
+
import { $q } from 'ngimport';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import type { Option } from 'react-select';
|
|
5
|
+
import Select, { Creatable } from 'react-select';
|
|
6
|
+
import { from as observableFrom, Subject } from 'rxjs';
|
|
7
|
+
import { switchMap, takeUntil, tap } from 'rxjs/operators';
|
|
8
|
+
|
|
9
|
+
import type { Application, IAccountDetails, IServerGroup } from '@spinnaker/core';
|
|
10
|
+
import {
|
|
11
|
+
AccountSelectInput,
|
|
12
|
+
AccountService,
|
|
13
|
+
AppListExtractor,
|
|
14
|
+
NgReact,
|
|
15
|
+
noop,
|
|
16
|
+
ScopeClusterSelector,
|
|
17
|
+
SETTINGS,
|
|
18
|
+
StageConfigField,
|
|
19
|
+
StageConstants,
|
|
20
|
+
} from '@spinnaker/core';
|
|
21
|
+
|
|
22
|
+
import type { IManifestLabelSelector } from './IManifestLabelSelector';
|
|
23
|
+
import type { IManifestSelector } from './IManifestSelector';
|
|
24
|
+
import { SelectorMode, SelectorModeDataMap } from './IManifestSelector';
|
|
25
|
+
import { ManifestKindSearchService } from '../ManifestKindSearch';
|
|
26
|
+
import LabelEditor from './labelEditor/LabelEditor';
|
|
27
|
+
|
|
28
|
+
export interface IManifestSelectorProps {
|
|
29
|
+
selector: IManifestSelector;
|
|
30
|
+
application?: Application;
|
|
31
|
+
includeSpinnakerKinds?: string[];
|
|
32
|
+
modes?: SelectorMode[];
|
|
33
|
+
onChange(selector: IManifestSelector): void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface IManifestSelectorState {
|
|
37
|
+
accounts: IAccountDetails[];
|
|
38
|
+
selector: IManifestSelector;
|
|
39
|
+
namespaces: string[];
|
|
40
|
+
kinds: string[];
|
|
41
|
+
resources: string[];
|
|
42
|
+
loading: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface ISelectorHandler {
|
|
46
|
+
handles(mode: SelectorMode): boolean;
|
|
47
|
+
handleModeChange(): void;
|
|
48
|
+
handleKindChange(kind: string): void;
|
|
49
|
+
getKind(): string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const parseSpinnakerName = (spinnakerName: string): { name: string; kind: string } => {
|
|
53
|
+
const [kind, name] = (spinnakerName || '').split(' ');
|
|
54
|
+
return { kind, name };
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
class StaticManifestSelectorHandler implements ISelectorHandler {
|
|
58
|
+
constructor(private component: ManifestSelector) {}
|
|
59
|
+
|
|
60
|
+
public handles = (mode: SelectorMode): boolean => mode === SelectorMode.Static;
|
|
61
|
+
|
|
62
|
+
public handleModeChange = (): void => {
|
|
63
|
+
const { selector } = this.component.state;
|
|
64
|
+
this.handleKindChange(selector.kind);
|
|
65
|
+
Object.assign(selector, SelectorModeDataMap.static.selectorDefaults);
|
|
66
|
+
this.component.setStateAndUpdateStage({ selector });
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
public handleKindChange = (kind: string): void => {
|
|
70
|
+
const { selector } = this.component.state;
|
|
71
|
+
const { name } = parseSpinnakerName(selector.manifestName);
|
|
72
|
+
selector.manifestName = kind ? (name ? `${kind} ${name}` : kind) : name;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
public getKind = (): string => parseSpinnakerName(this.component.state.selector.manifestName).kind;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
class DynamicManifestSelectorHandler implements ISelectorHandler {
|
|
79
|
+
constructor(private component: ManifestSelector) {}
|
|
80
|
+
|
|
81
|
+
public handles = (mode: SelectorMode): boolean => mode === SelectorMode.Dynamic;
|
|
82
|
+
|
|
83
|
+
public handleModeChange = (): void => {
|
|
84
|
+
const { selector } = this.component.state;
|
|
85
|
+
const { kind } = parseSpinnakerName(selector.manifestName);
|
|
86
|
+
selector.kind = kind || null;
|
|
87
|
+
Object.assign(selector, SelectorModeDataMap.dynamic.selectorDefaults);
|
|
88
|
+
this.component.setStateAndUpdateStage({ selector });
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
public handleKindChange = (kind: string): void => {
|
|
92
|
+
this.component.state.selector.kind = kind;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
public getKind = (): string => this.component.state.selector.kind;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
class LabelManifestSelectorHandler implements ISelectorHandler {
|
|
99
|
+
constructor(private component: ManifestSelector) {}
|
|
100
|
+
|
|
101
|
+
public handles = (mode: SelectorMode): boolean => mode === SelectorMode.Label;
|
|
102
|
+
|
|
103
|
+
public handleModeChange = (): void => {
|
|
104
|
+
const { selector } = this.component.state;
|
|
105
|
+
Object.assign(selector, SelectorModeDataMap.label.selectorDefaults);
|
|
106
|
+
this.component.setStateAndUpdateStage({ selector });
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
public handleKindChange = (): void => {};
|
|
110
|
+
|
|
111
|
+
public getKind = (): string => null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export class ManifestSelector extends React.Component<IManifestSelectorProps, IManifestSelectorState> {
|
|
115
|
+
private search$ = new Subject<{ kind: string; namespace: string; account: string }>();
|
|
116
|
+
private destroy$ = new Subject<void>();
|
|
117
|
+
private handlers: ISelectorHandler[];
|
|
118
|
+
|
|
119
|
+
constructor(props: IManifestSelectorProps) {
|
|
120
|
+
super(props);
|
|
121
|
+
|
|
122
|
+
if (!this.props.selector.mode) {
|
|
123
|
+
this.props.selector.mode = SelectorMode.Static;
|
|
124
|
+
this.props.onChange && this.props.onChange(this.props.selector);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
this.state = {
|
|
128
|
+
selector: props.selector,
|
|
129
|
+
accounts: [],
|
|
130
|
+
namespaces: [],
|
|
131
|
+
kinds: [],
|
|
132
|
+
resources: [],
|
|
133
|
+
loading: false,
|
|
134
|
+
};
|
|
135
|
+
this.handlers = [
|
|
136
|
+
new StaticManifestSelectorHandler(this),
|
|
137
|
+
new DynamicManifestSelectorHandler(this),
|
|
138
|
+
new LabelManifestSelectorHandler(this),
|
|
139
|
+
];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
public setStateAndUpdateStage = (state: Partial<IManifestSelectorState>, cb?: () => void): void => {
|
|
143
|
+
if (state.selector && this.props.onChange) {
|
|
144
|
+
this.props.onChange(state.selector);
|
|
145
|
+
}
|
|
146
|
+
this.setState(state as IManifestSelectorState, cb || noop);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
public componentDidMount = (): void => {
|
|
150
|
+
this.loadAccounts();
|
|
151
|
+
|
|
152
|
+
this.search$
|
|
153
|
+
.pipe(
|
|
154
|
+
tap(() => this.setStateAndUpdateStage({ loading: true })),
|
|
155
|
+
switchMap(({ kind, namespace, account }) => observableFrom(this.search(kind, namespace, account))),
|
|
156
|
+
takeUntil(this.destroy$),
|
|
157
|
+
)
|
|
158
|
+
.subscribe((resources) => {
|
|
159
|
+
if (this.state.selector.manifestName == null && this.getSelectedMode() === SelectorMode.Static) {
|
|
160
|
+
this.handleNameChange('');
|
|
161
|
+
}
|
|
162
|
+
this.setStateAndUpdateStage({ loading: false, resources: resources, selector: this.state.selector });
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
public componentWillUnmount = () => this.destroy$.next();
|
|
167
|
+
|
|
168
|
+
public loadAccounts = (): PromiseLike<void> => {
|
|
169
|
+
return AccountService.getAllAccountDetailsForProvider('kubernetes').then((accounts) => {
|
|
170
|
+
const selector = this.state.selector;
|
|
171
|
+
const kind = parseSpinnakerName(selector.manifestName).kind;
|
|
172
|
+
|
|
173
|
+
this.setStateAndUpdateStage({ accounts });
|
|
174
|
+
|
|
175
|
+
if (!selector.account && accounts.length > 0) {
|
|
176
|
+
selector.account = accounts.some((e) => e.name === SETTINGS.providers.kubernetes.defaults.account)
|
|
177
|
+
? SETTINGS.providers.kubernetes.defaults.account
|
|
178
|
+
: accounts[0].name;
|
|
179
|
+
}
|
|
180
|
+
if (selector.account) {
|
|
181
|
+
this.handleAccountChange(selector.account);
|
|
182
|
+
}
|
|
183
|
+
if (kind) {
|
|
184
|
+
this.search$.next({ kind, namespace: selector.location, account: selector.account });
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
private handleAccountChange = (selectedAccount: string): void => {
|
|
190
|
+
const details = (this.state.accounts || []).find((account) => account.name === selectedAccount);
|
|
191
|
+
if (!details) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const namespaces = (details.namespaces || []).sort();
|
|
195
|
+
const kinds = Object.entries(details.spinnakerKindMap || {})
|
|
196
|
+
.filter(([, spinnakerKind]) =>
|
|
197
|
+
this.props.includeSpinnakerKinds && this.props.includeSpinnakerKinds.length
|
|
198
|
+
? this.props.includeSpinnakerKinds.includes(spinnakerKind)
|
|
199
|
+
: true,
|
|
200
|
+
)
|
|
201
|
+
.map(([kind]) => kind)
|
|
202
|
+
.sort();
|
|
203
|
+
|
|
204
|
+
if (
|
|
205
|
+
!this.isExpression(this.state.selector.location) &&
|
|
206
|
+
namespaces.every((ns) => ns !== this.state.selector.location)
|
|
207
|
+
) {
|
|
208
|
+
this.state.selector.location = null;
|
|
209
|
+
}
|
|
210
|
+
this.state.selector.account = selectedAccount;
|
|
211
|
+
|
|
212
|
+
this.search$.next({
|
|
213
|
+
kind: parseSpinnakerName(this.state.selector.manifestName).kind || this.state.selector.kind,
|
|
214
|
+
namespace: this.state.selector.location,
|
|
215
|
+
account: this.state.selector.account,
|
|
216
|
+
});
|
|
217
|
+
this.setStateAndUpdateStage({
|
|
218
|
+
namespaces,
|
|
219
|
+
kinds,
|
|
220
|
+
selector: this.state.selector,
|
|
221
|
+
});
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
private handleNamespaceChange = (selectedNamespace: Option): void => {
|
|
225
|
+
this.state.selector.location =
|
|
226
|
+
selectedNamespace && selectedNamespace.value ? (selectedNamespace.value as string) : null;
|
|
227
|
+
this.search$.next({
|
|
228
|
+
kind: parseSpinnakerName(this.state.selector.manifestName).kind,
|
|
229
|
+
namespace: this.state.selector.location,
|
|
230
|
+
account: this.state.selector.account,
|
|
231
|
+
});
|
|
232
|
+
this.setStateAndUpdateStage({ selector: this.state.selector });
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
public handleKindChange = (kind: string): void => {
|
|
236
|
+
this.modeDelegate().handleKindChange(kind);
|
|
237
|
+
this.search$.next({ kind: kind, namespace: this.state.selector.location, account: this.state.selector.account });
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
private handleNameChange = (selectedName: string): void => {
|
|
241
|
+
const { kind } = parseSpinnakerName(this.state.selector.manifestName);
|
|
242
|
+
this.state.selector.manifestName = kind ? `${kind} ${selectedName}` : ` ${selectedName}`;
|
|
243
|
+
this.setStateAndUpdateStage({ selector: this.state.selector });
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
private isExpression = (value: string): boolean => (typeof value === 'string' ? value.includes('${') : false);
|
|
247
|
+
|
|
248
|
+
private search = (kind: string, namespace: string, account: string): PromiseLike<string[]> => {
|
|
249
|
+
if (this.isExpression(account)) {
|
|
250
|
+
return $q.resolve([]);
|
|
251
|
+
}
|
|
252
|
+
return ManifestKindSearchService.search(kind, namespace, account).then((results) =>
|
|
253
|
+
results.map((result) => result.name).sort(),
|
|
254
|
+
);
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
private handleModeSelect = (mode: SelectorMode) => {
|
|
258
|
+
this.state.selector.mode = mode;
|
|
259
|
+
this.setStateAndUpdateStage({ selector: this.state.selector }, () => {
|
|
260
|
+
this.modeDelegate().handleModeChange();
|
|
261
|
+
});
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
private handleClusterChange = ({ clusterName }: { clusterName: string }) => {
|
|
265
|
+
this.state.selector.cluster = clusterName;
|
|
266
|
+
this.setStateAndUpdateStage({ selector: this.state.selector });
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
private handleCriteriaChange = (criteria: string) => {
|
|
270
|
+
this.state.selector.criteria = criteria;
|
|
271
|
+
this.setStateAndUpdateStage({ selector: this.state.selector });
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
public handleKindsChange = (kinds: string[]): void => {
|
|
275
|
+
this.state.selector.kinds = kinds;
|
|
276
|
+
this.setStateAndUpdateStage({ selector: this.state.selector });
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
public handleLabelSelectorsChange = (labelSelectors: IManifestLabelSelector[]): void => {
|
|
280
|
+
this.state.selector.labelSelectors.selectors = labelSelectors;
|
|
281
|
+
this.setStateAndUpdateStage({ selector: this.state.selector });
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
private modeDelegate = (): ISelectorHandler =>
|
|
285
|
+
this.handlers.find((handler) => handler.handles(this.getSelectedMode()));
|
|
286
|
+
|
|
287
|
+
private promptTextCreator = (text: string) => `Use custom expression: ${text}`;
|
|
288
|
+
|
|
289
|
+
private getSelectedMode = (): SelectorMode => this.state.selector.mode || SelectorMode.Static;
|
|
290
|
+
|
|
291
|
+
private getFilteredClusters = (): string[] => {
|
|
292
|
+
const { application, includeSpinnakerKinds } = this.props;
|
|
293
|
+
const { selector } = this.state;
|
|
294
|
+
const applications = application ? [application] : [];
|
|
295
|
+
// If the only allowlisted Spinnaker kind is `serverGroups`, exclude server groups with `serverGroupManagers`.
|
|
296
|
+
// This is because traffic management stages only allow ReplicaSets.
|
|
297
|
+
const includeServerGroupsWithManagers: boolean =
|
|
298
|
+
isEmpty(includeSpinnakerKinds) || includeSpinnakerKinds.length > 1 || includeSpinnakerKinds[0] !== 'serverGroups';
|
|
299
|
+
const filter = (serverGroup: IServerGroup): boolean => {
|
|
300
|
+
const accountAndNamespaceFilter: boolean = AppListExtractor.clusterFilterForCredentialsAndRegion(
|
|
301
|
+
selector.account,
|
|
302
|
+
selector.location,
|
|
303
|
+
)(serverGroup);
|
|
304
|
+
const hasServerGroupManagers: boolean = get(serverGroup, 'serverGroupManagers.length', 0) > 0;
|
|
305
|
+
const serverGroupManagerFilter: boolean = includeServerGroupsWithManagers || !hasServerGroupManagers;
|
|
306
|
+
const nameToParseKind: string = hasServerGroupManagers ? serverGroup.cluster : serverGroup.name;
|
|
307
|
+
const kindFilter: boolean = parseSpinnakerName(nameToParseKind).kind === this.modeDelegate().getKind();
|
|
308
|
+
return accountAndNamespaceFilter && serverGroupManagerFilter && kindFilter;
|
|
309
|
+
};
|
|
310
|
+
return AppListExtractor.getClusters(applications, filter);
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
public render() {
|
|
314
|
+
const { TargetSelect } = NgReact;
|
|
315
|
+
const selectedMode = this.getSelectedMode();
|
|
316
|
+
const modes = this.props.modes || [selectedMode];
|
|
317
|
+
const { selector, accounts, kinds, namespaces, resources, loading } = this.state;
|
|
318
|
+
const kind = this.modeDelegate().getKind();
|
|
319
|
+
const name = parseSpinnakerName(selector.manifestName).name;
|
|
320
|
+
const resourceNames = resources.map((resource) => parseSpinnakerName(resource).name);
|
|
321
|
+
const selectedKinds = selector.kinds || [];
|
|
322
|
+
const KindField = (
|
|
323
|
+
<StageConfigField label="Kind">
|
|
324
|
+
<Creatable
|
|
325
|
+
clearable={false}
|
|
326
|
+
value={{ value: kind, label: kind }}
|
|
327
|
+
options={kinds.map((k) => ({ value: k, label: k }))}
|
|
328
|
+
onChange={(option: Option<string>) => this.handleKindChange(option && option.value)}
|
|
329
|
+
promptTextCreator={this.promptTextCreator}
|
|
330
|
+
/>
|
|
331
|
+
</StageConfigField>
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
return (
|
|
335
|
+
<>
|
|
336
|
+
<StageConfigField label="Account">
|
|
337
|
+
<AccountSelectInput
|
|
338
|
+
value={selector.account}
|
|
339
|
+
onChange={(evt: any) => this.handleAccountChange(evt.target.value)}
|
|
340
|
+
accounts={accounts}
|
|
341
|
+
provider="'kubernetes'"
|
|
342
|
+
/>
|
|
343
|
+
</StageConfigField>
|
|
344
|
+
<StageConfigField label="Namespace">
|
|
345
|
+
<Creatable
|
|
346
|
+
clearable={false}
|
|
347
|
+
value={{ value: selector.location, label: selector.location }}
|
|
348
|
+
options={namespaces.map((ns) => ({ value: ns, label: ns }))}
|
|
349
|
+
onChange={this.handleNamespaceChange}
|
|
350
|
+
promptTextCreator={this.promptTextCreator}
|
|
351
|
+
/>
|
|
352
|
+
</StageConfigField>
|
|
353
|
+
{!modes.includes(SelectorMode.Label) && KindField}
|
|
354
|
+
{modes.length > 1 && (
|
|
355
|
+
<StageConfigField label="Selector">
|
|
356
|
+
{modes.map((mode) => (
|
|
357
|
+
<div className="radio" key={mode}>
|
|
358
|
+
<label htmlFor={mode}>
|
|
359
|
+
<input
|
|
360
|
+
type="radio"
|
|
361
|
+
onChange={() => this.handleModeSelect(mode)}
|
|
362
|
+
checked={selectedMode === mode}
|
|
363
|
+
id={mode}
|
|
364
|
+
/>{' '}
|
|
365
|
+
{get(SelectorModeDataMap, [mode, 'label'], '')}
|
|
366
|
+
</label>
|
|
367
|
+
</div>
|
|
368
|
+
))}
|
|
369
|
+
</StageConfigField>
|
|
370
|
+
)}
|
|
371
|
+
{modes.includes(SelectorMode.Label) && selectedMode !== SelectorMode.Label && KindField}
|
|
372
|
+
{modes.includes(SelectorMode.Static) && selectedMode === SelectorMode.Static && (
|
|
373
|
+
<StageConfigField label="Name">
|
|
374
|
+
<Creatable
|
|
375
|
+
isLoading={loading}
|
|
376
|
+
clearable={false}
|
|
377
|
+
value={{ value: name, label: name }}
|
|
378
|
+
options={resourceNames.map((r) => ({ value: r, label: r }))}
|
|
379
|
+
onChange={(option: Option) => this.handleNameChange(option ? (option.value as string) : '')}
|
|
380
|
+
promptTextCreator={this.promptTextCreator}
|
|
381
|
+
/>
|
|
382
|
+
</StageConfigField>
|
|
383
|
+
)}
|
|
384
|
+
{modes.includes(SelectorMode.Dynamic) && selectedMode === SelectorMode.Dynamic && (
|
|
385
|
+
<>
|
|
386
|
+
<StageConfigField label="Cluster">
|
|
387
|
+
<ScopeClusterSelector
|
|
388
|
+
clusters={this.getFilteredClusters()}
|
|
389
|
+
model={selector.cluster}
|
|
390
|
+
onChange={this.handleClusterChange}
|
|
391
|
+
/>
|
|
392
|
+
</StageConfigField>
|
|
393
|
+
<StageConfigField label="Target">
|
|
394
|
+
<TargetSelect
|
|
395
|
+
onChange={this.handleCriteriaChange}
|
|
396
|
+
model={{ target: selector.criteria }}
|
|
397
|
+
options={StageConstants.MANIFEST_CRITERIA_OPTIONS}
|
|
398
|
+
/>
|
|
399
|
+
</StageConfigField>
|
|
400
|
+
</>
|
|
401
|
+
)}
|
|
402
|
+
{modes.includes(SelectorMode.Label) && selectedMode === SelectorMode.Label && (
|
|
403
|
+
<>
|
|
404
|
+
<StageConfigField label="Kinds">
|
|
405
|
+
<Select
|
|
406
|
+
clearable={false}
|
|
407
|
+
multi={true}
|
|
408
|
+
value={selectedKinds}
|
|
409
|
+
options={kinds.map((k) => ({ value: k, label: k }))}
|
|
410
|
+
onChange={(options: Array<Option<string>>) => this.handleKindsChange(options.map((o) => o.value))}
|
|
411
|
+
/>
|
|
412
|
+
</StageConfigField>
|
|
413
|
+
<StageConfigField label="Labels">
|
|
414
|
+
<LabelEditor
|
|
415
|
+
labelSelectors={get(selector, 'labelSelectors.selectors', [])}
|
|
416
|
+
onLabelSelectorsChange={this.handleLabelSelectorsChange}
|
|
417
|
+
/>
|
|
418
|
+
</StageConfigField>
|
|
419
|
+
</>
|
|
420
|
+
)}
|
|
421
|
+
</>
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { ShallowWrapper } from 'enzyme';
|
|
2
|
+
import { shallow } from 'enzyme';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
|
|
5
|
+
import type { ILabelEditorProps } from './LabelEditor';
|
|
6
|
+
import LabelEditor from './LabelEditor';
|
|
7
|
+
|
|
8
|
+
describe('<LabelEditor />', () => {
|
|
9
|
+
let onChangeSpy: jasmine.Spy;
|
|
10
|
+
let props: ILabelEditorProps;
|
|
11
|
+
let component: ShallowWrapper<LabelEditor>;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
onChangeSpy = jasmine.createSpy('onChangeSpy');
|
|
15
|
+
props = {
|
|
16
|
+
labelSelectors: [
|
|
17
|
+
{
|
|
18
|
+
key: 'my-label-1',
|
|
19
|
+
kind: 'EQUALS',
|
|
20
|
+
values: ['my-value-1', 'my-value-2'],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
key: 'my-label-2',
|
|
24
|
+
kind: 'NOT_EQUALS',
|
|
25
|
+
values: ['my-value-3'],
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
onLabelSelectorsChange: onChangeSpy,
|
|
29
|
+
};
|
|
30
|
+
component = shallow(<LabelEditor {...props} />);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('view', () => {
|
|
34
|
+
it('renders a row for each label selector', () => {
|
|
35
|
+
expect(component.find('.label-editor-selector-row').length).toEqual(props.labelSelectors.length);
|
|
36
|
+
});
|
|
37
|
+
it('renders selector values as comma-separated lists', () => {
|
|
38
|
+
expect(component.find('.label-editor-values-input').at(0).props().value).toEqual('my-value-1, my-value-2');
|
|
39
|
+
expect(component.find('.label-editor-values-input').at(1).props().value).toEqual('my-value-3');
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('functionality', () => {
|
|
44
|
+
it('calls `props.onLabelSelectorsChange` when selector properties are changed', () => {
|
|
45
|
+
component
|
|
46
|
+
.find('.label-editor-key-input')
|
|
47
|
+
.at(0)
|
|
48
|
+
.simulate('change', { target: { value: 'my-label-1-edited' } });
|
|
49
|
+
expect(onChangeSpy).toHaveBeenCalledWith([
|
|
50
|
+
{
|
|
51
|
+
...props.labelSelectors[0],
|
|
52
|
+
key: 'my-label-1-edited',
|
|
53
|
+
},
|
|
54
|
+
props.labelSelectors[1],
|
|
55
|
+
]);
|
|
56
|
+
component
|
|
57
|
+
.find('.label-editor-values-input')
|
|
58
|
+
.at(1)
|
|
59
|
+
.simulate('change', { target: { value: 'my-value-3, my-value-4' } });
|
|
60
|
+
expect(onChangeSpy).toHaveBeenCalledWith([
|
|
61
|
+
props.labelSelectors[0],
|
|
62
|
+
{
|
|
63
|
+
...props.labelSelectors[1],
|
|
64
|
+
values: ['my-value-3', 'my-value-4'],
|
|
65
|
+
},
|
|
66
|
+
]);
|
|
67
|
+
});
|
|
68
|
+
it('handles adding label selectors', () => {
|
|
69
|
+
component.find('.add-new').simulate('click');
|
|
70
|
+
expect(onChangeSpy).toHaveBeenCalledWith([...props.labelSelectors, { key: '', kind: 'EQUALS', values: [] }]);
|
|
71
|
+
});
|
|
72
|
+
it('handles removing label selectors', () => {
|
|
73
|
+
component.find('.label-editor-remove').at(1).simulate('click');
|
|
74
|
+
expect(onChangeSpy).toHaveBeenCalledWith([
|
|
75
|
+
{
|
|
76
|
+
...props.labelSelectors[0],
|
|
77
|
+
},
|
|
78
|
+
]);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Option } from 'react-select';
|
|
3
|
+
import Select from 'react-select';
|
|
4
|
+
|
|
5
|
+
import { noop } from '@spinnaker/core';
|
|
6
|
+
|
|
7
|
+
import type { IManifestLabelSelector } from '../IManifestLabelSelector';
|
|
8
|
+
import { LABEL_KINDS } from '../IManifestLabelSelector';
|
|
9
|
+
|
|
10
|
+
import './labelEditor.less';
|
|
11
|
+
|
|
12
|
+
export interface ILabelEditorProps {
|
|
13
|
+
labelSelectors: IManifestLabelSelector[];
|
|
14
|
+
onLabelSelectorsChange: (labelSelectors: IManifestLabelSelector[]) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const LabelKeyOptions: Array<Option<string>> = LABEL_KINDS.map((kind) => ({ label: kind, value: kind }));
|
|
18
|
+
|
|
19
|
+
export default class LabelEditor extends React.Component<ILabelEditorProps> {
|
|
20
|
+
public static defaultProps: Partial<ILabelEditorProps> = {
|
|
21
|
+
labelSelectors: [],
|
|
22
|
+
onLabelSelectorsChange: noop,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
private static convertValueStringToArray = (value: string): string[] => {
|
|
26
|
+
return value.split(',').map((v: string) => v.trim());
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
private handleChange = (idx: number, property: keyof IManifestLabelSelector, value: string | string[]): void => {
|
|
30
|
+
this.props.onLabelSelectorsChange(
|
|
31
|
+
this.props.labelSelectors.map((ls, i) => {
|
|
32
|
+
if (idx !== i) {
|
|
33
|
+
return ls;
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
...ls,
|
|
37
|
+
[property]: value,
|
|
38
|
+
};
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
private removeField = (idx: number): void => {
|
|
44
|
+
this.props.onLabelSelectorsChange(
|
|
45
|
+
this.props.labelSelectors.filter((_ls, i) => {
|
|
46
|
+
return idx !== i;
|
|
47
|
+
}),
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
private addField = (): void => {
|
|
52
|
+
return this.props.onLabelSelectorsChange(
|
|
53
|
+
this.props.labelSelectors.concat([
|
|
54
|
+
{
|
|
55
|
+
key: '',
|
|
56
|
+
kind: 'EQUALS',
|
|
57
|
+
values: [],
|
|
58
|
+
},
|
|
59
|
+
]),
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
public render() {
|
|
64
|
+
return (
|
|
65
|
+
<table className="table table-condensed packed tags">
|
|
66
|
+
<thead>
|
|
67
|
+
<tr>
|
|
68
|
+
<th>Key</th>
|
|
69
|
+
<th>Kind</th>
|
|
70
|
+
<th>Value(s)</th>
|
|
71
|
+
<th />
|
|
72
|
+
</tr>
|
|
73
|
+
</thead>
|
|
74
|
+
<tbody>
|
|
75
|
+
{this.props.labelSelectors.map((selector, idx) => (
|
|
76
|
+
<tr className="label-editor-selector-row" key={idx}>
|
|
77
|
+
<td>
|
|
78
|
+
<input
|
|
79
|
+
className="form-control input input-sm label-editor-key-input"
|
|
80
|
+
type="text"
|
|
81
|
+
value={selector.key}
|
|
82
|
+
onChange={(e: any) => this.handleChange(idx, 'key', e.target.value)}
|
|
83
|
+
/>
|
|
84
|
+
</td>
|
|
85
|
+
<td>
|
|
86
|
+
<Select
|
|
87
|
+
className="label-editor-kind-select"
|
|
88
|
+
clearable={false}
|
|
89
|
+
onChange={(option: Option<string>) => this.handleChange(idx, 'kind', option.value)}
|
|
90
|
+
options={LabelKeyOptions}
|
|
91
|
+
value={selector.kind}
|
|
92
|
+
/>
|
|
93
|
+
</td>
|
|
94
|
+
<td>
|
|
95
|
+
<input
|
|
96
|
+
className="form-control input input-sm label-editor-values-input"
|
|
97
|
+
onChange={(e: any) =>
|
|
98
|
+
this.handleChange(idx, 'values', LabelEditor.convertValueStringToArray(e.target.value))
|
|
99
|
+
}
|
|
100
|
+
placeholder="Comma-separated values"
|
|
101
|
+
value={selector.values.join(', ')}
|
|
102
|
+
type="text"
|
|
103
|
+
/>
|
|
104
|
+
</td>
|
|
105
|
+
<td>
|
|
106
|
+
<button className="link label-editor-remove" onClick={() => this.removeField(idx)}>
|
|
107
|
+
<span className="glyphicon glyphicon-trash" />
|
|
108
|
+
<span className="sr-only">Remove field</span>
|
|
109
|
+
</button>
|
|
110
|
+
</td>
|
|
111
|
+
</tr>
|
|
112
|
+
))}
|
|
113
|
+
</tbody>
|
|
114
|
+
<tfoot>
|
|
115
|
+
<tr>
|
|
116
|
+
<td colSpan={4}>
|
|
117
|
+
<button className="btn btn-block btn-sm add-new" onClick={this.addField}>
|
|
118
|
+
<span className="glyphicon glyphicon-plus-sign" /> Add Label
|
|
119
|
+
</button>
|
|
120
|
+
</td>
|
|
121
|
+
</tr>
|
|
122
|
+
</tfoot>
|
|
123
|
+
</table>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { module } from 'angular';
|
|
2
|
+
import { react2angular } from 'react2angular';
|
|
3
|
+
|
|
4
|
+
import { withErrorBoundary } from '@spinnaker/core';
|
|
5
|
+
|
|
6
|
+
import { ManifestSelector } from './ManifestSelector';
|
|
7
|
+
|
|
8
|
+
export const KUBERNETES_MANIFEST_SELECTOR = 'spinnaker.kubernetes.v2.manifest.selector.component';
|
|
9
|
+
module(KUBERNETES_MANIFEST_SELECTOR, []).component(
|
|
10
|
+
'kubernetesManifestSelector',
|
|
11
|
+
react2angular(withErrorBoundary(ManifestSelector, 'kubernetesManifestSelector'), [
|
|
12
|
+
'selector',
|
|
13
|
+
'modes',
|
|
14
|
+
'application',
|
|
15
|
+
'onChange',
|
|
16
|
+
]),
|
|
17
|
+
);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { DateTime } from 'luxon';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import { relativeTime } from '@spinnaker/core';
|
|
5
|
+
|
|
6
|
+
export interface IKubernetesManifestCondition {
|
|
7
|
+
status: string;
|
|
8
|
+
type: string;
|
|
9
|
+
lastTransitionTime: string;
|
|
10
|
+
message: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface IKubernetesManifestConditionProps {
|
|
14
|
+
condition: IKubernetesManifestCondition;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class ManifestCondition extends React.Component<IKubernetesManifestConditionProps> {
|
|
18
|
+
public render() {
|
|
19
|
+
const { condition } = this.props;
|
|
20
|
+
const transitionTime = DateTime.fromISO(condition.lastTransitionTime);
|
|
21
|
+
return [
|
|
22
|
+
<span key="properties">
|
|
23
|
+
{condition.status === 'True' && <span style={{ marginRight: '3px' }} className="glyphicon glyphicon-Normal" />}
|
|
24
|
+
{condition.status === 'False' && <span style={{ marginRight: '3px' }} className="glyphicon glyphicon-Warn" />}
|
|
25
|
+
{condition.status === 'Unknown' && <span style={{ marginRight: '3px' }}>?</span>}
|
|
26
|
+
<b style={{ marginRight: '3px' }}>{condition.type}</b>
|
|
27
|
+
<i>{transitionTime.isValid && relativeTime(transitionTime.toMillis())}</i>
|
|
28
|
+
</span>,
|
|
29
|
+
<div key="message">{condition.message}</div>,
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
}
|