Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Mar 18, 2024. It is now read-only.
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
47 changes: 47 additions & 0 deletions packages/core/src/scratchorg/pool/OrphanedOrgsDeleteImpl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import SFPLogger, { Logger, LoggerLevel } from '@dxatscale/sfp-logger';
import { Org } from '@salesforce/core';
import { PoolBaseImpl } from './PoolBaseImpl';
import ScratchOrg from '../ScratchOrg';
import ScratchOrgInfoFetcher from './services/fetchers/ScratchOrgInfoFetcher';
import ScratchOrgOperator from '../ScratchOrgOperator';

export default class OrphanedOrgsDeleteImpl extends PoolBaseImpl {
public constructor(hubOrg: Org, private logger:Logger) {
super(hubOrg);
this.hubOrg = hubOrg;
}

protected async onExec(): Promise<ScratchOrg[]> {
const results = (await new ScratchOrgInfoFetcher(this.hubOrg).getOrphanedScratchOrgs()) as any;

let scratchOrgToDelete: ScratchOrg[] = new Array<ScratchOrg>();
if (results.records.length > 0) {
let scrathOrgIds: string[] = [];
for (let element of results.records) {
if (element.Description?.includes(`"requestedBy":"sfpowerscripts"`)) {
let soDetail: ScratchOrg = {};
soDetail.orgId = element.ScratchOrg;
soDetail.username = element.SignupUsername;
soDetail.status = 'recovered';
scratchOrgToDelete.push(soDetail);
scrathOrgIds.push(`'${element.Id}'`);
}
}

if (scrathOrgIds.length > 0) {
let activeScrathOrgs = await new ScratchOrgInfoFetcher(this.hubOrg).getActiveScratchOrgsByInfoId(
scrathOrgIds.join(',')
);

if (activeScrathOrgs.records.length > 0) {
for (let scratchOrg of activeScrathOrgs.records) {
await new ScratchOrgOperator(this.hubOrg).delete(scratchOrg.Id);
SFPLogger.log(`Scratch org with username ${scratchOrg.SignupUsername} is recovered`,LoggerLevel.TRACE,this.logger);
}
}
}
}

return scratchOrgToDelete;
}
}
86 changes: 64 additions & 22 deletions packages/core/src/scratchorg/pool/PoolCreateImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import PoolFetchImpl from './PoolFetchImpl';
import { COLOR_SUCCESS } from '@dxatscale/sfp-logger';
import { COLOR_ERROR } from '@dxatscale/sfp-logger';
import getFormattedTime from '../../utils/GetFormattedTime';
import OrphanedOrgsDeleteImpl from './OrphanedOrgsDeleteImpl';
import path from 'path';

