Skip to content

Commit

Permalink
[UI] Upload pipeline version from local package (kubeflow#3059)
Browse files Browse the repository at this point in the history
* [UI] Upload pipeline version from local package

* Fix test error

* Improve broken license file
  • Loading branch information
Bobgy authored and Jeffwan committed Dec 9, 2020
1 parent f15bce2 commit 4509317
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 142 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ packaging,https://creativecommons.org/licenses/by-sa/3.0/,CC by-SA 3
pandas,https://github.com/pandas-dev/pandas/blob/master/LICENSE,BSD-3
pandocfilters,https://github.com/jgm/pandocfilters/blob/master/LICENSE,BSD-3
parso,https://github.com/davidhalter/parso/blob/master/LICENSE.txt,MIT
pbr,https://opendev.org/openstack/pbr/src/branch/master/LICENSE,Apache 2.0
pbr,https://opendev.org/openstack/pbr/raw/branch/master/LICENSE,Apache 2.0
pexpect,https://github.com/pexpect/pexpect/blob/master/LICENSE,ISC
pickleshare,https://github.com/pickleshare/pickleshare/blob/master/LICENSE,MIT
prometheus-client,https://github.com/prometheus/client_python/blob/master/LICENSE,Apache 2.0
Expand Down
21 changes: 20 additions & 1 deletion frontend/src/lib/Apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import * as portableFetch from 'portable-fetch';
import { HTMLViewerConfig } from 'src/components/viewers/HTMLViewer';
import { ExperimentServiceApi, FetchAPI } from '../apis/experiment';
import { JobServiceApi } from '../apis/job';
import { ApiPipeline, PipelineServiceApi } from '../apis/pipeline';
import { ApiPipeline, PipelineServiceApi, ApiPipelineVersion } from '../apis/pipeline';
import { RunServiceApi } from '../apis/run';
import { ApiVisualization, VisualizationServiceApi } from '../apis/visualization';
import { PlotType } from '../components/viewers/Viewer';
Expand Down Expand Up @@ -241,6 +241,25 @@ export class Apis {
);
}

public static async uploadPipelineVersion(
versionName: string,
pipelineId: string,
versionData: File,
): Promise<ApiPipelineVersion> {
const fd = new FormData();
fd.append('uploadfile', versionData, versionData.name);
return await this._fetchAndParse<ApiPipelineVersion>(
'/pipelines/upload_version',
v1beta1Prefix,
`name=${encodeURIComponent(versionName)}&pipelineid=${encodeURIComponent(pipelineId)}`,
{
body: fd,
cache: 'no-cache',
method: 'POST',
},
);
}

