From e1724f0d52c25370dc09adb3be2bb263a90f362f Mon Sep 17 00:00:00 2001 From: ZauberNerd Date: Wed, 17 Jul 2024 22:53:31 +0200 Subject: [PATCH] feat: accept onDestroy command to run before resource destruction Accept an optional command via `onDestroy` parameter to enable running a command during destroy-time. See also: https://developer.hashicorp.com/terraform/language/resources/provisioners/syntax#destroy-time-provisioners --- API.md | 24 ++++++++++++++++++++++++ src/index.ts | 34 ++++++++++++++++++++++++++++++++-- test/local-exec.test.ts | 31 +++++++++++++++++++++++++++---- 3 files changed, 83 insertions(+), 6 deletions(-) diff --git a/API.md b/API.md index 16c6acf..b306686 100644 --- a/API.md +++ b/API.md @@ -501,6 +501,7 @@ Refer to the {@link https://registry.terraform.io/providers/hashicorp/null/3.2.2 | triggers | {[ key: string ]: string} | *No description.* | | command | string | *No description.* | | cwd | string | *No description.* | +| onDestroy | string | *No description.* | --- @@ -696,6 +697,16 @@ public readonly cwd: string; --- +##### `onDestroy`Optional + +```typescript +public readonly onDestroy: string; +``` + +- *Type:* string + +--- + #### Constants | **Name** | **Type** | **Description** | @@ -1122,6 +1133,7 @@ const localExecConfig: LocalExecConfig = { ... } | copyBeforeRun | boolean | If set to true, the working directory will be copied to an asset directory. | | dependsOn | cdktf.ITerraformDependable[] | *No description.* | | lifecycle | cdktf.TerraformResourceLifecycle | *No description.* | +| onDestroy | string | Command to run at destroy-time. | | provider | cdktf.TerraformProvider | *No description.* | | triggers | {[ key: string ]: string} | *No description.* | @@ -1187,6 +1199,18 @@ public readonly lifecycle: TerraformResourceLifecycle; --- +##### `onDestroy`Optional + +```typescript +public readonly onDestroy: string; +``` + +- *Type:* string + +Command to run at destroy-time. + +--- + ##### `provider`Optional ```typescript diff --git a/src/index.ts b/src/index.ts index 30d6ebe..4f6e5b5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -42,13 +42,27 @@ export interface LocalExecConfig { * @default true */ readonly copyBeforeRun?: boolean; + + /** + * Command to run at destroy-time. + */ + readonly onDestroy?: string; } export { NullProvider as Provider } from "@cdktf/provider-null/lib/provider"; +interface LocalExecProvisioner { + "local-exec": { + working_dir: string; + command: string; + when?: string; + }; +} + export class LocalExec extends Resource { public cwd: string; public command: string; + public onDestroy?: string; constructor(scope: Construct, id: string, config: LocalExecConfig) { super(scope, id, config); @@ -63,8 +77,9 @@ export class LocalExec extends Resource { this.cwd = workingDir; this.command = config.command; + this.onDestroy = config.onDestroy; - this.addOverride("provisioner", [ + const overrides: LocalExecProvisioner[] = [ { "local-exec": { working_dir: workingDir, @@ -74,6 +89,21 @@ export class LocalExec extends Resource { }), }, }, - ]); + ]; + + if (typeof this.onDestroy === "string") { + overrides.push({ + "local-exec": { + when: "destroy", + working_dir: workingDir, + command: Lazy.stringValue({ + // TODO: wrap command to capture stdout and stderr + produce: () => this.onDestroy, + }), + }, + }); + } + + this.addOverride("provisioner", overrides); } } diff --git a/test/local-exec.test.ts b/test/local-exec.test.ts index 3fd2127..a949184 100644 --- a/test/local-exec.test.ts +++ b/test/local-exec.test.ts @@ -24,7 +24,13 @@ const apply = (addConstructs: (scope: Construct) => void) => { cwd: outdir, }); - return outdir; + const destroy = () => { + execSync("terraform destroy -auto-approve", { + cwd: outdir, + }); + }; + + return { outdir, destroy }; }; const workingDirectoryForAsset = (manifestPath: string, name: string) => { @@ -61,7 +67,7 @@ describe("LocalExec", () => { test("runs command inside copy of working directory", () => { // Create a file in the stack directory - const outdir = apply((stack) => { + const { outdir } = apply((stack) => { new LocalExec(stack, "myresource", { command: `cp ${__filename} test.txt`, cwd: __dirname, @@ -112,7 +118,7 @@ describe("LocalExec", () => { test("runs command can use token value inside a command", () => { const passwordLength = 4; - const outdir = apply((stack) => { + const { outdir } = apply((stack) => { new provider.RandomProvider(stack, "random"); const waiter = new LocalExec(stack, "timer", { @@ -144,7 +150,7 @@ describe("LocalExec", () => { test("command can be overwritten", () => { // Create a file in the stack directory - const outdir = apply((stack) => { + const { outdir } = apply((stack) => { const exec = new LocalExec(stack, "myresource", { command: "ls -la", cwd: __dirname, @@ -158,4 +164,21 @@ describe("LocalExec", () => { expect(fs.existsSync(path.resolve(workingDir, "test.txt"))).toBe(true); expect(fs.existsSync(path.resolve(__dirname, "test.txt"))).toBe(false); }); + + test("can specify a command to run at destroy-time", () => { + // Create a file in the working directory + const { destroy } = apply((stack) => { + new LocalExec(stack, "test", { + command: "cp origin.txt test.txt", + cwd: testdir, + onDestroy: "rm test.txt", + }); + }); + + expect(fs.existsSync(path.resolve(testdir, "test.txt"))).toBe(true); + + destroy(); + + expect(fs.existsSync(path.resolve(testdir, "test.txt"))).toBe(false); + }); });