Skip to content

Commit

Permalink
add earl jobs export command
Browse files Browse the repository at this point in the history
  • Loading branch information
busma13 committed Sep 3, 2024
1 parent 00ec945 commit 8eb853a
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 4 deletions.
46 changes: 46 additions & 0 deletions packages/teraslice-cli/src/cmds/jobs/export.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { CMD } from '../../interfaces.js';
import Config from '../../helpers/config.js';
import YargsOptions from '../../helpers/yargs-options.js';
import Jobs from '../../helpers/jobs.js';
import reply from '../../helpers/reply.js';

const yargsOptions = new YargsOptions();

export default {
command: 'export <cluster-alias> <job-id...>',
describe: 'Export job or jobs on a cluster to a json file. By default the file is saved as ~/.teraslice/export/<cluster-alias>/<job.name>.json\n',
builder(yargs: any) {
yargs.positional('job-id', yargsOptions.buildPositional('job-id'));
yargs.options('config-dir', yargsOptions.buildOption('config-dir'));
yargs.options('export-dir', yargsOptions.buildOption('export-dir'));
yargs.options('output', yargsOptions.buildOption('output'));
yargs.options('file-name', yargsOptions.buildOption('file-name'));
yargs.check((argv: { jobId: string[]; fileName: string[]; }) => {
if (argv.fileName && argv.jobId.length !== argv.fileName.length) {
throw new Error('The number of job IDs must match the number of file names');
}
return true;
});
// yargs.options('yes', yargsOptions.buildOption('yes'));
// yargs.options('status', yargsOptions.buildOption('jobs-status'));
yargs.strict()
.example('$0 jobs export CLUSTER_ALIAS JOB1', 'exports job config as a tjm compatible JSON file')
.example('$0 jobs export CLUSTER_ALIAS JOB1 JOB2', 'exports job config for two jobs')
.example('$0 jobs export CLUSTER_ALIAS JOB1 JOB2 --file-name name1.json name2.json', 'exports two jobs with custom file names')
.example('$0 jobs export CLUSTER_ALIAS JOB1 --export-dir ./my_jobs --f job_1.json', 'exports ajob to ./my_jobs/job_1.json');
// .example('$0 jobs export CLUSTER_ALIAS all --status failing', 'exports all failing jobs on a cluster')

Check failure on line 31 in packages/teraslice-cli/src/cmds/jobs/export.ts

View workflow job for this annotation

GitHub Actions / lint-and-sync

This line has a length of 117. Maximum allowed is 100

Check failure on line 31 in packages/teraslice-cli/src/cmds/jobs/export.ts

View workflow job for this annotation

GitHub Actions / lint-and-sync

Expected indentation of 8 spaces but found 12
// .example('$0 jobs export CLUSTER_ALIAS all -y', 'exports all jobs on a cluster and bypasses the prompt');

Check failure on line 32 in packages/teraslice-cli/src/cmds/jobs/export.ts

View workflow job for this annotation

GitHub Actions / lint-and-sync

This line has a length of 120. Maximum allowed is 100

Check failure on line 32 in packages/teraslice-cli/src/cmds/jobs/export.ts

View workflow job for this annotation

GitHub Actions / lint-and-sync

Expected indentation of 8 spaces but found 12
return yargs;
},
async handler(argv: any) {
const cliConfig = new Config(argv);
const jobs = new Jobs(cliConfig);

try {
await jobs.initialize();
await jobs.export();
} catch (e) {
reply.fatal(e);
}
}
} as CMD;
2 changes: 2 additions & 0 deletions packages/teraslice-cli/src/cmds/jobs/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { CMD } from '../../interfaces.js';
import awaitCmd from './await.js';
import errors from './errors.js';
import exportJob from './export.js';
import list from './list.js';
import pause from './pause.js';
import recover from './recover.js';
Expand All @@ -17,6 +18,7 @@ import workers from './workers.js';
const commandList = [
awaitCmd,
errors,
exportJob,
list,
pause,
recover,
Expand Down
13 changes: 11 additions & 2 deletions packages/teraslice-cli/src/helpers/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ export default class Config {
return `${this.configDir}/aliases.yaml`;
}

get defaultExportDir(): string {
return `${this.configDir}/export/${this.args.clusterAlias}`;
}

get jobStateDir(): string {
return `${this.configDir}/job_state_files`;
}
Expand All @@ -85,7 +89,8 @@ export default class Config {
get allSubDirs(): string[] {
return [
this.jobStateDir,
this.assetDir
this.assetDir,
this.defaultExportDir,
];
}

Expand All @@ -96,9 +101,13 @@ export default class Config {

this.allSubDirs.forEach((dir) => {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
fs.mkdirSync(dir, { recursive: true });
}
});

if (this.args.exportDir && !fs.existsSync(this.args.exportDir)) {
fs.mkdirSync(this.args.exportDir, { recursive: true });
}
}

private _addJobAction() {
Expand Down
18 changes: 17 additions & 1 deletion packages/teraslice-cli/src/helpers/jobs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Job } from 'teraslice-client-js';
import TerasliceUtil from './teraslice-util.js';
import Display from './display.js';
import reply from './reply.js';
import { getJobConfigFromFile } from './tjm-util.js';
import { getJobConfigFromFile, saveJobConfigToFile } from './tjm-util.js';
import Config from './config.js';
import {
JobMetadata,
Expand Down Expand Up @@ -754,6 +754,22 @@ export default class Jobs {
this.printDiff(diffObject, showUpdateField);
}

async export() {
await pMap(
this.jobs,
(job) => this.exportOne(job.config),
{ concurrency: this.concurrency }
);
}

async exportOne(jobConfig: Teraslice.JobConfig) {
const dirName = this.config.args.exportDir || this.config.defaultExportDir;
const fileNameIndex = this.config.args.jobId.indexOf(jobConfig.job_id);
const fileName = this.config.args.fileName[fileNameIndex] || `${jobConfig.name}.json`;
const fullPath = path.join(dirName, fileName);
await saveJobConfigToFile(jobConfig, fullPath);
}

/**
* @param args action and final property, final indicates if it is part of a series of commands
* @param job job metadata
Expand Down
17 changes: 16 additions & 1 deletion packages/teraslice-cli/src/helpers/tjm-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import {
has,
set,
unset,
get
get,
cloneDeep
} from '@terascope/utils';
import { Teraslice } from '@terascope/types';
import Config from './config.js';
import Jobs from './jobs.js';
import { getPackage } from './utils.js';
Expand Down Expand Up @@ -208,3 +210,16 @@ export function saveConfig(
function hasMetadata(jobConfig: Record<string, any>): boolean {
return has(jobConfig, '__metadata');
}

export async function saveJobConfigToFile(jobConfig: Teraslice.JobConfig, filePath: string) {
const jobConfigCopy = {};
const keysToSkip = ['_created', '_updated', '_deleted', '_deleted_on', '_context', 'job_id'];

for (const key of Object.keys(jobConfig)) {
if (!keysToSkip.includes(key)) {
jobConfigCopy[key] = cloneDeep(jobConfig[key]);
}
}

await fs.writeJSON(filePath, jobConfigCopy);
}
11 changes: 11 additions & 0 deletions packages/teraslice-cli/src/helpers/yargs-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,17 @@ export default class Options {
describe: 'Shows diff on a job in a cluster and a job file',
default: false,
type: 'boolean'
}),
'export-dir': () => ({
describe: 'Directory where exported job file will be saved',
type: 'string',
nargs: 1,
}),
'file-name': () => ({
alias: 'f',
describe: 'Names that files will be exported as. Ensure same order as job IDs',
type: 'string',
array: true
})
};

Expand Down

0 comments on commit 8eb853a

Please sign in to comment.