/*
* Retrieves the name of the Kubernetes cluster if it is running in GKE, otherwise returns an error.
*/
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/Buttons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ export default class Buttons {
outlined: true,
style: { minWidth: 160 },
title: label,
tooltip: 'Upload pipeline or pipeline version',
tooltip: 'Upload pipeline version',
};
return this;
}
Expand Down
266 changes: 129 additions & 137 deletions frontend/src/pages/NewPipelineVersion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,9 @@ class NewPipelineVersion extends Page<{}, NewPipelineVersionState> {
/>
</div>

{/* Form for uploading new pipeline */}
{/* Pipeline name and help text for uploading new pipeline */}
{newPipeline === true && (
<React.Fragment>
<>
<div className={css.explanation}>Upload pipeline with the specified package.</div>
<Input
id='newPipelineName'
Expand All @@ -237,115 +237,12 @@ class NewPipelineVersion extends Page<{}, NewPipelineVersionState> {
/>

{/* Choose a local file for package or specify a url for package */}

{/* Different package explanation based on import method*/}
{this.state.importMethod === ImportMethod.LOCAL && (
<React.Fragment>
<div className={padding(10, 'b')}>
Choose a pipeline package file from your computer, and give the pipeline a
unique name.
<br />
You can also drag and drop the file here.
</div>
<DocumentationCompilePipeline />
</React.Fragment>
)}
{this.state.importMethod === ImportMethod.URL && (
<React.Fragment>
<div className={padding(10, 'b')}>URL must be publicly accessible.</div>
<DocumentationCompilePipeline />
</React.Fragment>
)}

{/* Different package input field based on import method*/}
<div className={classes(commonCss.flex, padding(10, 'b'))}>
<FormControlLabel
id='localPackageBtn'
label='Upload a file'
checked={importMethod === ImportMethod.LOCAL}
control={<Radio color='primary' />}
onChange={() => this.setState({ importMethod: ImportMethod.LOCAL })}
/>
<Dropzone
id='dropZone'
disableClick={true}
onDrop={this._onDrop.bind(this)}
onDragEnter={this._onDropzoneDragEnter.bind(this)}
onDragLeave={this._onDropzoneDragLeave.bind(this)}
style={{ position: 'relative' }}
ref={this._dropzoneRef}
inputProps={{ tabIndex: -1 }}
disabled={importMethod === ImportMethod.URL}
>
{dropzoneActive && <div className={css.dropOverlay}>Drop files..</div>}
<Input
onChange={this.handleChange('fileName')}
value={fileName}
required={true}
label='File'
variant='outlined'
disabled={importMethod === ImportMethod.URL}
// Find a better to align this input box with others
InputProps={{
endAdornment: (
<InputAdornment position='end'>
<Button
color='secondary'
onClick={() => this._dropzoneRef.current!.open()}
style={{ padding: '3px 5px', margin: 0, whiteSpace: 'nowrap' }}
disabled={importMethod === ImportMethod.URL}
>
Choose file
</Button>
</InputAdornment>
),
readOnly: true,
style: {
maxWidth: 2000,
width: 455,
},
}}
/>
</Dropzone>
</div>
<div className={classes(commonCss.flex, padding(10, 'b'))}>
<FormControlLabel
id='remotePackageBtn'
label='Import by url'
checked={importMethod === ImportMethod.URL}
control={<Radio color='primary' />}
onChange={() => this.setState({ importMethod: ImportMethod.URL })}
/>
<Input
id='pipelinePackageUrl'
label='Package Url'
multiline={true}
onChange={this.handleChange('packageUrl')}
value={packageUrl}
variant='outlined'
disabled={importMethod === ImportMethod.LOCAL}
// Find a better to align this input box with others
style={{
maxWidth: 2000,
width: 465,
}}
/>
</div>
{/* Fill pipeline version code source url */}
<Input
id='pipelineVersionCodeSource'
label='Code Source (optional)'
multiline={true}
onChange={this.handleChange('codeSourceUrl')}
value={codeSourceUrl}
variant='outlined'
/>
</React.Fragment>
</>
)}

{/* Form for uploading new pipeline version */}
{/* Pipeline selector and help text for uploading new pipeline version */}
{newPipeline === false && (
<React.Fragment>
<>
<div className={css.explanation}>
Upload pipeline version with the specified package.
</div>
Expand Down Expand Up @@ -435,28 +332,112 @@ class NewPipelineVersion extends Page<{}, NewPipelineVersionState> {
autoFocus={true}
variant='outlined'
/>
</>
)}

{/* Fill pipeline package url */}
<Input
id='pipelineVersionPackageUrl'
label='Package Url'
multiline={true}
onChange={this.handleChange('packageUrl')}
value={packageUrl}
variant='outlined'
/>
{/* Different package explanation based on import method*/}
{this.state.importMethod === ImportMethod.LOCAL && (
<>
<div className={padding(10, 'b')}>
Choose a pipeline package file from your computer, and give the pipeline a unique
name.
<br />
You can also drag and drop the file here.
</div>
<DocumentationCompilePipeline />
</>
)}
{this.state.importMethod === ImportMethod.URL && (
<>
<div className={padding(10, 'b')}>URL must be publicly accessible.</div>
<DocumentationCompilePipeline />
</>
)}

{/* Fill pipeline version code source url */}
{/* Different package input field based on import method*/}
<div className={classes(commonCss.flex, padding(10, 'b'))}>
<FormControlLabel
id='localPackageBtn'
label='Upload a file'
checked={importMethod === ImportMethod.LOCAL}
control={<Radio color='primary' />}
onChange={() => this.setState({ importMethod: ImportMethod.LOCAL })}
/>
<Dropzone
id='dropZone'
disableClick={true}
onDrop={this._onDrop.bind(this)}
onDragEnter={this._onDropzoneDragEnter.bind(this)}
onDragLeave={this._onDropzoneDragLeave.bind(this)}
style={{ position: 'relative' }}
ref={this._dropzoneRef}
inputProps={{ tabIndex: -1 }}
disabled={importMethod === ImportMethod.URL}
>
{dropzoneActive && <div className={css.dropOverlay}>Drop files..</div>}
<Input
id='pipelineVersionCodeSource'
label='Code Source (optional)'
multiline={true}
onChange={this.handleChange('codeSourceUrl')}
value={codeSourceUrl}
onChange={this.handleChange('fileName')}
value={fileName}
required={true}
label='File'
variant='outlined'
disabled={importMethod === ImportMethod.URL}
// Find a better to align this input box with others
InputProps={{
endAdornment: (
<InputAdornment position='end'>
<Button
color='secondary'
onClick={() => this._dropzoneRef.current!.open()}
style={{ padding: '3px 5px', margin: 0, whiteSpace: 'nowrap' }}
disabled={importMethod === ImportMethod.URL}
>
Choose file
</Button>
</InputAdornment>
),
readOnly: true,
style: {
maxWidth: 2000,
width: 455,
},
}}
/>
</React.Fragment>
)}
</Dropzone>
</div>
<div className={classes(commonCss.flex, padding(10, 'b'))}>
<FormControlLabel
id='remotePackageBtn'
label='Import by url'
checked={importMethod === ImportMethod.URL}
control={<Radio color='primary' />}
onChange={() => this.setState({ importMethod: ImportMethod.URL })}
/>
<Input
id='pipelinePackageUrl'
label='Package Url'
multiline={true}
onChange={this.handleChange('packageUrl')}
value={packageUrl}
variant='outlined'
disabled={importMethod === ImportMethod.LOCAL}
// Find a better to align this input box with others
style={{
maxWidth: 2000,
width: 465,
}}
/>
</div>

{/* Fill pipeline version code source url */}
<Input
id='pipelineVersionCodeSource'
label='Code Source (optional)'
multiline={true}
onChange={this.handleChange('codeSourceUrl')}
value={codeSourceUrl}
variant='outlined'
/>

{/* Create pipeline or pipeline version */}
<div className={commonCss.flex}>
Expand Down Expand Up @@ -604,15 +585,26 @@ class NewPipelineVersion extends Page<{}, NewPipelineVersionState> {
}
};

