[automation-api/nodejs] - Support recovery workflow (#6038)
This commit is contained in:
parent
9d228721b6
commit
8e8129012e
|
@ -9,7 +9,10 @@ CHANGELOG
|
|||
- [CLI] Allow `pulumi console` to accept a stack name
|
||||
[#6031](https://github.com/pulumi/pulumi/pull/6031)
|
||||
|
||||
- [CLI] Add a confirmation promt when using `pulumi policy rm`
|
||||
- Support recovery workflow (import/export/cancel) in NodeJS Automation API.
|
||||
[#6038](https://github.com/pulumi/pulumi/pull/6038)
|
||||
|
||||
- [CLI] Add a confirmation prompt when using `pulumi policy rm`
|
||||
[#6034](https://github.com/pulumi/pulumi/pull/6034)
|
||||
|
||||
- [CLI] Ensure errors with the Pulumi credentials file
|
||||
|
|
|
@ -16,10 +16,7 @@ import * as assert from "assert";
|
|||
import * as upath from "upath";
|
||||
|
||||
import { Config } from "../../index";
|
||||
import { ConfigMap } from "../../x/automation/config";
|
||||
import { LocalWorkspace } from "../../x/automation/localWorkspace";
|
||||
import { ProjectSettings } from "../../x/automation/projectSettings";
|
||||
import { Stack } from "../../x/automation/stack";
|
||||
import { ConfigMap, LocalWorkspace, ProjectSettings, Stack } from "../../x/automation";
|
||||
import { asyncTest } from "../util";
|
||||
|
||||
describe("LocalWorkspace", () => {
|
||||
|
@ -95,7 +92,7 @@ describe("LocalWorkspace", () => {
|
|||
const secretKey = normalizeConfigKey("secret", projectName);
|
||||
|
||||
try {
|
||||
const empty = await stack.getConfig(plainKey);
|
||||
await stack.getConfig(plainKey);
|
||||
} catch (error) {
|
||||
caught++;
|
||||
}
|
||||
|
@ -241,6 +238,40 @@ describe("LocalWorkspace", () => {
|
|||
|
||||
await stack.workspace.removeStack(stackName);
|
||||
}));
|
||||
it(`imports and exports stacks`, asyncTest(async() => {
|
||||
const program = async () => {
|
||||
const config = new Config();
|
||||
return {
|
||||
exp_static: "foo",
|
||||
exp_cfg: config.get("bar"),
|
||||
exp_secret: config.getSecret("buzz"),
|
||||
};
|
||||
};
|
||||
const stackName = `int_test${getTestSuffix()}`;
|
||||
const projectName = "import_export_node";
|
||||
const stack = await LocalWorkspace.createStack({ stackName, projectName, program });
|
||||
|
||||
try {
|
||||
await stack.setAllConfig({
|
||||
"bar": { value: "abc" },
|
||||
"buzz": { value: "secret", secret: true },
|
||||
});
|
||||
await stack.up();
|
||||
|
||||
// export stack
|
||||
const state = await stack.exportStack();
|
||||
|
||||
// import stack
|
||||
await stack.importStack(state);
|
||||
const configVal = await stack.getConfig("bar");
|
||||
assert.strictEqual(configVal.value, "abc");
|
||||
} finally {
|
||||
const destroyRes = await stack.destroy();
|
||||
assert.strictEqual(destroyRes.summary.kind, "destroy");
|
||||
assert.strictEqual(destroyRes.summary.result, "succeeded");
|
||||
await stack.workspace.removeStack(stackName);
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
const getTestSuffix = () => {
|
||||
|
|
|
@ -22,7 +22,7 @@ import { ConfigMap, ConfigValue } from "./config";
|
|||
import { ProjectSettings } from "./projectSettings";
|
||||
import { Stack } from "./stack";
|
||||
import { StackSettings } from "./stackSettings";
|
||||
import { PluginInfo, PulumiFn, StackSummary, WhoAmIResult, Workspace } from "./workspace";
|
||||
import { Deployment, PluginInfo, PulumiFn, StackSummary, WhoAmIResult, Workspace } from "./workspace";
|
||||
|
||||
/**
|
||||
* LocalWorkspace is a default implementation of the Workspace interface.
|
||||
|
@ -491,6 +491,33 @@ export class LocalWorkspace implements Workspace {
|
|||
return value;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* exportStack exports the deployment state of the stack.
|
||||
* This can be combined with Workspace.importStack to edit a stack's state (such as recovery from failed deployments).
|
||||
*
|
||||
* @param stackName the name of the stack.
|
||||
*/
|
||||
async exportStack(stackName: string): Promise<Deployment> {
|
||||
await this.selectStack(stackName);
|
||||
const result = await this.runPulumiCmd(["stack", "export", "--show-secrets"]);
|
||||
return JSON.parse(result.stdout);
|
||||
}
|
||||
/**
|
||||
* importStack imports the specified deployment state into a pre-existing stack.
|
||||
* This can be combined with Workspace.exportStack to edit a stack's state (such as recovery from failed deployments).
|
||||
*
|
||||
* @param stackName the name of the stack.
|
||||
* @param state the stack state to import.
|
||||
*/
|
||||
async importStack(stackName: string, state: Deployment): Promise<void> {
|
||||
await this.selectStack(stackName);
|
||||
const randomSuffix = Math.floor(100000 + Math.random() * 900000);
|
||||
const filepath = upath.joinSafe(os.tmpdir(), `automation-${randomSuffix}`);
|
||||
const contents = JSON.stringify(state, null, 4);
|
||||
fs.writeFileSync(filepath, contents);
|
||||
await this.runPulumiCmd(["stack", "import", "--file", filepath]);
|
||||
fs.unlinkSync(filepath);
|
||||
}
|
||||
/**
|
||||
* serializeArgsForOp is hook to provide additional args to every CLI commands before they are executed.
|
||||
* Provided with stack name,
|
||||
|
|
|
@ -18,7 +18,7 @@ import { CommandResult, runPulumiCmd } from "./cmd";
|
|||
import { ConfigMap, ConfigValue } from "./config";
|
||||
import { StackAlreadyExistsError } from "./errors";
|
||||
import { LanguageServer, maxRPCMessageSize } from "./server";
|
||||
import { PulumiFn, Workspace } from "./workspace";
|
||||
import { Deployment, PulumiFn, Workspace } from "./workspace";
|
||||
|
||||
const langrpc = require("../../proto/language_grpc_pb.js");
|
||||
|
||||
|
@ -421,6 +421,35 @@ export class Stack {
|
|||
}
|
||||
return history[0];
|
||||
}
|
||||
/**
|
||||
* Cancel stops a stack's currently running update. It returns an error if no update is currently running.
|
||||
* Note that this operation is _very dangerous_, and may leave the stack in an inconsistent state
|
||||
* if a resource operation was pending when the update was canceled.
|
||||
* This command is not supported for local backends.
|
||||
*/
|
||||
async cancel(): Promise<void> {
|
||||
await this.workspace.selectStack(this.name);
|
||||
await this.runPulumiCmd(["cancel", "--yes"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* exportStack exports the deployment state of the stack.
|
||||
* This can be combined with Stack.importStack to edit a stack's state (such as recovery from failed deployments).
|
||||
*/
|
||||
async exportStack(): Promise<Deployment> {
|
||||
return this.workspace.exportStack(this.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* importStack imports the specified deployment state into a pre-existing stack.
|
||||
* This can be combined with Stack.exportStack to edit a stack's state (such as recovery from failed deployments).
|
||||
*
|
||||
* @param state the stack state to import.
|
||||
*/
|
||||
async importStack(state: Deployment): Promise<void> {
|
||||
return this.workspace.importStack(this.name, state);
|
||||
}
|
||||
|
||||
private async runPulumiCmd(args: string[], onOutput?: (out: string) => void): Promise<CommandResult> {
|
||||
let envs: { [key: string]: string } = {};
|
||||
const pulumiHome = this.workspace.pulumiHome;
|
||||
|
|
|
@ -187,7 +187,21 @@ export interface Workspace {
|
|||
* Returns a list of all plugins installed in the Workspace.
|
||||
*/
|
||||
listPlugins(): Promise<PluginInfo[]>;
|
||||
// TODO import/export
|
||||
/**
|
||||
* exportStack exports the deployment state of the stack.
|
||||
* This can be combined with Workspace.importStack to edit a stack's state (such as recovery from failed deployments).
|
||||
*
|
||||
* @param stackName the name of the stack.
|
||||
*/
|
||||
exportStack(stackName: string): Promise<Deployment>;
|
||||
/**
|
||||
* importStack imports the specified deployment state into a pre-existing stack.
|
||||
* This can be combined with Workspace.exportStack to edit a stack's state (such as recovery from failed deployments).
|
||||
*
|
||||
* @param stackName the name of the stack.
|
||||
* @param state the stack state to import.
|
||||
*/
|
||||
importStack(stackName: string, state: Deployment): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -202,6 +216,21 @@ export interface StackSummary {
|
|||
url?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deployment encapsulates the state of a stack deployment.
|
||||
*/
|
||||
export interface Deployment {
|
||||
/**
|
||||
* Version indicates the schema of the encoded deployment.
|
||||
*/
|
||||
version: number;
|
||||
/**
|
||||
* The pulumi deployment.
|
||||
*/
|
||||
// TODO: Expand type to encapsulate deployment.
|
||||
deployment: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* A Pulumi program as an inline function (in process).
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue