Skip to content

Commit

Permalink
[UI] textbox to select KSA when creating runs/jobs (#3651)
Browse files Browse the repository at this point in the history
* Add service account field to run and job api objects

* Update description

* Fix field casing

* Use service account from api object

* Fix bug and add unit test

* [UI] Allow choosing Kubernetes service account

* fix unit tests

* fix format

* Also clone service account

* service account UI features

* Add unit test for cloning service account

* Fix frontend integration tests
  • Loading branch information
Bobgy committed May 7, 2020
1 parent 9c16e12 commit e5bd2df
Show file tree
Hide file tree
Showing 6 changed files with 353 additions and 1 deletion.
8 changes: 7 additions & 1 deletion frontend/src/apis/job/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,17 @@ export interface ApiJob {
*/
pipeline_spec?: ApiPipelineSpec;
/**
* Optional input field. Specify which resource this run belongs to.
* Optional input field. Specify which resource this job belongs to.
* @type {Array<ApiResourceReference>}
* @memberof ApiJob
*/
resource_references?: Array<ApiResourceReference>;
/**
* Optional input field. Specify which Kubernetes service account this job uses.
* @type {string}
* @memberof ApiJob
*/
service_account?: string;
/**
*
* @type {string}
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/apis/run/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,12 @@ export interface ApiRun {
* @memberof ApiRun
*/
resource_references?: Array<ApiResourceReference>;
/**
* Optional input field. Specify which Kubernetes service account this run uses.
* @type {string}
* @memberof ApiRun
*/
service_account?: string;
/**
* Output. The time that the run created.
* @type {Date}
Expand Down
28 changes: 28 additions & 0 deletions frontend/src/pages/NewRun.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ describe('NewRun', () => {
run: {
id: 'some-mock-run-id',
name: 'some mock run name',
service_account: 'pipeline-runner',
pipeline_spec: {
pipeline_id: 'original-run-pipeline-id',
workflow_manifest: '{}',
Expand Down Expand Up @@ -785,6 +786,25 @@ describe('NewRun', () => {
expect(tree.state('runName')).toBe('Clone (2) of some run');
});

it('uses service account in the original run', async () => {
const defaultRunDetail = newMockRunDetail();
const runDetail = {
...defaultRunDetail,
run: {
...defaultRunDetail.run,
service_account: 'sa1',
},
};
const props = generateProps();
props.location.search = `?${QUERY_PARAMS.cloneFromRun}=${runDetail.run!.id}`;
getRunSpy.mockImplementation(() => runDetail);

tree = shallow(<TestNewRun {...props} />);
await TestUtils.flushPromises();

expect(tree.state('serviceAccount')).toBe('sa1');
});

it('uses the query param experiment ID over the one in the original run if an ID is present in both', async () => {
const experiment = newMockExperiment();
const runDetail = newMockRunDetail();
Expand Down Expand Up @@ -1189,6 +1209,9 @@ describe('NewRun', () => {
(tree.instance() as TestNewRun).handleChange('description')({
target: { value: 'test run description' },
});
(tree.instance() as TestNewRun).handleChange('serviceAccount')({
target: { value: 'service-account-name' },
});
await TestUtils.flushPromises();

tree
Expand All @@ -1205,6 +1228,7 @@ describe('NewRun', () => {
pipeline_spec: {
parameters: MOCK_PIPELINE.parameters,
},
service_account: 'service-account-name',
resource_references: [
{
key: {
Expand Down Expand Up @@ -1259,6 +1283,7 @@ describe('NewRun', () => {
pipeline_spec: {
parameters: [{ name: 'testName', value: '{\n "test2": "value2"\n}' }],
},
service_account: '',
resource_references: [
{
key: {
Expand Down Expand Up @@ -1351,6 +1376,7 @@ describe('NewRun', () => {
pipeline_id: undefined,
workflow_manifest: '{"metadata":{"name":"embedded"},"parameters":[]}',
},
service_account: 'pipeline-runner',
resource_references: [],
});
// TODO: verify route change happens
Expand Down Expand Up @@ -1655,6 +1681,7 @@ describe('NewRun', () => {

instance.handleChange('runName')({ target: { value: 'test run name' } });
instance.handleChange('description')({ target: { value: 'test run description' } });
instance.handleChange('serviceAccount')({ target: { value: 'service-account-name' } });
await TestUtils.flushPromises();

tree
Expand All @@ -1675,6 +1702,7 @@ describe('NewRun', () => {
pipeline_spec: {
parameters: MOCK_PIPELINE.parameters,
},
service_account: 'service-account-name',
resource_references: [
{
key: {
Expand Down
29 changes: 29 additions & 0 deletions frontend/src/pages/NewRun.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,15 @@ import { Description } from '../components/Description';
import { NamespaceContext } from '../lib/KubeflowClient';
import { NameWithTooltip } from '../components/CustomTableNameColumn';
import { PredicateOp, ApiFilter } from '../apis/filter';
import { HelpButton } from 'src/atoms/HelpButton';
import { ExternalLink } from 'src/atoms/ExternalLink';

interface NewRunState {
description: string;
errorMessage: string;
experiment?: ApiExperiment;
experimentName: string;
serviceAccount: string;
experimentSelectorOpen: boolean;
isBeingStarted: boolean;
isClone: boolean;
Expand Down Expand Up @@ -116,6 +119,7 @@ export class NewRun extends Page<{ namespace?: string }, NewRunState> {
description: '',
errorMessage: '',
experimentName: '',
serviceAccount: '',
experimentSelectorOpen: false,
isBeingStarted: false,
isClone: false,
Expand Down Expand Up @@ -176,6 +180,7 @@ export class NewRun extends Page<{ namespace?: string }, NewRunState> {
description,
errorMessage,
experimentName,
serviceAccount,
experimentSelectorOpen,
isClone,
isFirstRunInExperiment,
Expand Down Expand Up @@ -499,6 +504,27 @@ export class NewRun extends Page<{ namespace?: string }, NewRunState> {
}}
/>

<div>
This run will use the following Kubernetes service account.{' '}
<HelpButton
helpText={
<div>
Note, the service account needs{' '}
<ExternalLink href='https://github.com/argoproj/argo/blob/v2.3.0/docs/workflow-rbac.md'>
minimum permissions required by argo workflows
</ExternalLink>{' '}
and extra permissions the specific task requires.
</div>
}
/>
</div>
<Input
value={serviceAccount}
onChange={this.handleChange('serviceAccount')}
label='Service Account (Optional)'
variant='outlined'
/>

{/* One-off/Recurring Run Type */}
<div className={commonCss.header}>Run Type</div>
{isClone && <span>{isRecurringRun ? 'Recurring' : 'One-off'}</span>}
Expand Down Expand Up @@ -910,6 +936,7 @@ export class NewRun extends Page<{ namespace?: string }, NewRunState> {
let usePipelineFromRunLabel = '';
let name = '';
let pipelineVersionName = '';
const serviceAccount = originalRun.service_account || '';

// Case 1: a legacy run refers to a pipeline without specifying version.
const referencePipelineId = RunUtils.getPipelineId(originalRun);
Expand Down Expand Up @@ -984,6 +1011,7 @@ export class NewRun extends Page<{ namespace?: string }, NewRunState> {
usePipelineFromRunLabel,
useWorkflowFromRun,
workflowFromRun,
serviceAccount,
});

this._validate();
Expand Down Expand Up @@ -1039,6 +1067,7 @@ export class NewRun extends Page<{ namespace?: string }, NewRunState> {
: undefined,
},
resource_references: references,
service_account: this.state.serviceAccount,
};
if (this.state.isRecurringRun) {
newRun = Object.assign(newRun, {
Expand Down
Loading

0 comments on commit e5bd2df

Please sign in to comment.