const newPipelineVersion: ApiPipelineVersion = {
code_source_url: this.state.codeSourceUrl,
name: this.state.pipelineVersionName,
package_url: { pipeline_url: this.state.packageUrl },
resource_references: [
{ key: { id: await getPipelineId(), type: ApiResourceType.PIPELINE }, relationship: 1 },
],
};
return Apis.pipelineServiceApi.createPipelineVersion(newPipelineVersion);
if (this.state.importMethod === ImportMethod.LOCAL) {
if (!this.state.file) {
throw new Error('File should be selected');
}
return Apis.uploadPipelineVersion(
this.state.pipelineVersionName,
await getPipelineId(),
this.state.file,
);
} else {
// this.state.importMethod === ImportMethod.URL
return Apis.pipelineServiceApi.createPipelineVersion({
code_source_url: this.state.codeSourceUrl,
name: this.state.pipelineVersionName,
package_url: { pipeline_url: this.state.packageUrl },
resource_references: [
{ key: { id: await getPipelineId(), type: ApiResourceType.PIPELINE }, relationship: 1 },
],
});
}
}

private _validate(): void {
Expand All @@ -625,7 +617,7 @@ class NewPipelineVersion extends Page<{}, NewPipelineVersionState> {
try {
if (newPipeline) {
if (!packageUrl && !fileName) {
throw new Error('Must specify either package url or file');
throw new Error('Must specify either package url or file in .yaml, .zip, or .tar.gz');
}
} else {
if (!pipeline) {
Expand All @@ -634,8 +626,8 @@ class NewPipelineVersion extends Page<{}, NewPipelineVersionState> {
if (!pipelineVersionName) {
throw new Error('Pipeline version name is required');
}
if (!packageUrl) {
throw new Error('Please specify a pipeline package in .yaml, .zip, or .tar.gz');
if (!packageUrl && !fileName) {
throw new Error('Please specify either package url or file in .yaml, .zip, or .tar.gz');
}
}
this.setState({ validationError: '' });
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/PipelineDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ class PipelineDetails extends Page<{}, PipelineDetailsState> {
return pipelineVersionIdFromParams ? pipelineVersionIdFromParams : '';
},
)
.newPipelineVersion('Upload pipeline version', () =>
.newPipelineVersion('Upload version', () =>
pipelineIdFromParams ? pipelineIdFromParams : '',
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ exports[`NewPipelineVersion creating new pipeline renders the new pipeline page
<div
className="errorMessage"
>
Must specify either package url or file
Must specify either package url or file in .yaml, .zip, or .tar.gz
</div>
</div>
</div>
Expand Down

0 comments on commit 4509317

Please sign in to comment.