From ee89a64d3add95176671be9bf726a50e67eb70a8 Mon Sep 17 00:00:00 2001 From: Ethan Rogers Date: Wed, 10 Apr 2019 15:20:47 -0400 Subject: [PATCH] feat(kuberntes): v2 runJob (#6831) UI support for the V2 Run Job stage --- .../kubernetes/src/v2/kubernetes.v2.module.ts | 4 ++ .../runJob/KubernetesV2RunJobStageConfig.tsx | 63 ++++++++++++++++ .../stages/runJob/RunJobExecutionDetails.tsx | 72 +++++++++++++++++++ .../stages/runJob/RunJobLogsModal.tsx | 32 +++++++++ .../v2/pipelines/stages/runJob/runJobStage.ts | 51 +++++++++++++ 5 files changed, 222 insertions(+) create mode 100644 app/scripts/modules/kubernetes/src/v2/pipelines/stages/runJob/KubernetesV2RunJobStageConfig.tsx create mode 100644 app/scripts/modules/kubernetes/src/v2/pipelines/stages/runJob/RunJobExecutionDetails.tsx create mode 100644 app/scripts/modules/kubernetes/src/v2/pipelines/stages/runJob/RunJobLogsModal.tsx create mode 100644 app/scripts/modules/kubernetes/src/v2/pipelines/stages/runJob/runJobStage.ts diff --git a/app/scripts/modules/kubernetes/src/v2/kubernetes.v2.module.ts b/app/scripts/modules/kubernetes/src/v2/kubernetes.v2.module.ts index 4fb8e5a436a..fef99d589ed 100644 --- a/app/scripts/modules/kubernetes/src/v2/kubernetes.v2.module.ts +++ b/app/scripts/modules/kubernetes/src/v2/kubernetes.v2.module.ts @@ -3,6 +3,7 @@ import { module } from 'angular'; import { CloudProviderRegistry, STAGE_ARTIFACT_SELECTOR_COMPONENT_REACT, YAML_EDITOR_COMPONENT } from '@spinnaker/core'; import '../logo/kubernetes.logo.less'; + import { KUBERNETES_MANIFEST_BASIC_SETTINGS } from './manifest/wizard/basicSettings.component'; import { KUBERNETES_MANIFEST_DELETE_CTRL } from './manifest/delete/delete.controller'; import { KUBERNETES_MANIFEST_SCALE_CTRL } from './manifest/scale/scale.controller'; @@ -41,6 +42,8 @@ import { JSON_EDITOR_COMPONENT } from './manifest/editor/json/jsonEditor.compone import { ManifestWizard } from 'kubernetes/v2/manifest/wizard/ManifestWizard'; import { KUBERNETES_ENABLE_MANIFEST_STAGE } from 'kubernetes/v2/pipelines/stages/traffic/enableManifest.stage'; import { KUBERNETES_DISABLE_MANIFEST_STAGE } from 'kubernetes/v2/pipelines/stages/traffic/disableManifest.stage'; +import { KUBERNETES_V2_RUN_JOB_STAGE } from 'kubernetes/v2/pipelines/stages/runJob/runJobStage'; + import './pipelines/validation/manifestSelector.validator'; // load all templates into the $templateCache @@ -93,6 +96,7 @@ module(KUBERNETES_V2_MODULE, [ KUBERNETES_ENABLE_MANIFEST_STAGE, KUBERNETES_DISABLE_MANIFEST_STAGE, STAGE_ARTIFACT_SELECTOR_COMPONENT_REACT, + KUBERNETES_V2_RUN_JOB_STAGE, ]).config(() => { CloudProviderRegistry.registerProvider('kubernetes', { name: 'Kubernetes', diff --git a/app/scripts/modules/kubernetes/src/v2/pipelines/stages/runJob/KubernetesV2RunJobStageConfig.tsx b/app/scripts/modules/kubernetes/src/v2/pipelines/stages/runJob/KubernetesV2RunJobStageConfig.tsx new file mode 100644 index 00000000000..7e0692003bc --- /dev/null +++ b/app/scripts/modules/kubernetes/src/v2/pipelines/stages/runJob/KubernetesV2RunJobStageConfig.tsx @@ -0,0 +1,63 @@ +import * as React from 'react'; + +import { IStageConfigProps, AccountService, YamlEditor, yamlDocumentsToString, IAccount } from '@spinnaker/core'; + +import { ManifestBasicSettings } from 'kubernetes/v2/manifest/wizard/BasicSettings'; + +export interface IKubernetesRunJobStageConfigState { + credentials: IAccount[]; + rawManifest?: string; +} + +export class KubernetesV2RunJobStageConfig extends React.Component { + public state: IKubernetesRunJobStageConfigState = { + credentials: [], + }; + + public accountChanged = (account: string) => { + this.props.updateStageField({ + credentails: account, + account: account, + }); + }; + + public handleRawManifestChange = (rawManifest: string, manifests: any) => { + if (manifests) { + this.props.updateStageField({ manifest: manifests[0] }); + } + this.setState({ rawManifest }); + }; + + public initRawManifest() { + const { stage } = this.props; + if (stage.manifest) { + this.setState({ rawManifest: yamlDocumentsToString([stage.manifest]) }); + } + } + + public componentDidMount() { + this.props.updateStageField({ cloudProvider: 'kubernetes' }); + AccountService.getAllAccountDetailsForProvider('kubernetes', 'v2').then((accounts: any) => { + this.setState({ credentials: accounts }); + }); + this.initRawManifest(); + } + + public render() { + const { application, stage } = this.props; + + return ( +
+

Basic Settings

+ this.accountChanged(selectedAccount)} + /> +

