-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding support for Terraform Destroy. Adding ConfirmApply customization #251
Changes from 98 commits
4bad4b6
b956a3a
dacd71c
e95c8a5
3dcdc03
7316ae1
b161c37
9115fc9
dbde8cd
52d63c2
192189b
1929c58
95e07ad
3c9f4fd
07c866c
c0f9509
398359c
c2635f5
0948d99
9731413
b339e68
332bb40
2b5cfaf
2ebadf3
9c70aca
6edf162
8e5139d
0bba5b9
6c7e323
d95e32a
968df59
799d347
216f212
5e9541a
15f581a
973c96c
f7cf74e
5c6962f
1c95134
6e722d1
78f184f
4cf2653
eb224ce
e3206f7
ee06537
29d1fc9
3bef392
130b295
9a94da0
709000e
a132306
fc69c72
5c86117
ad679a9
31dab58
7e4041e
39c1c90
afbe57f
1c20dba
3af1e69
1528452
57e2130
f278ed2
89d7702
4eaf1c9
20ff00e
0f9d949
6d42814
ab41d93
d97f28f
9cecca4
c4026a4
13fac41
9a83f37
0cad059
457c10e
1a79575
adb779b
a837cdf
54ef87f
b08a234
58224d7
c56c534
a2dd1d4
b70e697
ca392ba
bc7e5cd
955d198
d37bf3d
d3a537e
ce6e4dc
018442e
4f66a7f
013c886
d176864
61360f7
25e6e0e
9870f1a
c21259a
7dfed77
c7784ad
59b6322
a40b543
d41e27d
c6a8f38
2866a8e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
## [DestroyPlugin](../src/DestroyPlugin.groovy) | ||
|
||
Enable this plugin to use `terraform destroy` to destroy your environment(s). | ||
|
||
When this plugin is enabled, the pipeline will follow these steps: | ||
1. Run a `terraform plan -destroy` to display which resources will get destroyed. | ||
2. Ask for human confirmation to proceed with the destroy. | ||
3. Run the `terraform destroy` command. | ||
|
||
|
||
``` | ||
// Jenkinsfile | ||
@Library(['[email protected]']) _ | ||
|
||
Jenkinsfile.init(this, env) | ||
|
||
// This enables the destroy functionality | ||
DestroyPlugin.init() | ||
|
||
def validate = new TerraformValidateStage() | ||
|
||
def destroyQa = new TerraformEnvironmentStage('qa') | ||
def destroyUat = new TerraformEnvironmentStage('uat') | ||
def destroyProd = new TerraformEnvironmentStage('prod') | ||
|
||
validate.then(destroyQa) | ||
.then(destroyUat) | ||
.then(destroyProd) | ||
.build() | ||
``` | ||
|
||
When using this plugin, your pipeline will look something like this: | ||
|
||
![DestroyPluginPipeline](../images/destroy-pipeline.png) | ||
|
||
--------- | ||
|
||
## How to destroy environments after deployment | ||
|
||
If you wish to run a traditional deployment and then run `terraform destroy`, you can enable the DestroyPlugin after the deployment. | ||
|
||
|
||
``` | ||
def validate = new TerraformValidateStage() | ||
|
||
def deployQa = new TerraformEnvironmentStage('qa') | ||
def deployUat = new TerraformEnvironmentStage('uat') | ||
def deployProd = new TerraformEnvironmentStage('prod') | ||
|
||
// First we deploy our environments | ||
validate.then(deployQa) | ||
.then(deployUat) | ||
.then(deployProd) | ||
.build() | ||
|
||
|
||
// Now enable the destroy functionality | ||
DestroyPlugin.init() | ||
|
||
def destroyQa = new TerraformEnvironmentStage('qa') | ||
def destroyUat = new TerraformEnvironmentStage('uat') | ||
def destroyProd = new TerraformEnvironmentStage('prod') | ||
|
||
// Destroy the environments | ||
validate.then(destroyQa) | ||
.then(destroyUat) | ||
.then(destroyProd) | ||
.build() | ||
``` | ||
|
||
kmanning marked this conversation as resolved.
Show resolved
Hide resolved
|
||
With this approach, the entire pipeline will look like so: | ||
|
||
![DeployAndDestroyPipeline](../images/deploy-and-destroy.png) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import static TerraformEnvironmentStage.ALL | ||
import static TerraformEnvironmentStage.PLAN | ||
import static TerraformEnvironmentStage.CONFIRM | ||
import static TerraformEnvironmentStage.APPLY | ||
|
||
class DefaultStrategy { | ||
|
||
private TerraformInitCommand initCommand | ||
private TerraformPlanCommand planCommand | ||
private TerraformApplyCommand applyCommand | ||
private Jenkinsfile jenkinsfile | ||
|
||
public Closure createPipelineClosure(String environment, StageDecorations decorations) { | ||
initCommand = TerraformInitCommand.instanceFor(environment) | ||
planCommand = TerraformPlanCommand.instanceFor(environment) | ||
applyCommand = TerraformApplyCommand.instanceFor(environment) | ||
|
||
jenkinsfile = Jenkinsfile.instance | ||
|
||
return { -> | ||
node(jenkinsfile.getNodeName()) { | ||
deleteDir() | ||
checkout(scm) | ||
|
||
decorations.apply(ALL) { | ||
stage("${PLAN}-${environment}") { | ||
decorations.apply(PLAN) { | ||
sh initCommand.toString() | ||
sh planCommand.toString() | ||
} | ||
} | ||
|
||
decorations.apply("Around-${CONFIRM}") { | ||
stage("${CONFIRM}-${environment}") { | ||
decorations.apply(CONFIRM) { | ||
echo "Approved" | ||
} | ||
} | ||
} | ||
|
||
decorations.apply("Around-${APPLY}") { | ||
stage("${APPLY}-${environment}") { | ||
decorations.apply(APPLY) { | ||
sh initCommand.toString() | ||
sh applyCommand.toString() | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
kmanning marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
class DestroyPlugin implements TerraformEnvironmentStagePlugin { | ||
|
||
public static void init() { | ||
DestroyPlugin plugin = new DestroyPlugin() | ||
|
||
ConfirmApplyPlugin.withConfirmMessage('WARNING! Are you absolutely sure the plan above is correct? Your environment will be IMMEDIATELY DESTROYED via "terraform destroy"') | ||
ConfirmApplyPlugin.withOkMessage("Run terraform DESTROY now") | ||
|
||
TerraformEnvironmentStage.addPlugin(plugin) | ||
} | ||
|
||
@Override | ||
public void apply(TerraformEnvironmentStage stage) { | ||
stage.withStrategy(new DestroyStrategy()) | ||
} | ||
|
||
} | ||
kmanning marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import static TerraformEnvironmentStage.ALL | ||
import static TerraformEnvironmentStage.PLAN | ||
import static TerraformEnvironmentStage.CONFIRM | ||
import static TerraformEnvironmentStage.DESTROY | ||
|
||
class DestroyStrategy { | ||
|
||
private TerraformInitCommand initCommand | ||
private TerraformPlanCommand planCommand | ||
private TerraformApplyCommand destroyCommand | ||
private Jenkinsfile jenkinsfile | ||
|
||
public Closure createPipelineClosure(String environment, StageDecorations decorations) { | ||
initCommand = TerraformInitCommand.instanceFor(environment) | ||
planCommand = TerraformPlanCommand.instanceFor(environment) | ||
planCommand = planCommand.withArgument("-destroy") | ||
destroyCommand = TerraformApplyCommand.instanceFor(environment) | ||
destroyCommand = destroyCommand.withCommand("destroy") | ||
jenkinsfile = Jenkinsfile.instance | ||
|
||
return { -> | ||
node(jenkinsfile.getNodeName()) { | ||
deleteDir() | ||
checkout(scm) | ||
|
||
decorations.apply(ALL) { | ||
stage("${PLAN}-${DESTROY}-${environment}") { | ||
decorations.apply(PLAN) { | ||
sh initCommand.toString() | ||
sh planCommand.toString() | ||
} | ||
} | ||
|
||
decorations.apply("Around-${CONFIRM}") { | ||
stage("${CONFIRM}-${DESTROY}-${environment}") { | ||
decorations.apply(CONFIRM) { | ||
echo "Approved" | ||
} | ||
} | ||
} | ||
|
||
decorations.apply("Around-${DESTROY}") { | ||
stage("${DESTROY}-${environment}") { | ||
decorations.apply(DESTROY) { | ||
sh destroyCommand.toString() | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
kmanning marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -2,10 +2,8 @@ class TerraformEnvironmentStage implements Stage { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private Jenkinsfile jenkinsfile | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private String environment | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private StageDecorations decorations | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private TerraformInitCommand initCommand | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private TerraformPlanCommand planCommand | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private TerraformApplyCommand applyCommand | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private localPlugins | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private static strategy = new DefaultStrategy() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private static final DEFAULT_PLUGINS = [ new ConditionalApplyPlugin(), new ConfirmApplyPlugin(), new DefaultEnvironmentPlugin() ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private static globalPlugins = DEFAULT_PLUGINS.clone() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -14,6 +12,7 @@ class TerraformEnvironmentStage implements Stage { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
public static final String PLAN = 'plan' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
public static final String CONFIRM = 'confirm' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
public static final String APPLY = 'apply' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
public static final String DESTROY = 'destroy' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This new state of TerraformEnvironmentStage.DESTROY. How will this behavior with the existing plugins that are part of the library (or worse, plugins that are in completely external libraries?) eg:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kmanning I am making some changes now. I will have to add |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TerraformEnvironmentStage(String environment) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.environment = environment | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -46,46 +45,13 @@ class TerraformEnvironmentStage implements Stage { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Jenkinsfile.build(pipelineConfiguration()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private Closure pipelineConfiguration() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
initCommand = TerraformInitCommand.instanceFor(environment) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
planCommand = TerraformPlanCommand.instanceFor(environment) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
applyCommand = TerraformApplyCommand.instanceFor(environment) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
public void withStrategy(newStrategy) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.strategy = newStrategy | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private Closure pipelineConfiguration() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
applyPlugins() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def String environment = this.environment | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return { -> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
node(jenkinsfile.getNodeName()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
deleteDir() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
checkout(scm) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
decorations.apply(ALL) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
stage("${PLAN}-${environment}") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
decorations.apply(PLAN) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sh initCommand.toString() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sh planCommand.toString() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
decorations.apply("Around-${CONFIRM}") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
stage("${CONFIRM}-${environment}") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
decorations.apply(CONFIRM) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
echo "Approved" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
decorations.apply("Around-${APPLY}") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
stage("${APPLY}-${environment}") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
decorations.apply(APPLY) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sh initCommand.toString() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
sh applyCommand.toString() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return strategy.createPipelineClosure(environment, decorations) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
kmanning marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
public void decorate(Closure decoration) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice touch on the screenshot - thank you for that.