-
Notifications
You must be signed in to change notification settings - Fork 900
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(k8s): Enable new artifacts workflow in Patch Manifest (#7109)
* feat(k8s): Enable new artifacts workflow in Patch Manifest * fix merge conflict
- Loading branch information
1 parent
ce194af
commit aca6626
Showing
9 changed files
with
275 additions
and
319 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
...pts/modules/kubernetes/src/v2/pipelines/stages/patchManifest/PatchManifestOptionsForm.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import * as React from 'react'; | ||
import { CheckboxInput, StageConfigField } from '@spinnaker/core'; | ||
import Select, { Option } from 'react-select'; | ||
|
||
export interface IPatchManifestOptionsFormProps { | ||
record: boolean; | ||
onRecordChange: (record: boolean) => void; | ||
strategy: string; | ||
onStrategyChange: (strategy: string) => void; | ||
} | ||
|
||
export const PatchManifestOptionsForm: React.FunctionComponent<IPatchManifestOptionsFormProps> = ( | ||
props: IPatchManifestOptionsFormProps, | ||
) => { | ||
return ( | ||
<div className="form-horizontal"> | ||
<StageConfigField label="Record Patch Annotation" helpKey="kubernetes.manifest.patch.record"> | ||
<CheckboxInput | ||
checked={props.record} | ||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => props.onRecordChange(e.target.checked)} | ||
/> | ||
</StageConfigField> | ||
<StageConfigField label="Merge Strategy" helpKey="kubernetes.manifest.patch.mergeStrategy" fieldColumns={3}> | ||
<Select | ||
clearable={false} | ||
value={props.strategy} | ||
options={['strategic', 'json', 'merge'].map(k => ({ value: k, label: k }))} | ||
onChange={(e: Option<string>) => props.onStrategyChange(e.value)} | ||
/> | ||
</StageConfigField> | ||
</div> | ||
); | ||
}; |
58 changes: 58 additions & 0 deletions
58
...pts/modules/kubernetes/src/v2/pipelines/stages/patchManifest/PatchManifestStageConfig.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import * as React from 'react'; | ||
import { defaults } from 'lodash'; | ||
import { Observable, Subject } from 'rxjs'; | ||
|
||
import { FormikStageConfig, IStage, IStageConfigProps } from '@spinnaker/core'; | ||
import { KubernetesManifestCommandBuilder } from 'kubernetes/v2/manifest/manifestCommandBuilder.service'; | ||
import { PatchManifestStageForm } from './PatchManifestStageForm'; | ||
|
||
export class PatchManifestStageConfig extends React.Component<IStageConfigProps> { | ||
private readonly stage: IStage; | ||
private destroy$ = new Subject(); | ||
|
||
public constructor(props: IStageConfigProps) { | ||
super(props); | ||
|
||
// Intentionally initializing the stage config only once in the constructor | ||
// The stage config is then completely owned within FormikStageConfig's Formik state | ||
this.stage = props.stage; | ||
} | ||
|
||
public componentDidMount(): void { | ||
Observable.fromPromise( | ||
KubernetesManifestCommandBuilder.buildNewManifestCommand( | ||
this.props.application, | ||
this.stage.patchBody, | ||
this.stage.moniker, | ||
), | ||
) | ||
.takeUntil(this.destroy$) | ||
.subscribe(builtCommand => { | ||
if (this.stage.isNew) { | ||
defaults(this.stage, { | ||
account: builtCommand.command.account, | ||
manifestArtifactAccount: 'embedded-artifact', | ||
patchBody: builtCommand.command.manifest, | ||
source: 'text', | ||
options: { | ||
record: true, | ||
strategy: 'strategic', | ||
}, | ||
location: '', | ||
cloudProvider: 'kubernetes', | ||
}); | ||
} | ||
}); | ||
} | ||
|
||
public render() { | ||
return ( | ||
<FormikStageConfig | ||
{...this.props} | ||
stage={this.stage} | ||
onChange={this.props.updateStage} | ||
render={props => <PatchManifestStageForm {...props} updatePipeline={this.props.updatePipeline} />} | ||
/> | ||
); | ||
} | ||
} |
166 changes: 166 additions & 0 deletions
166
...ripts/modules/kubernetes/src/v2/pipelines/stages/patchManifest/PatchManifestStageForm.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
import * as React from 'react'; | ||
import { Option } from 'react-select'; | ||
import { capitalize, get, isEmpty, map } from 'lodash'; | ||
|
||
import { | ||
ArtifactTypePatterns, | ||
IArtifact, | ||
IExpectedArtifact, | ||
IFormikStageConfigInjectedProps, | ||
IPipeline, | ||
RadioButtonInput, | ||
StageArtifactSelectorDelegate, | ||
StageConfigField, | ||
yamlDocumentsToString, | ||
YamlEditor, | ||
} from '@spinnaker/core'; | ||
|
||
import { ManifestBindArtifactsSelectorDelegate } from 'kubernetes/v2/pipelines/stages/deployManifest/ManifestBindArtifactsSelectorDelegate'; | ||
import { IManifestBindArtifact } from 'kubernetes/v2/pipelines/stages/deployManifest/ManifestBindArtifactsSelector'; | ||
import { ManifestSelector } from 'kubernetes/v2/manifest/selector/ManifestSelector'; | ||
import { SelectorMode } from 'kubernetes/v2/manifest/selector/IManifestSelector'; | ||
import { PatchManifestOptionsForm } from './PatchManifestOptionsForm'; | ||
|
||
interface IPatchManifestStageConfigFormProps { | ||
updatePipeline: (pipeline: IPipeline) => void; | ||
} | ||
|
||
interface IPatchManifestStageConfigFormState { | ||
rawManifest: string; | ||
} | ||
|
||
export class PatchManifestStageForm extends React.Component< | ||
IPatchManifestStageConfigFormProps & IFormikStageConfigInjectedProps, | ||
IPatchManifestStageConfigFormState | ||
> { | ||
public readonly textSource = 'text'; | ||
public readonly artifactSource = 'artifact'; | ||
private readonly excludedManifestArtifactTypes = [ | ||
ArtifactTypePatterns.DOCKER_IMAGE, | ||
ArtifactTypePatterns.KUBERNETES, | ||
ArtifactTypePatterns.FRONT50_PIPELINE_TEMPLATE, | ||
ArtifactTypePatterns.EMBEDDED_BASE64, | ||
ArtifactTypePatterns.MAVEN_FILE, | ||
]; | ||
|
||
public constructor(props: IPatchManifestStageConfigFormProps & IFormikStageConfigInjectedProps) { | ||
super(props); | ||
const patchBody: string = get(props.formik.values, 'patchBody'); | ||
const isTextManifest: boolean = get(props.formik.values, 'source') === this.textSource; | ||
this.state = { | ||
rawManifest: !isEmpty(patchBody) && isTextManifest ? yamlDocumentsToString([patchBody]) : '', | ||
}; | ||
} | ||
|
||
private onManifestArtifactSelected = (expectedArtifactId: string): void => { | ||
this.props.formik.setFieldValue('manifestArtifactId', expectedArtifactId); | ||
this.props.formik.setFieldValue('manifestArtifact', null); | ||
}; | ||
|
||
private onManifestArtifactEdited = (artifact: IArtifact) => { | ||
this.props.formik.setFieldValue('manifestArtifactId', null); | ||
this.props.formik.setFieldValue('manifestArtifact', artifact); | ||
}; | ||
|
||
private onManifestArtifactAccountSelected = (accountName: string): void => { | ||
this.props.formik.setFieldValue('manifestArtifactAccount', accountName); | ||
}; | ||
|
||
private getRequiredArtifacts = (): IManifestBindArtifact[] => { | ||
const { requiredArtifactIds, requiredArtifacts } = this.props.formik.values; | ||
return (requiredArtifactIds || []) | ||
.map((id: string) => ({ expectedArtifactId: id })) | ||
.concat(requiredArtifacts || []); | ||
}; | ||
|
||
private onRequiredArtifactsChanged = (bindings: IManifestBindArtifact[]): void => { | ||
this.props.formik.setFieldValue( | ||
'requiredArtifactIds', | ||
bindings.filter(b => b.expectedArtifactId).map(b => b.expectedArtifactId), | ||
); | ||
this.props.formik.setFieldValue('requiredArtifacts', bindings.filter(b => b.artifact)); | ||
}; | ||
|
||
private handleRawManifestChange = (rawManifest: string, manifests: any): void => { | ||
this.setState({ | ||
rawManifest, | ||
}); | ||
this.props.formik.setFieldValue('patchBody', manifests[0]); | ||
}; | ||
|
||
private onManifestSelectorChange = (): void => {}; | ||
|
||
private getSourceOptions = (): Array<Option<string>> => { | ||
return map([this.textSource, this.artifactSource], option => ({ | ||
label: capitalize(option), | ||
value: option, | ||
})); | ||
}; | ||
|
||
public render() { | ||
const stage = this.props.formik.values; | ||
return ( | ||
<div className="container-fluid form-horizontal"> | ||
<h4>Resource to Patch</h4> | ||
<ManifestSelector | ||
application={this.props.application} | ||
modes={[SelectorMode.Static, SelectorMode.Dynamic]} | ||
onChange={this.onManifestSelectorChange} | ||
selector={stage as any} | ||
/> | ||
<hr /> | ||
<h4>Patch Content</h4> | ||
<StageConfigField label="Manifest Source" helpKey="kubernetes.manifest.source"> | ||
<RadioButtonInput | ||
options={this.getSourceOptions()} | ||
onChange={(e: any) => this.props.formik.setFieldValue('source', e.target.value)} | ||
value={stage.source || 'text'} | ||
inline={true} | ||
/> | ||
</StageConfigField> | ||
{stage.source === this.textSource && ( | ||
<StageConfigField label="Manifest"> | ||
<YamlEditor onChange={this.handleRawManifestChange} value={this.state.rawManifest} /> | ||
</StageConfigField> | ||
)} | ||
{stage.source === this.artifactSource && ( | ||
<> | ||
<StageArtifactSelectorDelegate | ||
artifact={stage.manifestArtifact} | ||
excludedArtifactTypePatterns={this.excludedManifestArtifactTypes} | ||
expectedArtifactId={stage.manifestArtifactId} | ||
helpKey="kubernetes.manifest.expectedArtifact" | ||
label="Manifest Artifact" | ||
onArtifactEdited={this.onManifestArtifactEdited} | ||
onExpectedArtifactSelected={(artifact: IExpectedArtifact) => this.onManifestArtifactSelected(artifact.id)} | ||
pipeline={this.props.pipeline} | ||
selectedArtifactAccount={stage.manifestArtifactAccount} | ||
selectedArtifactId={stage.manifestArtifactId} | ||
setArtifactAccount={this.onManifestArtifactAccountSelected} | ||
setArtifactId={this.onManifestArtifactSelected} | ||
stage={stage} | ||
updatePipeline={this.props.updatePipeline} | ||
/> | ||
</> | ||
)} | ||
<StageConfigField label="Required Artifacts to Bind" helpKey="kubernetes.manifest.requiredArtifactsToBind"> | ||
<ManifestBindArtifactsSelectorDelegate | ||
bindings={this.getRequiredArtifacts()} | ||
onChangeBindings={this.onRequiredArtifactsChanged} | ||
pipeline={this.props.pipeline} | ||
stage={stage} | ||
/> | ||
</StageConfigField> | ||
|
||
<hr /> | ||
<h4>Patch Options</h4> | ||
<PatchManifestOptionsForm | ||
strategy={!!stage.options && stage.options.strategy} | ||
onStrategyChange={(strategy: string) => this.props.formik.setFieldValue('options.strategy', strategy)} | ||
record={!!stage.options && stage.options.record} | ||
onRecordChange={(record: boolean) => this.props.formik.setFieldValue('options.record', record)} | ||
/> | ||
</div> | ||
); | ||
} | ||
} |
Oops, something went wrong.