Manifest Configuration

+ +
+ ); + } +} diff --git a/app/scripts/modules/kubernetes/src/v2/pipelines/stages/runJob/RunJobExecutionDetails.tsx b/app/scripts/modules/kubernetes/src/v2/pipelines/stages/runJob/RunJobExecutionDetails.tsx new file mode 100644 index 00000000000..69b9d1b816b --- /dev/null +++ b/app/scripts/modules/kubernetes/src/v2/pipelines/stages/runJob/RunJobExecutionDetails.tsx @@ -0,0 +1,72 @@ +import * as React from 'react'; +import { get } from 'lodash'; + +import { RunJobLogsModal, IRunJobLogsModalProps } from './RunJobLogsModal'; + +import { + IExecutionDetailsSectionProps, + ExecutionDetailsSection, + AccountTag, + ReactModal, + ReactInjector, +} from '@spinnaker/core'; + +export class RunJobExecutionDetails extends React.Component { + public static title = 'runJobConfig'; + + public showLogsModal = (_event: any): void => { + const { stage, execution } = this.props; + const { executionService } = ReactInjector; + executionService.getExecution(execution.id).then((fullExecution: any) => { + const fullStage = fullExecution.stages.find((s: any) => s.id === stage.id); + if (!fullStage) { + return; + } + + const modalProps = { dialogClassName: 'modal-lg modal-fullscreen' }; + ReactModal.show( + RunJobLogsModal, + { + logs: get(fullStage, 'context.jobStatus.logs', 'No log output found.'), + } as IRunJobLogsModalProps, + modalProps, + ); + }); + }; + + public render() { + const { stage, name, current } = this.props; + const { context } = stage; + + return ( + +
+
+
+
Account
+
+ +
+
+ {stage.context.jobStatus && stage.context.jobStatus.location && ( +
+
Namespace
+
{stage.context.jobStatus.location}
+
+ )} +
+ {stage.context.jobStatus && stage.context.jobStatus.logs && ( +
+
+
Logs
+
+ Console Output (Raw) +
+
+
+ )} +
+
+ ); + } +} diff --git a/app/scripts/modules/kubernetes/src/v2/pipelines/stages/runJob/RunJobLogsModal.tsx b/app/scripts/modules/kubernetes/src/v2/pipelines/stages/runJob/RunJobLogsModal.tsx new file mode 100644 index 00000000000..ccaa7e2101d --- /dev/null +++ b/app/scripts/modules/kubernetes/src/v2/pipelines/stages/runJob/RunJobLogsModal.tsx @@ -0,0 +1,32 @@ +import * as React from 'react'; + +import { IModalComponentProps } from '@spinnaker/core'; + +export interface IRunJobLogsModalProps extends IModalComponentProps { + logs: string; +} + +export class RunJobLogsModal extends React.Component { + constructor(props: IRunJobLogsModalProps) { + super(props); + } + + public render() { + const { dismissModal } = this.props; + return ( +
+
+

Execution Logs

+
+
+
{this.props.logs || ''}
+
+
+ +
+
+ ); + } +} diff --git a/app/scripts/modules/kubernetes/src/v2/pipelines/stages/runJob/runJobStage.ts b/app/scripts/modules/kubernetes/src/v2/pipelines/stages/runJob/runJobStage.ts new file mode 100644 index 00000000000..e7fe274c16c --- /dev/null +++ b/app/scripts/modules/kubernetes/src/v2/pipelines/stages/runJob/runJobStage.ts @@ -0,0 +1,51 @@ +import { module } from 'angular'; + +import { + Registry, + ExecutionDetailsTasks, + IPipeline, + IStage, + IValidatorConfig, + IStageOrTriggerTypeConfig, + ICustomValidator, +} from '@spinnaker/core'; + +import { KubernetesV2RunJobStageConfig } from './KubernetesV2RunJobStageConfig'; +import { RunJobExecutionDetails } from './RunJobExecutionDetails'; + +export const KUBERNETES_V2_RUN_JOB_STAGE = 'spinnaker.kubernetes.v2.pipeline.stage.runJob'; + +module(KUBERNETES_V2_RUN_JOB_STAGE, []) + .config(() => { + Registry.pipeline.registerStage({ + label: 'Run Job (Manifest)', + description: 'Run a Kubernetes Job mainfest yaml/json file.', + key: 'runJobManifest', + alias: 'runJob', + addAliasToConfig: true, + cloudProvider: 'kubernetes', + component: KubernetesV2RunJobStageConfig, + executionDetailsSections: [ExecutionDetailsTasks, RunJobExecutionDetails], + defaultTimeoutMs: 30 * 60 * 1000, + validators: [ + { + type: 'custom', + validate: ( + _pipeline: IPipeline, + stage: IStage, + _validator: IValidatorConfig, + _config: IStageOrTriggerTypeConfig, + ): string => { + if (!stage.manifest || !stage.manifest.kind) { + return ''; + } + if (stage.manifest.kind !== 'Job') { + return 'Run Job (Manifest) only accepts manifest of kind Job.'; + } + return ''; + }, + } as ICustomValidator, + ], + }); + }) + .controller('KubernetesV2RunJobStageConfig', KubernetesV2RunJobStageConfig);