@ao-tools/pulumi-ao
Version:
A Pulumi provider for AO processes
193 lines • 7.61 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProcessProvider = void 0;
const AoConnect = require("@permaweb/aoconnect");
const Utils = require("../utilities");
/**
* Uses aoconnect to manage AO processes
*/
class ProcessProvider {
/**
* Checks that Resource inputs are valid
* Runs every time
*/
async check(_olds, news) {
const failures = [];
if (news.code && news.codeId) {
failures.push({
property: "codeId",
reason: "Only one of 'code' or 'codeId' can be set",
});
failures.push({
property: "code",
reason: "Only one of 'code' or 'codeId' can be set",
});
}
if (!news.code && !news.codeId) {
failures.push({
property: "codeId",
reason: "One of 'code' or 'codeId' must be set",
});
failures.push({
property: "code",
reason: "One of 'code' or 'codeId' must be set",
});
}
if (news.codeId && !Utils.isTxId(news.codeId))
failures.push({
property: "codeId",
reason: "ID invalid: " + news.codeId,
});
if (!Utils.isTxId(news.moduleId))
failures.push({
property: "moduleId",
reason: "ID invalid: " + news.moduleId,
});
if (!Utils.isTxId(news.schedulerId))
failures.push({
property: "schedulerId",
reason: "ID invalid: " + news.schedulerId,
});
if (!Utils.isTxId(news.authorityId))
failures.push({
property: "authorityId",
reason: "ID invalid: " + news.authorityId,
});
return { failures };
}
/**
* Loads the current state of a process from AO
* Called by pulumi refresh
*/
async read(id, props) {
const processTx = await Utils.loadProcessTx(props.gatewayUrl, id);
const readProps = {};
readProps.name = processTx.tags.find((t) => t.name === "Name")?.value ?? "";
readProps.codeId =
processTx.tags.find((t) => t.name === "On-Boot")?.value ?? "";
readProps.moduleId =
processTx.tags.find((t) => t.name === "Module")?.value ?? "";
readProps.schedulerId =
processTx.tags.find((t) => t.name === "Scheduler")?.value ?? "";
readProps.authorityId =
processTx.tags.find((t) => t.name === "Authority")?.value ?? "";
readProps.tags = Utils.tagsArrayToObject(processTx.tags);
return { id, props: readProps };
}
/**
* Checks if a process needs to be updated or replaced
* Called after check()
*/
async diff(_id, olds, news) {
let diffResult = {
changes: false,
};
// changes that require to create a new process
const replaces = [];
if (olds.name !== news.name)
replaces.push("name");
if (olds.moduleId !== news.moduleId)
replaces.push("moduleId");
if (olds.schedulerId !== news.schedulerId)
replaces.push("schedulerId");
if (olds.authorityId !== news.authorityId)
replaces.push("authorityId");
let tagsChanged = false;
for (let [name, value] of Object.entries(news.customTags)) {
if (!olds.customTags[name] || olds.customTags[name] !== value) {
replaces.push("customTags");
tagsChanged = true;
break;
}
}
if (!tagsChanged) {
for (let [name, value] of Object.entries(olds.customTags)) {
if (!news.customTags[name] || news.customTags[name] !== value) {
replaces.push("customTags");
break;
}
}
}
if (replaces.length > 0)
diffResult.replaces = replaces;
// changes that can be done via update
const updates = olds.codeId !== news.codeId ||
olds.code !== news.code ||
JSON.stringify(olds.environment) !== JSON.stringify(news.environment);
diffResult.changes = diffResult.replaces !== undefined || updates;
return diffResult;
}
/**
* Spawns a new AO process, and sets the environment variables after creation.
* Called after diff() when a new process is created or needs to be replaced.
*/
async create(inputs) {
const jwkWallet = Utils.loadWallet(inputs.walletPath);
const spawnTags = [
{ name: "Name", value: inputs.name },
{ name: "On-Boot", value: inputs.codeId ?? "Data" },
{ name: "Authority", value: inputs.authorityId },
];
const customTags = Utils.tagsObjectToArray(inputs.customTags);
const allTags = [...customTags, ...spawnTags];
const spawnOptions = {
module: inputs.moduleId,
scheduler: inputs.schedulerId,
signer: AoConnect.createDataItemSigner(jwkWallet),
tags: allTags,
};
if (inputs.code)
spawnOptions.data = inputs.code;
const processId = await AoConnect.spawn(spawnOptions);
const envTable = Object.entries(inputs.environment)
.map(([name, value]) => `["${name}"]="${value}"`)
.join(", ");
const setEnvironmentCode = `Environment = { ${envTable} }`;
await Utils.retry(5, () => AoConnect.message({
process: processId,
signer: AoConnect.createDataItemSigner(jwkWallet),
tags: [{ name: "Action", value: "Eval" }],
data: setEnvironmentCode,
}));
const processTx = await Utils.retry(5, () => Utils.loadProcessTx(inputs.gatewayUrl, processId));
const outputs = {
owner: processTx.owner.address,
tags: Utils.tagsArrayToObject(processTx.tags),
};
return {
id: processId,
outs: { ...inputs, ...outputs },
};
}
/**
* Sends messages to the AO process to update the environment variables and code.
* Called after diff() when a process needs to be updated.
*/
async update(id, olds, news) {
let codeUpdate = "";
if (JSON.stringify(news.environment) !== JSON.stringify(olds.environment)) {
const envTable = Object.entries(news.environment)
.map(([name, value]) => `["${name}"]="${value}"`)
.join(", ");
codeUpdate = `Environment = { ${envTable} }
`; // newline for additional code
}
if (news.code && !news.codeId && news.code !== olds.code)
codeUpdate += news.code;
if (news.codeId && !news.code && news.codeId !== olds.codeId)
codeUpdate += await Utils.loadCode(news.gatewayUrl, news.codeId);
const jwkWallet = Utils.loadWallet(news.walletPath);
const messageId = await AoConnect.message({
process: id,
signer: AoConnect.createDataItemSigner(jwkWallet),
tags: [{ name: "Action", value: "Eval" }],
data: codeUpdate,
});
const result = await AoConnect.result({ process: id, message: messageId });
if (result.Error)
throw new Error(result.Error);
return { outs: { ...news, tags: olds.tags, owner: olds.owner } };
}
}
exports.ProcessProvider = ProcessProvider;
//# sourceMappingURL=provider.js.map