Add back-end function to create deployment with auction
This commit is contained in:
parent
0466632426
commit
da3ecde4a1
@ -28,6 +28,12 @@ export enum DeploymentStatus {
|
||||
Deleting = 'Deleting',
|
||||
}
|
||||
|
||||
export interface ApplicationDeploymentAuction {
|
||||
application: string;
|
||||
auction: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface ApplicationDeploymentRequest {
|
||||
type: string;
|
||||
version: string;
|
||||
|
@ -10,14 +10,16 @@ import {
|
||||
ApplicationRecord,
|
||||
Deployment,
|
||||
ApplicationDeploymentRequest,
|
||||
ApplicationDeploymentRemovalRequest
|
||||
ApplicationDeploymentRemovalRequest,
|
||||
ApplicationDeploymentAuction
|
||||
} from './entity/Deployment';
|
||||
import { AppDeploymentRecord, AppDeploymentRemovalRecord, PackageJSON } from './types';
|
||||
import { AppDeploymentRecord, AppDeploymentRemovalRecord, AuctionData, PackageJSON } from './types';
|
||||
import { sleep } from './utils';
|
||||
|
||||
const log = debug('snowball:registry');
|
||||
|
||||
const APP_RECORD_TYPE = 'ApplicationRecord';
|
||||
const APP_DEPLOYMENT_AUCTION_RECORD_TYPE = 'ApplicationDeploymentAuction';
|
||||
const APP_DEPLOYMENT_REQUEST_TYPE = 'ApplicationDeploymentRequest';
|
||||
const APP_DEPLOYMENT_REMOVAL_REQUEST_TYPE = 'ApplicationDeploymentRemovalRequest';
|
||||
const APP_DEPLOYMENT_RECORD_TYPE = 'ApplicationDeploymentRecord';
|
||||
@ -148,10 +150,94 @@ export class Registry {
|
||||
};
|
||||
}
|
||||
|
||||
async createApplicationDeploymentAuction (data: {
|
||||
deployment: Deployment,
|
||||
appName: string,
|
||||
},
|
||||
auctionData: AuctionData,
|
||||
): Promise<{
|
||||
applicationDeploymentAuctionId: string;
|
||||
applicationDeploymentAuctionData: ApplicationDeploymentAuction;
|
||||
deployerLrns: string[];
|
||||
}> {
|
||||
const lrn = this.getLrn(data.appName);
|
||||
const records = await this.registry.resolveNames([lrn]);
|
||||
const applicationRecord = records[0];
|
||||
|
||||
if (!applicationRecord) {
|
||||
throw new Error(`No record found for ${lrn}`);
|
||||
}
|
||||
|
||||
const fee = parseGasAndFees(this.registryConfig.fee.gas, this.registryConfig.fee.fees);
|
||||
|
||||
// TODO: Take auction params from user
|
||||
const auctionResult = await this.registry.createProviderAuction(
|
||||
{
|
||||
commitFee: auctionData.commitFee,
|
||||
commitsDuration: auctionData.commitsDuration,
|
||||
revealFee: auctionData.revealFee,
|
||||
revealsDuration: auctionData.revealsDuration,
|
||||
denom: auctionData.denom,
|
||||
maxPrice: auctionData.maxPrice,
|
||||
numProviders: auctionData.numProviders,
|
||||
},
|
||||
this.registryConfig.privateKey,
|
||||
fee
|
||||
)
|
||||
|
||||
if (!auctionResult.auction) {
|
||||
throw new Error('Error creating auction');
|
||||
}
|
||||
|
||||
// Create record of type applicationDeploymentAuction and publish
|
||||
const applicationDeploymentAuction = {
|
||||
application: `${lrn}@${applicationRecord.attributes.app_version}`,
|
||||
auction: auctionResult.auction.id,
|
||||
type: APP_DEPLOYMENT_AUCTION_RECORD_TYPE,
|
||||
};
|
||||
|
||||
await sleep(SLEEP_DURATION);
|
||||
|
||||
const result = await this.registry.setRecord(
|
||||
{
|
||||
privateKey: this.registryConfig.privateKey,
|
||||
record: applicationDeploymentAuction,
|
||||
bondId: this.registryConfig.bondId
|
||||
},
|
||||
this.registryConfig.privateKey,
|
||||
fee
|
||||
);
|
||||
log(`Application deployment auction record published: ${result.id}`);
|
||||
log('Application deployment auction data:', applicationDeploymentAuction);
|
||||
|
||||
let deployerLrns = [];
|
||||
const { winnerAddresses } = auctionResult.auction;
|
||||
|
||||
for (const auctionWinner of winnerAddresses) {
|
||||
const deployerRecord = await this.registry.queryRecords(
|
||||
{
|
||||
paymentAddress: auctionWinner,
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
const lrn = deployerRecord.names.length > 0 ? deployerRecord.names[0] : null;
|
||||
deployerLrns.push(lrn);
|
||||
}
|
||||
|
||||
return {
|
||||
applicationDeploymentAuctionId: auctionResult.auction.id,
|
||||
applicationDeploymentAuctionData: applicationDeploymentAuction,
|
||||
deployerLrns
|
||||
};
|
||||
}
|
||||
|
||||
async createApplicationDeploymentRequest (data: {
|
||||
deployment: Deployment,
|
||||
appName: string,
|
||||
repository: string,
|
||||
auctionId?: string,
|
||||
lrn?: string,
|
||||
environmentVariables: { [key: string]: string },
|
||||
dns: string,
|
||||
}): Promise<{
|
||||
@ -175,8 +261,6 @@ export class Registry {
|
||||
dns: data.dns,
|
||||
|
||||
// TODO: Not set in test-progressive-web-app CI
|
||||
// deployment: '$CERC_REGISTRY_DEPLOYMENT_LRN',
|
||||
|
||||
// https://git.vdb.to/cerc-io/laconic-registry-cli/commit/129019105dfb93bebcea02fde0ed64d0f8e5983b
|
||||
config: JSON.stringify({
|
||||
env: data.environmentVariables
|
||||
@ -187,7 +271,9 @@ export class Registry {
|
||||
)}`,
|
||||
repository: data.repository,
|
||||
repository_ref: data.deployment.commitHash
|
||||
})
|
||||
}),
|
||||
...(data.lrn && { deployer: data.lrn }),
|
||||
...(data.auctionId && { auction: data.auctionId }),
|
||||
};
|
||||
|
||||
await sleep(SLEEP_DURATION);
|
||||
|
@ -19,6 +19,7 @@ import {
|
||||
AddProjectFromTemplateInput,
|
||||
AppDeploymentRecord,
|
||||
AppDeploymentRemovalRecord,
|
||||
AuctionData,
|
||||
GitPushEventPayload,
|
||||
PackageJSON,
|
||||
} from './types';
|
||||
@ -644,10 +645,145 @@ export class Service {
|
||||
return newDeployment;
|
||||
}
|
||||
|
||||
async createDeploymentFromAuction(
|
||||
userId: string,
|
||||
octokit: Octokit,
|
||||
data: DeepPartial<Deployment>,
|
||||
auctionData: AuctionData
|
||||
): Promise<Deployment> {
|
||||
assert(data.project?.repository, 'Project repository not found');
|
||||
log(
|
||||
`Creating deployment in project ${data.project.name} from branch ${data.branch}`,
|
||||
);
|
||||
const [owner, repo] = data.project.repository.split('/');
|
||||
|
||||
const { data: packageJSONData } = await octokit.rest.repos.getContent({
|
||||
owner,
|
||||
repo,
|
||||
path: 'package.json',
|
||||
ref: data.commitHash,
|
||||
});
|
||||
|
||||
if (!packageJSONData) {
|
||||
throw new Error('Package.json file not found');
|
||||
}
|
||||
|
||||
assert(!Array.isArray(packageJSONData) && packageJSONData.type === 'file');
|
||||
const packageJSON: PackageJSON = JSON.parse(atob(packageJSONData.content));
|
||||
|
||||
assert(packageJSON.name, "name field doesn't exist in package.json");
|
||||
|
||||
const repoUrl = (
|
||||
await octokit.rest.repos.get({
|
||||
owner,
|
||||
repo,
|
||||
})
|
||||
).data.html_url;
|
||||
|
||||
// TODO: Set environment variables for each deployment (environment variables can`t be set in application record)
|
||||
const { applicationRecordId, applicationRecordData } =
|
||||
await this.registry.createApplicationRecord({
|
||||
appName: repo,
|
||||
packageJSON,
|
||||
appType: data.project!.template!,
|
||||
commitHash: data.commitHash!,
|
||||
repoUrl,
|
||||
});
|
||||
|
||||
// Update previous deployment with prod branch domain
|
||||
// TODO: Fix unique constraint error for domain
|
||||
if (data.domain) {
|
||||
await this.db.updateDeployment(
|
||||
{
|
||||
domainId: data.domain.id,
|
||||
},
|
||||
{
|
||||
domain: null,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const newDeployment = await this.db.addDeployment({
|
||||
project: data.project,
|
||||
branch: data.branch,
|
||||
commitHash: data.commitHash,
|
||||
commitMessage: data.commitMessage,
|
||||
environment: data.environment,
|
||||
status: DeploymentStatus.Building,
|
||||
applicationRecordId,
|
||||
applicationRecordData,
|
||||
domain: data.domain,
|
||||
createdBy: Object.assign(new User(), {
|
||||
id: userId,
|
||||
}),
|
||||
});
|
||||
|
||||
log(
|
||||
`Created deployment ${newDeployment.id} and published application record ${applicationRecordId}`,
|
||||
);
|
||||
|
||||
const deploymentAuctionData = await this.registry.createApplicationDeploymentAuction({
|
||||
deployment: newDeployment,
|
||||
appName: repo
|
||||
}, auctionData
|
||||
);
|
||||
|
||||
const deploymentAuctionId = deploymentAuctionData.applicationDeploymentAuctionId;
|
||||
|
||||
const environmentVariables =
|
||||
await this.db.getEnvironmentVariablesByProjectId(data.project.id!, {
|
||||
environment: Environment.Production,
|
||||
});
|
||||
|
||||
const environmentVariablesObj = environmentVariables.reduce(
|
||||
(acc, env) => {
|
||||
acc[env.key] = env.value;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as { [key: string]: string },
|
||||
);
|
||||
|
||||
// To set project DNS
|
||||
if (data.environment === Environment.Production) {
|
||||
// On deleting deployment later, project DNS deployment is also deleted
|
||||
// So publish project DNS deployment first so that ApplicationDeploymentRecord for the same is available when deleting deployment later
|
||||
await this.registry.createApplicationDeploymentRequest({
|
||||
deployment: newDeployment,
|
||||
appName: repo,
|
||||
repository: repoUrl,
|
||||
environmentVariables: environmentVariablesObj,
|
||||
dns: `${newDeployment.project.name}`,
|
||||
});
|
||||
}
|
||||
|
||||
for (const deployer in deploymentAuctionData.deployerLrns) {
|
||||
const { applicationDeploymentRequestId, applicationDeploymentRequestData } =
|
||||
// Create requests for all the deployers
|
||||
await this.registry.createApplicationDeploymentRequest({
|
||||
deployment: newDeployment,
|
||||
appName: repo,
|
||||
repository: repoUrl,
|
||||
auctionId: deploymentAuctionId,
|
||||
lrn: deployer,
|
||||
environmentVariables: environmentVariablesObj,
|
||||
dns: `${newDeployment.project.name}-${newDeployment.id}`,
|
||||
});
|
||||
|
||||
await this.db.updateDeploymentById(newDeployment.id, {
|
||||
applicationDeploymentRequestId,
|
||||
applicationDeploymentRequestData,
|
||||
});
|
||||
}
|
||||
|
||||
return newDeployment;
|
||||
}
|
||||
|
||||
async addProjectFromTemplate(
|
||||
user: User,
|
||||
organizationSlug: string,
|
||||
data: AddProjectFromTemplateInput,
|
||||
auctionData?: AuctionData
|
||||
): Promise<Project | undefined> {
|
||||
try {
|
||||
const octokit = await this.getOctokit(user.id);
|
||||
@ -678,7 +814,7 @@ export class Service {
|
||||
repository: gitRepo.data.full_name,
|
||||
// TODO: Set selected template
|
||||
template: 'webapp',
|
||||
});
|
||||
}, auctionData);
|
||||
|
||||
if (!project || !project.id) {
|
||||
throw new Error('Failed to create project from template');
|
||||
@ -695,6 +831,7 @@ export class Service {
|
||||
user: User,
|
||||
organizationSlug: string,
|
||||
data: DeepPartial<Project>,
|
||||
auctiondata?: AuctionData
|
||||
): Promise<Project | undefined> {
|
||||
const organization = await this.db.getOrganization({
|
||||
where: {
|
||||
@ -720,14 +857,18 @@ export class Service {
|
||||
});
|
||||
|
||||
// Create deployment with prod branch and latest commit
|
||||
const deployment = await this.createDeployment(user.id, octokit, {
|
||||
const deploymentData = {
|
||||
project,
|
||||
branch: project.prodBranch,
|
||||
environment: Environment.Production,
|
||||
domain: null,
|
||||
commitHash: latestCommit.sha,
|
||||
commitMessage: latestCommit.commit.message,
|
||||
});
|
||||
};
|
||||
|
||||
const deployment = auctiondata
|
||||
? await this.createDeploymentFromAuction(user.id, octokit, deploymentData, auctiondata)
|
||||
: await this.createDeployment(user.id, octokit, deploymentData);
|
||||
|
||||
await this.createRepoHook(octokit, project);
|
||||
|
||||
|
@ -69,3 +69,13 @@ export interface AddProjectFromTemplateInput {
|
||||
name: string;
|
||||
isPrivate: boolean;
|
||||
}
|
||||
|
||||
export interface AuctionData {
|
||||
commitFee: string,
|
||||
commitsDuration: string,
|
||||
revealFee: string,
|
||||
revealsDuration: string,
|
||||
denom: string,
|
||||
maxPrice: string,
|
||||
numProviders: number,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user