Compare commits
33
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a17386eb68 | ||
|
|
f318a95641 | ||
|
|
e9f746cd8b | ||
|
|
e59c14440c | ||
|
|
22f8a8c943 | ||
|
|
df5062c75f | ||
|
|
22e814840e | ||
|
|
9d27dd568a | ||
|
|
fc62392c83 | ||
|
|
2ee65b8fd8 | ||
|
|
d0302d0153 | ||
|
|
90e9260eb6 | ||
|
|
6dcee29487 | ||
|
|
5be10b6b0a | ||
|
|
6e8af82dc2 | ||
|
|
1a5ab4d47e | ||
|
|
6df685831f | ||
|
|
13892015a5 | ||
|
|
55500b5914 | ||
|
|
a7190ed804 | ||
|
|
6a79ee5c47 | ||
|
|
f2ba931961 | ||
|
|
85b4265fbe | ||
|
|
63223a4807 | ||
|
|
9784eb4f4a | ||
|
|
624f36155a | ||
|
|
82c4b71694 | ||
|
|
5d9c965f54 | ||
|
|
56eccb48b3 | ||
|
|
b5762432e6 | ||
|
|
4ae4670dca | ||
|
|
c202554002 | ||
|
|
534871a7ae |
@@ -27,6 +27,7 @@
|
||||
"nanoid": "3",
|
||||
"nanoid-dictionary": "^5.0.0-beta.1",
|
||||
"octokit": "^3.1.2",
|
||||
"openpgp": "^6.0.1",
|
||||
"reflect-metadata": "^0.2.1",
|
||||
"semver": "^7.6.0",
|
||||
"toml": "^3.0.0",
|
||||
|
||||
@@ -15,6 +15,9 @@ export class Deployer {
|
||||
@Column('varchar')
|
||||
baseDomain!: string;
|
||||
|
||||
@Column('varchar', { nullable: true})
|
||||
publicKey!: string | null;
|
||||
|
||||
@Column('varchar', { nullable: true })
|
||||
minimumPayment!: string | null;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { DateTime } from 'luxon';
|
||||
import { Octokit } from 'octokit';
|
||||
import { inc as semverInc } from 'semver';
|
||||
import { DeepPartial } from 'typeorm';
|
||||
import * as openpgp from 'openpgp';
|
||||
|
||||
import { Account, DEFAULT_GAS_ESTIMATION_MULTIPLIER, Registry as LaconicRegistry, getGasPrice, parseGasAndFees } from '@cerc-io/registry-sdk';
|
||||
import { DeliverTxResponse, IndexedTx } from '@cosmjs/stargate';
|
||||
@@ -246,8 +247,11 @@ export class Registry {
|
||||
repository: string,
|
||||
auctionId?: string | null,
|
||||
lrn: string,
|
||||
apiUrl: string,
|
||||
environmentVariables: { [key: string]: string },
|
||||
dns: string,
|
||||
requesterAddress: string,
|
||||
publicKey: string,
|
||||
payment?: string | null
|
||||
}): Promise<{
|
||||
applicationDeploymentRequestId: string;
|
||||
@@ -261,6 +265,13 @@ export class Registry {
|
||||
throw new Error(`No record found for ${lrn}`);
|
||||
}
|
||||
|
||||
const hash = await this.generateConfigHash(
|
||||
data.environmentVariables,
|
||||
data.requesterAddress,
|
||||
data.publicKey,
|
||||
data.apiUrl,
|
||||
);
|
||||
|
||||
// Create record of type ApplicationDeploymentRequest and publish
|
||||
const applicationDeploymentRequest = {
|
||||
type: APP_DEPLOYMENT_REQUEST_TYPE,
|
||||
@@ -271,7 +282,7 @@ export class Registry {
|
||||
|
||||
// https://git.vdb.to/cerc-io/laconic-registry-cli/commit/129019105dfb93bebcea02fde0ed64d0f8e5983b
|
||||
config: JSON.stringify({
|
||||
env: data.environmentVariables
|
||||
ref: hash,
|
||||
}),
|
||||
meta: JSON.stringify({
|
||||
note: `Added by Snowball @ ${DateTime.utc().toFormat(
|
||||
@@ -431,9 +442,9 @@ export class Registry {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch deployment DNS record by Id
|
||||
* Fetch record by Id
|
||||
*/
|
||||
async getDNSRecordById(id: string): Promise<DNSRecord[]> {
|
||||
async getRecordById(id: string): Promise<any[]> {
|
||||
return this.registry.getRecordsByIds([id]);
|
||||
}
|
||||
|
||||
@@ -538,4 +549,43 @@ export class Registry {
|
||||
assert(this.registryConfig.authority, "Authority doesn't exist");
|
||||
return `lrn://${this.registryConfig.authority}/applications/${appName}`;
|
||||
}
|
||||
|
||||
async generateConfigHash(
|
||||
environmentVariables: { [key: string]: string },
|
||||
requesterAddress: string,
|
||||
pubKey: string,
|
||||
url: string,
|
||||
): Promise<string> {
|
||||
// Config to be encrypted
|
||||
const config = {
|
||||
authorized: [requesterAddress],
|
||||
config: { env: environmentVariables },
|
||||
};
|
||||
|
||||
// Serialize the config
|
||||
const serialized = JSON.stringify(config, null, 2);
|
||||
|
||||
const armoredKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n${pubKey}\n\n-----END PGP PUBLIC KEY BLOCK-----`;
|
||||
const publicKey = await openpgp.readKey({ armoredKey });
|
||||
|
||||
// Encrypt the config
|
||||
const encrypted = await openpgp.encrypt({
|
||||
message: await openpgp.createMessage({ text: serialized }),
|
||||
encryptionKeys: publicKey,
|
||||
format: 'binary',
|
||||
});
|
||||
|
||||
// Get the hash after uploading encrypted config
|
||||
const response = await fetch(`${url}/upload/config`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
},
|
||||
body: encrypted,
|
||||
});
|
||||
|
||||
const configHash = await response.json();
|
||||
|
||||
return configHash.id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
AppDeploymentRemovalRecord,
|
||||
AuctionParams,
|
||||
DeployerRecord,
|
||||
DNSRecord,
|
||||
DNSRecordAttributes,
|
||||
EnvironmentVariables,
|
||||
GitPushEventPayload,
|
||||
@@ -200,61 +201,61 @@ export class Service {
|
||||
if (!deployment.project) {
|
||||
log(`Project ${deployment.projectId} not found`);
|
||||
return;
|
||||
} else {
|
||||
const dnsRecord = (await this.laconicRegistry.getDNSRecordById(record.attributes.dns))[0];
|
||||
}
|
||||
|
||||
const dnsRecordData: DNSRecordAttributes = {
|
||||
name: dnsRecord.attributes.name,
|
||||
request: dnsRecord.attributes.request,
|
||||
resourceType: dnsRecord.attributes.resourceType,
|
||||
value: dnsRecord.attributes.value,
|
||||
version: dnsRecord.attributes.version,
|
||||
}
|
||||
const dnsRecord: DNSRecord = (await this.laconicRegistry.getRecordById(record.attributes.dns))[0];
|
||||
|
||||
deployment.applicationDeploymentRecordId = record.id;
|
||||
deployment.applicationDeploymentRecordData = record.attributes;
|
||||
deployment.url = record.attributes.url;
|
||||
deployment.status = DeploymentStatus.Ready;
|
||||
deployment.isCurrent = deployment.environment === Environment.Production;
|
||||
deployment.dnsRecordData = dnsRecordData;
|
||||
const dnsRecordData: DNSRecordAttributes = {
|
||||
name: dnsRecord.attributes.name,
|
||||
request: dnsRecord.attributes.request,
|
||||
resourceType: dnsRecord.attributes.resourceType,
|
||||
value: dnsRecord.attributes.value,
|
||||
version: dnsRecord.attributes.version,
|
||||
}
|
||||
|
||||
if (deployment.isDNS) {
|
||||
// Delete previous DNS deployment
|
||||
const oldDNSDeployment = await this.db.getDeployment({
|
||||
where: {
|
||||
projectId: deployment.project.id,
|
||||
deployer: deployment.deployer,
|
||||
isDNS: true,
|
||||
isCurrent: true,
|
||||
}
|
||||
});
|
||||
deployment.applicationDeploymentRecordId = record.id;
|
||||
deployment.applicationDeploymentRecordData = record.attributes;
|
||||
deployment.url = record.attributes.url;
|
||||
deployment.status = DeploymentStatus.Ready;
|
||||
deployment.isCurrent = deployment.environment === Environment.Production;
|
||||
deployment.dnsRecordData = dnsRecordData;
|
||||
|
||||
if (oldDNSDeployment) {
|
||||
await this.db.deleteDeploymentById(oldDNSDeployment.id);
|
||||
if (oldDNSDeployment.domain) {
|
||||
await this.db.updateDeploymentById(deployment.id, { domain: oldDNSDeployment.domain })
|
||||
}
|
||||
if (deployment.isDNS) {
|
||||
// Delete previous DNS deployment
|
||||
const oldDNSDeployment = await this.db.getDeployment({
|
||||
where: {
|
||||
projectId: deployment.project.id,
|
||||
deployer: deployment.deployer,
|
||||
isDNS: true,
|
||||
isCurrent: true,
|
||||
}
|
||||
});
|
||||
|
||||
if (oldDNSDeployment) {
|
||||
await this.db.deleteDeploymentById(oldDNSDeployment.id);
|
||||
if (oldDNSDeployment.domain) {
|
||||
await this.db.updateDeploymentById(deployment.id, { domain: oldDNSDeployment.domain })
|
||||
}
|
||||
}
|
||||
|
||||
await this.db.updateDeploymentById(deployment.id, deployment);
|
||||
|
||||
// Release deployer funds on successful deployment
|
||||
if (!deployment.project.fundsReleased) {
|
||||
const fundsReleased = await this.releaseDeployerFundsByProjectId(deployment.projectId);
|
||||
|
||||
// Return remaining amount to owner
|
||||
await this.returnUserFundsByProjectId(deployment.projectId, true);
|
||||
|
||||
await this.db.updateProjectById(deployment.projectId, {
|
||||
fundsReleased,
|
||||
});
|
||||
}
|
||||
|
||||
log(
|
||||
`Updated deployment ${deployment.id} with URL ${record.attributes.url}`,
|
||||
);
|
||||
}
|
||||
|
||||
await this.db.updateDeploymentById(deployment.id, deployment);
|
||||
|
||||
// Release deployer funds on successful deployment
|
||||
if (!deployment.project.fundsReleased) {
|
||||
const fundsReleased = await this.releaseDeployerFundsByProjectId(deployment.projectId);
|
||||
|
||||
// Return remaining amount to owner
|
||||
await this.returnUserFundsByProjectId(deployment.projectId, true);
|
||||
|
||||
await this.db.updateProjectById(deployment.projectId, {
|
||||
fundsReleased,
|
||||
});
|
||||
}
|
||||
|
||||
log(
|
||||
`Updated deployment ${deployment.id} with URL ${record.attributes.url}`,
|
||||
);
|
||||
});
|
||||
|
||||
await Promise.all(deploymentUpdatePromises);
|
||||
@@ -684,6 +685,7 @@ export class Service {
|
||||
const deployment = await this.createDeploymentFromData(userId, data, deployer!.deployerLrn!, applicationRecordId, applicationRecordData, false);
|
||||
const dnsDeployment = await this.createDeploymentFromData(userId, data, deployer!.deployerLrn!, applicationRecordId, applicationRecordData, true);
|
||||
|
||||
const address = await this.getAddress();
|
||||
const { repo, repoUrl } = await getRepoDetails(octokit, data.project.repository, data.commitHash);
|
||||
const environmentVariablesObj = await this.getEnvVariables(data.project!.id!);
|
||||
|
||||
@@ -702,8 +704,11 @@ export class Service {
|
||||
environmentVariables: environmentVariablesObj,
|
||||
dns: domain?.name ?? `${dnsDeployment.project.name}`,
|
||||
lrn: deployer!.deployerLrn!,
|
||||
apiUrl: deployer!.deployerApiUrl!,
|
||||
payment: data.project.txHash,
|
||||
auctionId: data.project.auctionId
|
||||
auctionId: data.project.auctionId,
|
||||
requesterAddress: address,
|
||||
publicKey: deployer!.publicKey!
|
||||
});
|
||||
|
||||
await this.db.updateDeploymentById(dnsDeployment.id, {
|
||||
@@ -719,10 +724,13 @@ export class Service {
|
||||
appName: repo,
|
||||
repository: repoUrl,
|
||||
lrn: deployer!.deployerLrn!,
|
||||
apiUrl: deployer!.deployerApiUrl!,
|
||||
environmentVariables: environmentVariablesObj,
|
||||
dns: `${deployment.project.name}-${deployment.id}`,
|
||||
payment: data.project.txHash,
|
||||
auctionId: data.project.auctionId
|
||||
auctionId: data.project.auctionId,
|
||||
requesterAddress: address,
|
||||
publicKey: deployer!.publicKey!
|
||||
});
|
||||
|
||||
await this.db.updateDeploymentById(deployment.id, {
|
||||
@@ -775,6 +783,7 @@ export class Service {
|
||||
|
||||
const deployment = await this.createDeploymentFromData(project.ownerId!, deploymentData, deployerLrn, applicationRecordId, applicationRecordData, false);
|
||||
const dnsDeployment = await this.createDeploymentFromData(project.ownerId!, deploymentData, deployerLrn, applicationRecordId, applicationRecordData, true);
|
||||
const address = await this.getAddress();
|
||||
|
||||
const environmentVariablesObj = await this.getEnvVariables(project!.id!);
|
||||
// To set project DNS
|
||||
@@ -790,6 +799,9 @@ export class Service {
|
||||
dns: `${dnsDeployment.project.name}`,
|
||||
auctionId: project.auctionId!,
|
||||
lrn: deployerLrn,
|
||||
apiUrl: deployer!.deployerApiUrl!,
|
||||
requesterAddress: address,
|
||||
publicKey: deployer!.publicKey!
|
||||
});
|
||||
|
||||
await this.db.updateDeploymentById(dnsDeployment.id, {
|
||||
@@ -806,8 +818,11 @@ export class Service {
|
||||
repository: repoUrl,
|
||||
auctionId: project.auctionId!,
|
||||
lrn: deployerLrn,
|
||||
apiUrl: deployer!.deployerApiUrl!,
|
||||
environmentVariables: environmentVariablesObj,
|
||||
dns: `${deployment.project.name}-${deployment.id}`,
|
||||
requesterAddress: address,
|
||||
publicKey: deployer!.publicKey!
|
||||
});
|
||||
|
||||
await this.db.updateDeploymentById(deployment.id, {
|
||||
@@ -1500,6 +1515,7 @@ export class Service {
|
||||
const deployerApiUrl = record.attributes.apiUrl;
|
||||
const minimumPayment = record.attributes.minimumPayment;
|
||||
const paymentAddress = record.attributes.paymentAddress;
|
||||
const publicKey = record.attributes.publicKey;
|
||||
const baseDomain = deployerApiUrl.substring(deployerApiUrl.indexOf('.') + 1);
|
||||
|
||||
const deployerData = {
|
||||
@@ -1508,7 +1524,8 @@ export class Service {
|
||||
deployerApiUrl,
|
||||
baseDomain,
|
||||
minimumPayment,
|
||||
paymentAddress
|
||||
paymentAddress,
|
||||
publicKey
|
||||
};
|
||||
|
||||
// TODO: Update deployers table in a separate job
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Deployment, DeploymentStatus, Environment } from '../src/entity/Deploym
|
||||
const log = debug('snowball:publish-deploy-records');
|
||||
|
||||
async function main() {
|
||||
const { registryConfig, database, misc } = await getConfig();
|
||||
const { registryConfig, database } = await getConfig();
|
||||
|
||||
const registry = new Registry(
|
||||
registryConfig.gqlEndpoint,
|
||||
|
||||
@@ -14407,6 +14407,11 @@ open@^8.0.4, open@^8.4.0:
|
||||
is-docker "^2.1.1"
|
||||
is-wsl "^2.2.0"
|
||||
|
||||
openpgp@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/openpgp/-/openpgp-6.0.1.tgz#ab3a2c08a3e23ac7f85c4fc62a3e715d50dc08fb"
|
||||
integrity sha512-3lReDKjgWsKFArZT4Y/yj7/Q0q6/VhXarn4WqKEkyiBWckNjrThSGoB1t0IKo3Ke0ClvBpyQfTwumkGUkxOwww==
|
||||
|
||||
optimism@^0.18.0:
|
||||
version "0.18.0"
|
||||
resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.18.0.tgz#e7bb38b24715f3fdad8a9a7fc18e999144bbfa63"
|
||||
|
||||
Reference in New Issue
Block a user