-
Notifications
You must be signed in to change notification settings - Fork 903
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(kubernetes): feature-flagged support for kubernetes traffic mana…
…gement strategies (#6816)
- Loading branch information
1 parent
36074c2
commit 2d7f388
Showing
8 changed files
with
234 additions
and
3 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
51 changes: 51 additions & 0 deletions
51
...ules/kubernetes/src/v2/pipelines/stages/deployManifest/ManifestDeploymentOptions.spec.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,51 @@ | ||
import * as React from 'react'; | ||
import { shallow } from 'enzyme'; | ||
|
||
import { StageConfigField } from '@spinnaker/core'; | ||
|
||
import { | ||
IManifestDeploymentOptionsProps, | ||
ManifestDeploymentOptions, | ||
defaultTrafficManagementConfig, | ||
} from './ManifestDeploymentOptions'; | ||
|
||
describe('<ManifestDeploymentOptions />', () => { | ||
const onConfigChangeSpy = jasmine.createSpy('onConfigChangeSpy'); | ||
let wrapper: any; | ||
let props: IManifestDeploymentOptionsProps; | ||
|
||
beforeEach(() => { | ||
props = { | ||
accounts: [], | ||
config: defaultTrafficManagementConfig, | ||
onConfigChange: onConfigChangeSpy, | ||
selectedAccount: null, | ||
}; | ||
wrapper = shallow(<ManifestDeploymentOptions {...props} />); | ||
}); | ||
|
||
describe('view', () => { | ||
it('renders only the enable checkbox when config is disabled', () => { | ||
expect(wrapper.find(StageConfigField).length).toEqual(1); | ||
expect(wrapper.find('input[type="checkbox"]').length).toEqual(1); | ||
}); | ||
it('renders config fields for `namespace`, `services`, and `enableTraffic` when config is enabled', () => { | ||
props.config.enabled = true; | ||
wrapper = shallow(<ManifestDeploymentOptions {...props} />); | ||
expect(wrapper.find(StageConfigField).length).toEqual(4); | ||
}); | ||
}); | ||
|
||
describe('functionality', () => { | ||
it('updates `config.enabled` when enable checkbox is toggled', () => { | ||
wrapper | ||
.find('input[type="checkbox"]') | ||
.at(0) | ||
.simulate('change', { target: { checked: true } }); | ||
expect(onConfigChangeSpy).toHaveBeenCalledWith({ | ||
...defaultTrafficManagementConfig, | ||
enabled: true, | ||
}); | ||
}); | ||
}); | ||
}); |
151 changes: 151 additions & 0 deletions
151
...s/modules/kubernetes/src/v2/pipelines/stages/deployManifest/ManifestDeploymentOptions.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,151 @@ | ||
import { module } from 'angular'; | ||
import * as React from 'react'; | ||
import { react2angular } from 'react2angular'; | ||
import { cloneDeep, find, get, map, set, split } from 'lodash'; | ||
import Select, { Option } from 'react-select'; | ||
|
||
import { IAccountDetails, StageConfigField } from '@spinnaker/core'; | ||
|
||
import { ManifestKindSearchService } from 'kubernetes/v2/manifest/ManifestKindSearch'; | ||
|
||
export interface ITrafficManagementConfig { | ||
enabled: boolean; | ||
options: ITrafficManagementOptions; | ||
} | ||
|
||
export interface ITrafficManagementOptions { | ||
namespace: string; | ||
services: string[]; | ||
enableTraffic: boolean; | ||
strategy: string; | ||
} | ||
|
||
export const defaultTrafficManagementConfig: ITrafficManagementConfig = { | ||
enabled: false, | ||
options: { | ||
namespace: null, | ||
services: [], | ||
enableTraffic: false, | ||
strategy: null, | ||
}, | ||
}; | ||
|
||
export interface IManifestDeploymentOptionsProps { | ||
accounts: IAccountDetails[]; | ||
config: ITrafficManagementConfig; | ||
onConfigChange: (config: ITrafficManagementConfig) => void; | ||
selectedAccount: string; | ||
} | ||
|
||
export interface IManifestDeploymentOptionsState { | ||
services: string[]; | ||
} | ||
|
||
export class ManifestDeploymentOptions extends React.Component< | ||
IManifestDeploymentOptionsProps, | ||
IManifestDeploymentOptionsState | ||
> { | ||
public state: IManifestDeploymentOptionsState = { services: [] }; | ||
|
||
private onConfigChange = (key: string, value: any): void => { | ||
const updatedConfig = cloneDeep(this.props.config); | ||
set(updatedConfig, key, value); | ||
this.props.onConfigChange(updatedConfig); | ||
}; | ||
|
||
private fetchServices = (): void => { | ||
const namespace = this.props.config.options.namespace; | ||
const account = this.props.selectedAccount; | ||
if (!namespace || !account) { | ||
this.setState({ | ||
services: [], | ||
}); | ||
} | ||
ManifestKindSearchService.search('service', namespace, account).then(services => { | ||
this.setState({ services: map(services, 'name') }); | ||
}); | ||
}; | ||
|
||
private getNamespaceOptions = (): Array<Option<string>> => { | ||
const { accounts, selectedAccount } = this.props; | ||
const selectedAccountDetails = find(accounts, a => a.name === selectedAccount); | ||
const namespaces = get(selectedAccountDetails, 'namespaces', []); | ||
return map(namespaces, n => ({ label: n, value: n })); | ||
}; | ||
|
||
public componentDidMount() { | ||
this.fetchServices(); | ||
} | ||
|
||
public componentDidUpdate(prevProps: IManifestDeploymentOptionsProps) { | ||
if (prevProps.selectedAccount !== this.props.selectedAccount) { | ||
this.onConfigChange('options.namespace', null); | ||
} | ||
|
||
if (prevProps.config.options.namespace !== this.props.config.options.namespace) { | ||
this.onConfigChange('options.services', null); | ||
this.fetchServices(); | ||
} | ||
} | ||
|
||
public render() { | ||
const { config } = this.props; | ||
return ( | ||
<> | ||
<h4>Rollout Strategy Options</h4> | ||
<hr /> | ||
<StageConfigField helpKey="kubernetes.manifest.rolloutStrategyOptions" fieldColumns={8} label="Enable"> | ||
<div className="checkbox"> | ||
<label> | ||
<input | ||
checked={config.enabled} | ||
onChange={e => this.onConfigChange('enabled', e.target.checked)} | ||
type="checkbox" | ||
/> | ||
Spinnaker manages traffic based on your selected strategy | ||
</label> | ||
</div> | ||
</StageConfigField> | ||
{config.enabled && ( | ||
<> | ||
<StageConfigField fieldColumns={8} label="Service(s) Namespace"> | ||
<Select | ||
clearable={false} | ||
onChange={(option: Option<string>) => this.onConfigChange('options.namespace', option.value)} | ||
options={this.getNamespaceOptions()} | ||
value={config.options.namespace} | ||
/> | ||
</StageConfigField> | ||
<StageConfigField fieldColumns={8} label="Service(s)"> | ||
<Select | ||
clearable={false} | ||
multi={true} | ||
onChange={options => this.onConfigChange('options.services', map(options, 'value'))} | ||
options={map(this.state.services, s => ({ label: split(s, ' ')[1], value: s }))} | ||
value={config.options.services} | ||
/> | ||
</StageConfigField> | ||
<StageConfigField fieldColumns={8} label="Traffic"> | ||
<div className="checkbox"> | ||
<label> | ||
<input | ||
checked={config.options.enableTraffic} | ||
onChange={e => this.onConfigChange('options.enableTraffic', e.target.checked)} | ||
type="checkbox" | ||
/> | ||
Send client requests to new pods | ||
</label> | ||
</div> | ||
</StageConfigField> | ||
</> | ||
)} | ||
</> | ||
); | ||
} | ||
} | ||
|
||
export const MANIFEST_DEPLOYMENT_OPTIONS = 'spinnaker.kubernetes.v2.pipelines.deployManifest.manifestDeploymentOptions'; | ||
module(MANIFEST_DEPLOYMENT_OPTIONS, []).component( | ||
'manifestDeploymentOptions', | ||
react2angular(ManifestDeploymentOptions, ['accounts', 'config', 'onConfigChange', 'selectedAccount']), | ||
); |
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
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