export default class PoolCreateImpl extends PoolBaseImpl {
private limiter;
Expand Down Expand Up @@ -48,7 +50,8 @@ export default class PoolCreateImpl extends PoolBaseImpl {
protected async onExec(): Promise<Result<PoolConfig, PoolError>> {
await this.hubOrg.refreshAuth();

let scriptExecPromises: Array<Promise<ScriptExecutionResult>> = [];
const scriptExecPromises: Array<Promise<ScriptExecutionResult>> = [];


//fetch current status limits
this.limits = await new ScratchOrgLimitsFetcher(this.hubOrg).getScratchOrgLimits();
Expand Down Expand Up @@ -120,8 +123,8 @@ export default class PoolCreateImpl extends PoolBaseImpl {
}

// Assign workers to executed scripts
for (let scratchOrg of this.pool.scratchOrgs) {
let result = this.scriptExecutorWrappedForBottleneck(scratchOrg, this.hubOrg.getUsername());
for (const scratchOrg of this.pool.scratchOrgs) {
const result = this.scriptExecutorWrappedForBottleneck(scratchOrg, this.hubOrg.getUsername());
scriptExecPromises.push(result);
}

Expand All @@ -142,11 +145,13 @@ export default class PoolCreateImpl extends PoolBaseImpl {
});
}
return ok(this.pool);


}

private async computeAllocation(): Promise<number> {
//Compute current pool requirement
let activeCount = await this.scratchOrgInfoFetcher.getCountOfActiveScratchOrgsByTag(this.pool.tag);
const activeCount = await this.scratchOrgInfoFetcher.getCountOfActiveScratchOrgsByTag(this.pool.tag);
return this.allocateScratchOrgsPerTag(this.limits.ActiveScratchOrgs.Remaining, activeCount, this.pool);
}

Expand Down Expand Up @@ -183,23 +188,25 @@ export default class PoolCreateImpl extends PoolBaseImpl {
//Generate Scratch Orgs
SFPLogger.log(COLOR_KEY_MESSAGE('Generate Scratch Orgs..'), LoggerLevel.INFO);

let scratchOrgPromises = new Array<Promise<ScratchOrg>>();
const scratchOrgPromises = new Array<Promise<ScratchOrg>>();

const scratchOrgCreationLimiter = new Bottleneck({
maxConcurrent: pool.batchSize,
});

let startTime = Date.now();
addDescriptionToScratchOrg(pool);

const startTime = Date.now();
for (let i = 1; i <= pool.to_allocate; i++) {
let scratchOrgPromise: Promise<ScratchOrg> = scratchOrgCreationLimiter.schedule(() =>
const scratchOrgPromise: Promise<ScratchOrg> = scratchOrgCreationLimiter.schedule(() =>
scratchOrgOperator.create(`SO` + i, this.pool.configFilePath, this.pool.expiry, this.pool.waitTime)
);
scratchOrgPromises.push(scratchOrgPromise);
}

SFPLogger.log(`Waiting for all scratch org request to complete, Please wait`);
//Wait for all orgs to be created
let scratchOrgCreationResults = await Promise.allSettled(scratchOrgPromises);
const scratchOrgCreationResults = await Promise.allSettled(scratchOrgPromises);
//Only worry about scrath orgs that have suceeded
const isFulfilled = <T>(p: PromiseSettledResult<T>): p is PromiseFulfilledResult<T> => p.status === 'fulfilled';
const isRejected = <T>(p: PromiseSettledResult<T>): p is PromiseRejectedResult => p.status === 'rejected';
Expand All @@ -209,7 +216,7 @@ export default class PoolCreateImpl extends PoolBaseImpl {
for (const reason of rejectedScratchOrgs) {
if (reason.message.includes(`The client has timed out`)) {
//Log how many we were able to create
let elapsedTime = Date.now() - startTime;
const elapsedTime = Date.now() - startTime;
SFPLogger.log(
`A scratch org creation was rejected due to saleforce not responding within the set wait time of ${pool.waitTime} mins \n` +
`Time elasped so far ${COLOR_KEY_MESSAGE(
Expand All @@ -220,7 +227,7 @@ export default class PoolCreateImpl extends PoolBaseImpl {
}

//Log how many we were able to create
let elapsedTime = Date.now() - startTime;
const elapsedTime = Date.now() - startTime;
SFPLogger.log(
`Created ${COLOR_SUCCESS(scratchOrgs.length)} of ${pool.to_allocate} successfully with ${COLOR_ERROR(
rejectedScratchOrgs.length
Expand All @@ -233,7 +240,7 @@ export default class PoolCreateImpl extends PoolBaseImpl {
let index = scratchOrgs.length;
while (index--) {
try {
let orgDetails = await new OrgDetailsFetcher(scratchOrgs[index].username).getOrgDetails();
const orgDetails = await new OrgDetailsFetcher(scratchOrgs[index].username).getOrgDetails();
if (orgDetails.status === 'Deleted') {
throw new Error(
`Throwing away scratch org ${this.pool.scratchOrgs[index].alias} as it has a status of deleted`
Expand All @@ -246,7 +253,7 @@ export default class PoolCreateImpl extends PoolBaseImpl {

scratchOrgs = await this.scratchOrgInfoFetcher.getScratchOrgRecordId(scratchOrgs);

let scratchOrgInprogress = [];
const scratchOrgInprogress = [];

scratchOrgs.forEach((scratchOrg) => {
scratchOrgInprogress.push({
Expand All @@ -264,6 +271,43 @@ export default class PoolCreateImpl extends PoolBaseImpl {
}
return scratchOrgs;
} else throw new Error(`No scratch orgs were sucesfully generated`);

function addDescriptionToScratchOrg(pool: PoolConfig) {

const configClonePath = path.join('.sfpowerscripts','scratchorg-configs',`${ makeFileId(8)}.json`);
fs.mkdirpSync('.sfpowerscripts/scratchorg-configs');
fs.copyFileSync(pool.configFilePath,configClonePath);

const scratchOrgDefn = fs.readJSONSync(configClonePath);
if (!scratchOrgDefn.description)
scratchOrgDefn.description = JSON.stringify({
requestedBy: 'sfpowerscripts',
pool: pool.tag,
requestedAt: new Date().toISOString(),
});
else
scratchOrgDefn.description = scratchOrgDefn.description.concat(
' ',
JSON.stringify({
requestedBy: 'sfpowerscripts',
pool: pool.tag,
requestedAt: new Date().toISOString(),
})
);
fs.writeJSONSync(configClonePath, scratchOrgDefn, { spaces: 4 });
pool.configFilePath = configClonePath;
}

function makeFileId(length): string {
let result = '';
const characters =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
}

private async fetchScratchOrgsFromSnapshotPool(
Expand All @@ -290,9 +334,9 @@ export default class PoolCreateImpl extends PoolBaseImpl {
).execute()) as ScratchOrg[];
scratchOrgs = await scratchOrgInfoFetcher.getScratchOrgRecordId(scratchOrgs);

let scratchOrgInprogress = [];
const scratchOrgInprogress = [];

if (scratchOrgs && scratchOrgs.length>0) {
if (scratchOrgs && scratchOrgs.length > 0) {
scratchOrgs.forEach((scratchOrg) => {
scratchOrgInprogress.push({
Id: scratchOrg.recordId,
Expand All @@ -308,9 +352,7 @@ export default class PoolCreateImpl extends PoolBaseImpl {
await scratchOrgInfoAssigner.setScratchOrgInfo(scratchOrgInprogress);
}
return scratchOrgs;
}
else
{
} else {
throw new Error('No scratch orgs were found to be fetched');
}
}
Expand All @@ -322,7 +364,7 @@ export default class PoolCreateImpl extends PoolBaseImpl {
) {
pool.failedToCreate = 0;
for (let i = pool.scratchOrgs.length - 1; i >= 0; i--) {
let scratchOrg = pool.scratchOrgs[i];
const scratchOrg = pool.scratchOrgs[i];
if (scratchOrg.isScriptExecuted) {
continue;
}
Expand All @@ -335,7 +377,7 @@ export default class PoolCreateImpl extends PoolBaseImpl {
try {
//Delete scratchorgs that failed to execute script

let activeScratchOrgRecordId = await scratchOrgInfoFetcher.getActiveScratchOrgRecordIdGivenScratchOrg(
const activeScratchOrgRecordId = await scratchOrgInfoFetcher.getActiveScratchOrgRecordIdGivenScratchOrg(
scratchOrg.orgId
);

Expand All @@ -360,12 +402,12 @@ export default class PoolCreateImpl extends PoolBaseImpl {
LoggerLevel.INFO
);

let startTime = Date.now();
let result = await this.poolScriptExecutor.execute(scratchOrg, this.hubOrg, this.logLevel);
const startTime = Date.now();
const result = await this.poolScriptExecutor.execute(scratchOrg, this.hubOrg, this.logLevel);

if (result.isOk()) {
scratchOrg.isScriptExecuted = true;
let submitInfoToPool = await this.scratchOrgInfoAssigner.setScratchOrgInfo({
const submitInfoToPool = await this.scratchOrgInfoAssigner.setScratchOrgInfo({
Id: scratchOrg.recordId,
Allocation_status__c: 'Available',
});
Expand Down
6 changes: 4 additions & 2 deletions packages/core/src/scratchorg/pool/PoolDeleteImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ import { PoolBaseImpl } from './PoolBaseImpl';
import ScratchOrg from '../ScratchOrg';
import ScratchOrgInfoFetcher from './services/fetchers/ScratchOrgInfoFetcher';
import ScratchOrgOperator from '../ScratchOrgOperator';
import { Logger } from '@dxatscale/sfp-logger';
import { LoggerLevel } from '@dxatscale/sfp-logger';

export default class PoolDeleteImpl extends PoolBaseImpl {
private tag: string;
private mypool: boolean;
private allScratchOrgs: boolean;
private inprogressonly: boolean;

public constructor(hubOrg: Org, tag: string, mypool: boolean, allScratchOrgs: boolean, inprogressonly: boolean) {
public constructor(hubOrg: Org, tag: string, mypool: boolean, allScratchOrgs: boolean, inprogressonly: boolean,private logger:Logger) {
super(hubOrg);
this.hubOrg = hubOrg;
this.tag = tag;
Expand Down Expand Up @@ -52,7 +54,7 @@ export default class PoolDeleteImpl extends PoolBaseImpl {
if (activeScrathOrgs.records.length > 0) {
for (let scratchOrg of activeScrathOrgs.records) {
await new ScratchOrgOperator(this.hubOrg).delete(scratchOrg.Id);
SFPLogger.log(`Scratch org with username ${scratchOrg.SignupUsername} is deleted successfully`);
SFPLogger.log(`Scratch org with username ${scratchOrg.SignupUsername} is deleted successfully`,LoggerLevel.TRACE,this.logger);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ export default class ScratchOrgInfoFetcher {
let query;

if (tag)
query = `SELECT Pooltag__c, Id, CreatedDate, ScratchOrg, ExpirationDate, SignupUsername, SignupEmail, Password__c, Allocation_status__c,LoginUrl,SfdxAuthUrl__c FROM ScratchOrgInfo WHERE Pooltag__c = '${tag}' AND Status = 'Active' `;
query = `SELECT Pooltag__c, Id, CreatedDate, ScratchOrg, ExpirationDate, SignupUsername, SignupEmail, Password__c, Allocation_status__c,LoginUrl,SfdxAuthUrl__c FROM ScratchOrgInfo WHERE Pooltag__c = '${tag}' AND Status = 'Active' `;
else
query = `SELECT Pooltag__c, Id, CreatedDate, ScratchOrg, ExpirationDate, SignupUsername, SignupEmail, Password__c, Allocation_status__c,LoginUrl,SfdxAuthUrl__c FROM ScratchOrgInfo WHERE Pooltag__c != null AND Status = 'Active' `;
query = `SELECT Pooltag__c, Id, CreatedDate, ScratchOrg, ExpirationDate, SignupUsername, SignupEmail, Password__c, Allocation_status__c,LoginUrl,SfdxAuthUrl__c FROM ScratchOrgInfo WHERE Pooltag__c != null AND Status = 'Active' `;

if (isMyPool) {
query = query + ` AND createdby.username = '${this.hubOrg.getUsername()}' `;
Expand All @@ -68,6 +68,22 @@ export default class ScratchOrgInfoFetcher {
);
}

public async getOrphanedScratchOrgs() {
let hubConn = this.hubOrg.getConnection();

return retry(
async (bail) => {
let query;
query = `SELECT Id, Pooltag__c,SignupUsername,Description,ScratchOrg FROM ScratchOrgInfo WHERE Pooltag__c = null AND Status = 'Active'`;
query = query + ORDER_BY_FILTER;
SFPLogger.log('QUERY:' + query, LoggerLevel.TRACE);
const results = (await hubConn.query(query)) as any;
return results;
},
{ retries: 3, minTimeout: 3000 }
);
}

public async getActiveScratchOrgsByInfoId(scrathOrgIds: string) {
let hubConn = this.hubOrg.getConnection();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"tagDescription": "tag used to identify the scratch org pool",
"mypoolDescription": "Filter only Scratch orgs created by current user in the pool",
"allscratchorgsDescription": "Deletes all used and unused Scratch orgs from pool by the tag",
"inprogressonlyDescription": "Deletes all In Progress Scratch orgs from pool by the tag"
"inprogressonlyDescription": "Deletes all In Progress Scratch orgs from pool by the tag",
"recoverOrphanedScratchOrgsDescription": "Recovers scratch orgs that were created by salesforce but were not tagged to sfpowerscripts due to timeouts etc."
}
Loading