Skip to content
This repository has been archived by the owner on Jun 6, 2024. It is now read-only.

create a user secret for every job to save user extension #5310

Merged
merged 6 commits into from
Feb 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/database-controller/sdk/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,12 @@ class DatabaseModel {
type: Sequelize.DATE,
allowNull: false,
},
// `dockerSecretDef`, `configSecretDef`, `tokenSecretDef` and `priorityClassDef` is the definition of job add-ons.
// `dockerSecretDef`, `configSecretDef`, `userExtensionSecretDef`, `tokenSecretDef` and `priorityClassDef` is the definition of job add-ons.
// They are generated by rest-server and recorded into database by write-merger.
// These add-ons are created by poller or the short-cut in write-merger.
dockerSecretDef: Sequelize.TEXT,
configSecretDef: Sequelize.TEXT,
userExtensionSecretDef: Sequelize.TEXT,
tokenSecretDef: Sequelize.TEXT,
priorityClassDef: Sequelize.TEXT,
retries: Sequelize.INTEGER,
Expand Down
30 changes: 29 additions & 1 deletion src/database-controller/src/common/framework.js
Original file line number Diff line number Diff line change
Expand Up @@ -350,10 +350,11 @@ class Snapshot {
}

// Class Add-ons handles creation/patching/deletion of job add-ons.
// Currently there are 4 types of add-ons: configSecret, priorityClass, dockerSecret, and tokenSecret.
// Currently there are 5 types of add-ons: configSecret, userExtensionSecret, priorityClass, dockerSecret, and tokenSecret.
class AddOns {
constructor(
configSecretDef = null,
userExtensionSecretDef = null,
priorityClassDef = null,
dockerSecretDef = null,
tokenSecretDef = null,
Expand All @@ -363,6 +364,11 @@ class AddOns {
} else {
this._configSecretDef = configSecretDef;
}
if (userExtensionSecretDef !== null && !(userExtensionSecretDef instanceof Object)) {
this._userExtensionSecretDef = JSON.parse(userExtensionSecretDef);
} else {
this._userExtensionSecretDef = userExtensionSecretDef;
}
if (priorityClassDef !== null && !(priorityClassDef instanceof Object)) {
this._priorityClassDef = JSON.parse(priorityClassDef);
} else {
Expand Down Expand Up @@ -394,6 +400,19 @@ class AddOns {
}
}
}
if (this._userExtensionSecretDef) {
try {
await k8s.createSecret(this._userExtensionSecretDef);
} catch (err) {
if (err.response && err.response.statusCode === 409) {
logger.warn(
`Secret ${this._userExtensionSecretDef.metadata.name} already exists.`,
);
} else {
throw err;
}
}
}
if (this._priorityClassDef) {
try {
await k8s.createPriorityClass(this._priorityClassDef);
Expand Down Expand Up @@ -441,6 +460,10 @@ class AddOns {
k8s
.patchSecretOwnerToFramework(this._configSecretDef, frameworkResponse)
.catch(logError);
this._userExtensionSecretDef &&
k8s
.patchSecretOwnerToFramework(this._userExtensionSecretDef, frameworkResponse)
.catch(logError);
this._dockerSecretDef &&
k8s
.patchSecretOwnerToFramework(this._dockerSecretDef, frameworkResponse)
Expand All @@ -455,6 +478,8 @@ class AddOns {
// do not await for delete
this._configSecretDef &&
k8s.deleteSecret(this._configSecretDef.metadata.name).catch(logError);
this._userExtensionSecretDef &&
k8s.deleteSecret(this._userExtensionSecretDef.metadata.name).catch(logError);
this._priorityClassDef &&
k8s
.deletePriorityClass(this._priorityClassDef.metadata.name)
Expand All @@ -470,6 +495,9 @@ class AddOns {
if (this._configSecretDef) {
update.configSecretDef = JSON.stringify(this._configSecretDef);
}
if (this._userExtensionSecretDef) {
update.userExtensionSecretDef = JSON.stringify(this._userExtensionSecretDef);
}
if (this._priorityClassDef) {
update.priorityClassDef = JSON.stringify(this._priorityClassDef);
}
Expand Down
2 changes: 2 additions & 0 deletions src/database-controller/src/poller/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ async function poll() {
attributes: [
'name',
'configSecretDef',
'userExtensionSecretDef',
'priorityClassDef',
'dockerSecretDef',
'tokenSecretDef',
Expand All @@ -165,6 +166,7 @@ async function poll() {
const snapshot = new Snapshot(framework.snapshot);
const addOns = new AddOns(
framework.configSecretDef,
framework.userExtensionSecretDef,
framework.priorityClassDef,
framework.dockerSecretDef,
framework.tokenSecretDef,
Expand Down
9 changes: 8 additions & 1 deletion src/database-controller/src/write-merger/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ async function postWatchEvents(req, res, next) {
internalUpdate.apiServerDeleted = true;
// If the job is completed and deleted, we could reset its sensitive fields.
internalUpdate.configSecretDef = '';
internalUpdate.userExtensionSecretDef = '';
internalUpdate.dockerSecretDef = '';
internalUpdate.tokenSecretDef = '';
} else {
Expand Down Expand Up @@ -190,6 +191,7 @@ async function patchFrameworkRequest(req, res, next) {
'snapshot',
'submissionTime',
'configSecretDef',
'userExtensionSecretDef',
'priorityClassDef',
'dockerSecretDef',
'tokenSecretDef',
Expand All @@ -206,6 +208,7 @@ async function patchFrameworkRequest(req, res, next) {
snapshot.applyRequestPatch(patchData);
const addOns = new AddOns(
oldFramework.configSecretDef,
oldFramework.userExtensionSecretDef,
oldFramework.priorityClassDef,
oldFramework.dockerSecretDef,
oldFramework.tokenSecretDef,
Expand All @@ -222,7 +225,7 @@ async function patchFrameworkRequest(req, res, next) {
async function putFrameworkRequest(req, res, next) {
// The handler to handle PUT /frameworkRequest.
// PUT means provide a full spec of framework request, and the corresponding request will be created or updated.
// Along with the framework request, user must provide other job add-ons, e.g. configSecretDef, priorityClassDef, dockerSecretDef, tokenSecretDef.
// Along with the framework request, user must provide other job add-ons, e.g. configSecretDef, userExtensionSecretDef, priorityClassDef, dockerSecretDef, tokenSecretDef.
// If the framework doesn't exist in database, the record will be created.
// If the framework already exists, the record will be updated, and all job add-ons will be ignored. (Job add-ons can't be changed).
// If the framework request JSON is changed(or created), we will mark it as requestSynced=false.
Expand All @@ -232,6 +235,7 @@ async function putFrameworkRequest(req, res, next) {
frameworkRequest,
submissionTime,
configSecretDef,
userExtensionSecretDef,
priorityClassDef,
dockerSecretDef,
tokenSecretDef,
Expand All @@ -254,6 +258,7 @@ async function putFrameworkRequest(req, res, next) {
'snapshot',
'submissionTime',
'configSecretDef',
'userExtensionSecretDef',
'priorityClassDef',
'dockerSecretDef',
],
Expand All @@ -264,6 +269,7 @@ async function putFrameworkRequest(req, res, next) {
// if the old framework doesn't exist, create add-ons.
const addOns = new AddOns(
configSecretDef,
userExtensionSecretDef,
priorityClassDef,
dockerSecretDef,
tokenSecretDef,
Expand All @@ -275,6 +281,7 @@ async function putFrameworkRequest(req, res, next) {
const oldSnapshot = new Snapshot(oldFramework.snapshot);
const addOns = new AddOns(
oldFramework.configSecretDef,
oldFramework.userExtensionSecretDef,
oldFramework.priorityClassDef,
oldFramework.dockerSecretDef,
oldFramework.tokenSecretDef,
Expand Down
45 changes: 45 additions & 0 deletions src/rest-server/src/models/v2/job/k8s.js
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,7 @@ const generateTaskRole = (
const generateFrameworkDescription = (
frameworkName,
virtualCluster,
userExtension,
config,
rawConfig,
) => {
Expand Down Expand Up @@ -770,6 +771,23 @@ const generateFrameworkDescription = (
mountPath: '/usr/local/pai/secrets',
});
}
// mount user secrets to initContainers & job container if exist
if (userExtension) {
taskRoleDescription.task.pod.spec.volumes.push({
name: 'user-secrets',
secret: {
secretName: `${encodeName(frameworkName)}-usercred`,
},
});
taskRoleDescription.task.pod.spec.initContainers[0].volumeMounts.push({
name: 'user-secrets',
mountPath: '/usr/local/pai/secrets',
});
taskRoleDescription.task.pod.spec.containers[0].volumeMounts.push({
name: 'user-secrets',
mountPath: '/usr/local/pai/secrets',
});
}
// mount token-secrets to initContainers & job container
taskRoleDescription.task.pod.spec.volumes.push({
name: 'token-secrets',
Expand Down Expand Up @@ -850,6 +868,24 @@ const getConfigSecretDef = (frameworkName, secrets) => {
};
};

const getuserExtensionSecretDef = (frameworkName, secrets) => {
const data = {
'userExtensionSecrets.yaml': Buffer.from(yaml.safeDump(secrets)).toString(
'base64',
),
};
return {
apiVersion: 'v1',
kind: 'Secret',
metadata: {
name: `${encodeName(frameworkName)}-usercred`,
namespace: 'default',
},
data: data,
type: 'Opaque',
};
};

const getTokenSecretDef = (frameworkName, token) => {
const data = {
token: Buffer.from(token).toString('base64'),
Expand Down Expand Up @@ -1083,9 +1119,17 @@ const put = async (frameworkName, config, rawConfig) => {
}
}

// generate the user-extension-secret definition
const user = await userModel.getUser(userName);
const userExtension = user.extension;
const userExtensionSecretDef = userExtension
? getuserExtensionSecretDef(frameworkName, userExtension)
: null;

const frameworkDescription = generateFrameworkDescription(
frameworkName,
virtualCluster,
userExtension,
config,
rawConfig,
);
Expand Down Expand Up @@ -1149,6 +1193,7 @@ const put = async (frameworkName, config, rawConfig) => {
frameworkRequest: frameworkDescription,
submissionTime: submissionTime,
configSecretDef: configSecretDef,
userExtensionSecretDef: userExtensionSecretDef,
priorityClassDef: priorityClassDef,
dockerSecretDef: dockerSecretDef,
tokenSecretDef: tokenSecretDef,
Expand Down