Skip to content

Commit 6c49415

Browse files
authored
Webportal support jobType in job protocol and dedicated parameters in inference job (#113)
* add job type support; add inference job support * update * deploy webportal-dind when webportal changed * update * update * update * update * update * update
1 parent 56f2a32 commit 6c49415

File tree

7 files changed

+117
-9
lines changed

7 files changed

+117
-9
lines changed

.github/workflows/build-deploy-changes.yaml

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -171,13 +171,27 @@ jobs:
171171
--overwrite-existing
172172
kubelogin convert-kubeconfig -l azurecli
173173
kubectl config use-context ${{ secrets.KUBERNETES_CLUSTER }}
174+
# Replace "webportal" with "webportal-dind" if "webportal" is changed
175+
services_to_deploy="${{ steps.changes.outputs.folders }}"
176+
if echo " $services_to_deploy " | grep -q " webportal "; then
177+
tmp=""
178+
for s in $services_to_deploy; do
179+
[ "$s" = "webportal" ] && continue
180+
[ "$s" = "webportal-dind" ] && continue
181+
tmp="$tmp $s"
182+
done
183+
services_to_deploy="$tmp webportal-dind"
184+
services_to_deploy=$(echo "$services_to_deploy" | xargs)
185+
fi
186+
echo "Final services to deploy: $services_to_deploy"
187+
174188
echo "${{ secrets.PAI_CLUSTER_NAME }}" > cluster_id
175-
echo "Stopping changed pai services \"${{ steps.changes.outputs.folders }}\" on ${{ secrets.PAI_CLUSTER_NAME }} ..."
176-
$GITHUB_WORKSPACE/paictl.py service stop -n ${{ steps.changes.outputs.folders }} < cluster_id
189+
echo "Stopping changed pai services $services_to_deploy on ${{ secrets.PAI_CLUSTER_NAME }} ..."
190+
$GITHUB_WORKSPACE/paictl.py service stop -n $services_to_deploy < cluster_id
177191
echo "Pushing config to cluster \"${{ secrets.PAI_CLUSTER_NAME }}\" ..."
178-
$GITHUB_WORKSPACE/paictl.py config push -m service -p $GITHUB_WORKSPACE/config/cluster-configuration < cluster_id
179-
echo "Starting to update \"${{ steps.changes.outputs.folders }}\" on ${{ secrets.PAI_CLUSTER_NAME }} ..."
180-
$GITHUB_WORKSPACE/paictl.py service start -n ${{ steps.changes.outputs.folders }} < cluster_id
192+
$GITHUB_WORKSPACE/paictl.py config push -m service -p $GITHUB_WORKSPACE/config/cluster-configuration < cluster_id
193+
echo "Starting to update $services_to_deploy on ${{ secrets.PAI_CLUSTER_NAME }} ..."
194+
$GITHUB_WORKSPACE/paictl.py service start -n $services_to_deploy < cluster_id
181195
kubectl get pod
182196
kubectl get service
183197

src/webportal/src/app/job-submission/components/job-information.jsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { FormTextField } from './form-text-field';
2929
import { FormPage } from './form-page';
3030
import { FormSpinButton } from './form-spin-button';
3131
import { VirtualCluster } from './virtual-cluster';
32+
import { JobType } from './job-type';
3233
import Card from '../../components/card';
3334
import { JobBasicInfo } from '../models/job-basic-info';
3435
import { PROTOCOL_TOOLTIPS } from '../utils/constants';
@@ -37,7 +38,7 @@ const JOB_NAME_REGX = /^[A-Za-z0-9\-._~]+$/;
3738

3839
export const JobInformation = React.memo(
3940
({ jobInformation, onChange, advanceFlag }) => {
40-
const { name, virtualCluster, jobRetryCount } = jobInformation;
41+
const { name, virtualCluster, jobRetryCount, jobType } = jobInformation;
4142

4243
const onChangeProp = useCallback(
4344
(type, value) => {
@@ -59,6 +60,11 @@ export const JobInformation = React.memo(
5960
[onChangeProp],
6061
);
6162

63+
const onJobTypeChange = useCallback(
64+
jobType => onChangeProp('jobType', jobType),
65+
[onChangeProp],
66+
);
67+
6268
const onRetryCountChange = useCallback(
6369
val => onChangeProp('jobRetryCount', val),
6470
[onChangeProp],
@@ -89,6 +95,10 @@ export const JobInformation = React.memo(
8995
onChange={onRetryCountChange}
9096
/>
9197
)}
98+
<JobType
99+
onChange={onJobTypeChange}
100+
jobType={jobType}
101+
/>
92102
</FormPage>
93103
</Card>
94104
);
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
import React, { useMemo, useCallback } from 'react';
5+
import { BasicSection } from './basic-section';
6+
import { Dropdown } from 'office-ui-fabric-react';
7+
import { FormShortSection } from './form-page';
8+
import PropTypes from 'prop-types';
9+
10+
export const JobType = React.memo(props => {
11+
const { onChange, jobType } = props;
12+
const jobTypes = ['others', 'training', 'inference'];
13+
14+
const options = useMemo(
15+
() =>
16+
jobTypes.map((jobType, index) => {
17+
return {
18+
key: `jobType_${index}`,
19+
text: jobType,
20+
};
21+
}),
22+
);
23+
24+
const _onChange = useCallback(
25+
(_, item) => {
26+
if (onChange !== undefined) {
27+
onChange(item.text);
28+
}
29+
},
30+
[onChange],
31+
);
32+
33+
const jobTypeIndex = options.findIndex(value => value.text === jobType);
34+
return (
35+
<BasicSection sectionLabel={'Job type'}>
36+
<FormShortSection>
37+
<Dropdown
38+
placeholder='Select an option'
39+
options={options}
40+
onChange={_onChange}
41+
selectedKey={jobTypeIndex === -1 ? "jobType_0" : `jobType_${jobTypeIndex}`}
42+
/>
43+
</FormShortSection>
44+
</BasicSection>
45+
);
46+
});
47+
48+
JobType.propTypes = {
49+
onChange: PropTypes.func,
50+
jobType: PropTypes.string,
51+
};

src/webportal/src/app/job-submission/job-submission-page.jsx

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ export const JobSubmissionPage = ({
250250
break;
251251
}
252252
}
253-
} catch {} // ignore all exceptions here
253+
} catch { } // ignore all exceptions here
254254
if (!isEmpty(defaultStorageConfig)) {
255255
const storagePlugin = {
256256
plugin: STORAGE_PLUGIN,
@@ -386,6 +386,33 @@ export const JobSubmissionPage = ({
386386
.catch(alert);
387387
}, [jobInformation.virtualCluster]);
388388

389+
useEffect(() => {
390+
if (jobInformation.jobType === 'inference') {
391+
const hasApiKey = parameters.find(param => param.key === 'API_KEY');
392+
const hasInternalServerIp = parameters.find(param => param.key === 'INTERNAL_SERVER_IP');
393+
const hasInternalServerPort = parameters.find(param => param.key === 'INTERNAL_SERVER_PORT');
394+
if (!hasApiKey || !hasInternalServerIp || !hasInternalServerPort) {
395+
const newParameters = [...parameters];
396+
if (!hasInternalServerIp) {
397+
// set fixed INTERNAL_SERVER_IP
398+
newParameters.push({ key: 'INTERNAL_SERVER_IP', value: '$PAI_HOST_IP_taskrole_0' });
399+
}
400+
if (!hasInternalServerPort) {
401+
// set fixed INTERNAL_SERVER_PORT
402+
newParameters.push({ key: 'INTERNAL_SERVER_PORT', value: '$PAI_PORT_LIST_taskrole_0_http' });
403+
}
404+
if (!hasApiKey) {
405+
{
406+
// set a random generated api key
407+
const randomKey = crypto.randomUUID();
408+
newParameters.push({ key: 'API_KEY', value: randomKey });
409+
}
410+
}
411+
setParameters(newParameters);
412+
}
413+
}
414+
}, [jobInformation.jobType]);
415+
389416
const onTemplateChange = useCallback((_, item) => {
390417
if (item.key === 'No') {
391418
return;

src/webportal/src/app/job-submission/models/job-basic-info.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,21 @@ import { isEmpty, get } from 'lodash';
2727

2828
export class JobBasicInfo {
2929
constructor(props) {
30-
const { name, jobRetryCount, virtualCluster } = props;
30+
const { name, jobRetryCount, virtualCluster, jobType } = props;
3131
this.name = name || '';
3232
this.jobRetryCount = jobRetryCount || 0;
3333
this.virtualCluster = virtualCluster || '';
34+
this.jobType = jobType || '';
3435
}
3536

3637
static fromProtocol(protocol) {
37-
const { name, jobRetryCount } = protocol;
38+
const { name, jobRetryCount, jobType } = protocol;
3839
const virtualCluster = get(protocol, 'defaults.virtualCluster', 'default');
3940
return new JobBasicInfo({
4041
name: name,
4142
jobRetryCount: jobRetryCount,
4243
virtualCluster: virtualCluster,
44+
jobType: jobType,
4345
});
4446
}
4547

@@ -59,6 +61,7 @@ export class JobBasicInfo {
5961
name: this.name,
6062
type: 'job',
6163
jobRetryCount: this.jobRetryCount,
64+
jobType: this.jobType,
6265
};
6366
}
6467
}

src/webportal/src/app/job-submission/models/job-protocol.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export class JobProtocol {
3737
const {
3838
name,
3939
jobRetryCount,
40+
jobType,
4041
prerequisites,
4142
parameters,
4243
taskRoles,
@@ -53,6 +54,7 @@ export class JobProtocol {
5354
this.contributor = contributor || '';
5455
this.type = 'job';
5556
this.jobRetryCount = jobRetryCount || 0;
57+
this.jobType = jobType || 'others';
5658
this.prerequisites = prerequisites || [];
5759
this.parameters = parameters || {};
5860
this.taskRoles = taskRoles || {};

src/webportal/src/app/job-submission/models/protocol-schema.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ export const jobProtocolSchema = Joi.object().keys({
173173
version: [Joi.string(), Joi.number()],
174174
contributor: Joi.string(),
175175
description: Joi.string(),
176+
jobType: Joi.string().valid(['others', 'training', 'inference']).default('others'),
176177

177178
prerequisites: Joi.array()
178179
.items(prerequisitesSchema)

0 commit comments

Comments
 (0)