Skip to content
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

Merged
merged 106 commits into from
Jul 24, 2020
Merged
Show file tree
Hide file tree
Changes from 105 commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
4bad4b6
Merge pull request #1 from manheim/master
jleopold28 Jul 16, 2020
b956a3a
adding destroy command
jleopold28 Jul 16, 2020
dacd71c
typo fix
jleopold28 Jul 16, 2020
e95c8a5
remove constructor
jleopold28 Jul 16, 2020
3dcdc03
super constructor
jleopold28 Jul 16, 2020
7316ae1
remove comments
jleopold28 Jul 16, 2020
b161c37
super constructor
jleopold28 Jul 16, 2020
9115fc9
change private to protected
jleopold28 Jul 16, 2020
dbde8cd
adding localPlugins var
jleopold28 Jul 16, 2020
52d63c2
testing protected
jleopold28 Jul 16, 2020
192189b
add destroyCommand back
jleopold28 Jul 16, 2020
1929c58
remove extra init
jleopold28 Jul 16, 2020
95e07ad
add conditional around destroy
jleopold28 Jul 16, 2020
3c9f4fd
revert overloading method
jleopold28 Jul 16, 2020
07c866c
fix typo
jleopold28 Jul 16, 2020
c0f9509
change branch to destroy_test
jleopold28 Jul 16, 2020
398359c
ConfirmApply updates
jleopold28 Jul 16, 2020
c2635f5
fix typo
jleopold28 Jul 16, 2020
0948d99
define command_name
jleopold28 Jul 16, 2020
9731413
ading destroy support for target plugin
jleopold28 Jul 16, 2020
b339e68
typo
jleopold28 Jul 16, 2020
332bb40
adding initial docs
jleopold28 Jul 17, 2020
2b5cfaf
refactor
jleopold28 Jul 17, 2020
2ebadf3
adding newlines, refactoring
jleopold28 Jul 17, 2020
9c70aca
refactoring of confirm plugin
jleopold28 Jul 17, 2020
6edf162
clean up
jleopold28 Jul 17, 2020
8e5139d
add parens
jleopold28 Jul 17, 2020
0bba5b9
add void
jleopold28 Jul 17, 2020
6c7e323
Add constructor
jleopold28 Jul 17, 2020
d95e32a
adding constructors
jleopold28 Jul 17, 2020
968df59
adding constructor for tf destroy command
jleopold28 Jul 17, 2020
799d347
refactor
jleopold28 Jul 17, 2020
216f212
remove extends
jleopold28 Jul 17, 2020
5e9541a
Revert docs
jleopold28 Jul 17, 2020
15f581a
remove extension for destroy command
jleopold28 Jul 17, 2020
973c96c
add init() method
jleopold28 Jul 17, 2020
f7cf74e
Add jenkinsfile ver
jleopold28 Jul 17, 2020
5c6962f
define vars
jleopold28 Jul 17, 2020
1c95134
Add decorations
jleopold28 Jul 17, 2020
6e722d1
import hooks
jleopold28 Jul 17, 2020
78f184f
change init method
jleopold28 Jul 17, 2020
4cf2653
refactor getStrategyName
jleopold28 Jul 17, 2020
eb224ce
static strat
jleopold28 Jul 17, 2020
e3206f7
change destroycommand to extend from apply command
jleopold28 Jul 17, 2020
ee06537
testing destroy with extends
jleopold28 Jul 17, 2020
29d1fc9
changing constructor to use super
jleopold28 Jul 17, 2020
3bef392
change to protected
jleopold28 Jul 17, 2020
130b295
change to protected for method
jleopold28 Jul 17, 2020
9a94da0
change all to protected
jleopold28 Jul 17, 2020
709000e
remove unneeded file
jleopold28 Jul 17, 2020
a132306
testing changes to destroyPlugin
jleopold28 Jul 17, 2020
fc69c72
revert last changes
jleopold28 Jul 17, 2020
5c86117
testing set_command
jleopold28 Jul 17, 2020
ad679a9
fix typo
jleopold28 Jul 17, 2020
31dab58
Testing withCommand
jleopold28 Jul 17, 2020
7e4041e
remove static
jleopold28 Jul 17, 2020
39c1c90
make it all static=
jleopold28 Jul 17, 2020
afbe57f
cleanup
jleopold28 Jul 17, 2020
1c20dba
cleanup unused method
jleopold28 Jul 17, 2020
3af1e69
remove whitespace:
jleopold28 Jul 17, 2020
1528452
refactoring confirmapply logic
jleopold28 Jul 20, 2020
57e2130
change name of method
jleopold28 Jul 20, 2020
f278ed2
double quotes
jleopold28 Jul 20, 2020
89d7702
testing
jleopold28 Jul 20, 2020
4eaf1c9
fix typo
jleopold28 Jul 20, 2020
20ff00e
adding back addPlugin
jleopold28 Jul 20, 2020
0f9d949
change back to private
jleopold28 Jul 20, 2020
6d42814
remove static
jleopold28 Jul 20, 2020
ab41d93
return object
jleopold28 Jul 20, 2020
d97f28f
change wording
jleopold28 Jul 20, 2020
9cecca4
fix style
jleopold28 Jul 20, 2020
c4026a4
adding docs
jleopold28 Jul 20, 2020
13fac41
refactor, remove hook for destroy
jleopold28 Jul 20, 2020
9a83f37
revert prev changes
jleopold28 Jul 20, 2020
0cad059
import
jleopold28 Jul 20, 2020
457c10e
test import again
jleopold28 Jul 20, 2020
1a79575
testing decorate destroy stage
jleopold28 Jul 20, 2020
adb779b
remove unused import
jleopold28 Jul 20, 2020
a837cdf
set destroy_test
jleopold28 Jul 20, 2020
54ef87f
adding images
jleopold28 Jul 20, 2020
b08a234
fix ref
jleopold28 Jul 20, 2020
58224d7
adding support for deploy and destroy
jleopold28 Jul 20, 2020
c56c534
add import
jleopold28 Jul 20, 2020
a2dd1d4
remove dploy and destroy
jleopold28 Jul 20, 2020
b70e697
update readme
jleopold28 Jul 20, 2020
ca392ba
update docs
jleopold28 Jul 20, 2020
bc7e5cd
update docs, add images
jleopold28 Jul 20, 2020
955d198
fix filename
jleopold28 Jul 20, 2020
d37bf3d
change back to master
jleopold28 Jul 20, 2020
d3a537e
initial testing
jleopold28 Jul 20, 2020
ce6e4dc
update tests
jleopold28 Jul 20, 2020
018442e
update tests
jleopold28 Jul 20, 2020
4f66a7f
remove old import
jleopold28 Jul 20, 2020
013c886
change branch for tests
jleopold28 Jul 22, 2020
d176864
set to public
jleopold28 Jul 22, 2020
61360f7
switch branch
jleopold28 Jul 22, 2020
25e6e0e
testing remove get methods
jleopold28 Jul 22, 2020
9870f1a
fix tests, remove getMethods
jleopold28 Jul 22, 2020
c21259a
remove docs about destroy after deploy
jleopold28 Jul 24, 2020
7dfed77
change branch for testing
jleopold28 Jul 24, 2020
c7784ad
change back to masterg
jleopold28 Jul 24, 2020
59b6322
adding withArg support
jleopold28 Jul 24, 2020
a40b543
change apply branch
jleopold28 Jul 24, 2020
d41e27d
add arg to destroy command, not plan
jleopold28 Jul 24, 2020
c6a8f38
change branch to master
jleopold28 Jul 24, 2020
2866a8e
Adding DESTROY stage decorations for existing plugins
jleopold28 Jul 24, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Unreleased

* [Issue #88](https://github.com/manheim/terraform-pipeline/issues/88) Add an optional TerraformDestroyStage
* [Issue #24](https://github.com/manheim/terraform-pipeline/issues/24) ConfirmApplyPlugin - allow customization

# v5.8

* Docs
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ The example above gives you a bare-bones pipeline, and there may be Jenkinsfile
* [TargetPlugin](./docs/TargetPlugin.md): set `-target` parameter for terraform plan and apply.
* [TerraformDirectoryPlugin](./docs/TerraformDirectoryPlugin.md): Change the default directory containing your terraform code.
* [TerraformLandscapePlugin](./docs/TerraformLandscapePlugin.md): Enable terraform-landscape plan output.
* [DestroyPlugin](./docs/DestroyPlugin.md): Use this to change the pipeline functionality to `terraform destroy`. (Requires manual confirmation)

## Write your own Plugin

Expand Down
50 changes: 50 additions & 0 deletions docs/DestroyPlugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
## [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(['terraform-pipeline@v3.10']) _

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)

Copy link
Collaborator

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.

## Adding arguments to the destroy command

You can use `withArgument("-some-arg")` to add arguments to the `terraform destroy` command.
```
// Jenkinsfile
@Library(['terraform-pipeline@v3.10']) _

Jenkinsfile.init(this, env)

// This enables the destroy functionality
// Set refresh to false for destroy command
DestroyPlugin.withArgument("-refresh=false").init()

...
```
Binary file added images/destroy-pipeline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/ConditionalApplyPlugin.groovy
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import static TerraformEnvironmentStage.CONFIRM
import static TerraformEnvironmentStage.APPLY
import static TerraformEnvironmentStage.DESTROY

public class ConditionalApplyPlugin implements TerraformEnvironmentStagePlugin {

Expand All @@ -13,6 +14,7 @@ public class ConditionalApplyPlugin implements TerraformEnvironmentStagePlugin {
public void apply(TerraformEnvironmentStage stage) {
stage.decorateAround(CONFIRM, onlyOnExpectedBranch())
stage.decorateAround(APPLY, onlyOnExpectedBranch())
stage.decorateAround(DESTROY, onlyOnExpectedBranch())
}
kmanning marked this conversation as resolved.
Show resolved Hide resolved
kmanning marked this conversation as resolved.
Show resolved Hide resolved

public Closure onlyOnExpectedBranch() {
Expand Down
21 changes: 18 additions & 3 deletions src/ConfirmApplyPlugin.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import static TerraformEnvironmentStage.CONFIRM
class ConfirmApplyPlugin implements TerraformEnvironmentStagePlugin {

public static enabled = true
public static String confirmMessage = 'Are you absolutely sure the plan above is correct, and should be IMMEDIATELY DEPLOYED via "terraform apply"?'
public static String okMessage = 'Run terraform apply now'
public static String submitter = 'approver'

ConfirmApplyPlugin() {
}
Expand All @@ -24,9 +27,9 @@ class ConfirmApplyPlugin implements TerraformEnvironmentStagePlugin {
try {
timeout(time: 15, unit: 'MINUTES') {
input(
message: 'Are you absolutely sure the plan above is correct, and should be IMMEDIATELY DEPLOYED via "terraform apply"?',
ok: 'Run terraform APPLY now',
submitterParameter: 'approver'
message: confirmMessage,
ok: okMessage,
submitterParameter: submitter
)
}
} catch (ex) {
Expand All @@ -36,6 +39,18 @@ class ConfirmApplyPlugin implements TerraformEnvironmentStagePlugin {
}
}

public static void withConfirmMessage(String newMessage) {
this.confirmMessage = newMessage
}

public static void withOkMessage(String newMessage) {
this.okMessage = newMessage
}

public static void withSubmitterParameter(String newParam) {
this.submitterParameter = newParam
}

public static disable() {
this.enabled = false
return this
Expand Down
53 changes: 53 additions & 0 deletions src/DefaultStrategy.groovy
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
24 changes: 24 additions & 0 deletions src/DestroyPlugin.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class DestroyPlugin implements TerraformEnvironmentStagePlugin {

private static arguments = []

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)
}

public static withArgument(String arg) {
arguments << arg
return this
}

@Override
public void apply(TerraformEnvironmentStage stage) {
stage.withStrategy(new DestroyStrategy(arguments))
}

}
kmanning marked this conversation as resolved.
Show resolved Hide resolved
64 changes: 64 additions & 0 deletions src/DestroyStrategy.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
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
private List extraArguments

DestroyStrategy(List args) {
this.extraArguments = args
}

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")
for (arg in extraArguments) {
destroyCommand = destroyCommand.withArgument(arg)
}

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
5 changes: 5 additions & 0 deletions src/TerraformApplyCommand.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ class TerraformApplyCommand {
this.environment = environment
}

public TerraformApplyCommand withCommand(String newCommand) {
this.command = newCommand
return this
}

public TerraformApplyCommand withInput(boolean input) {
this.input = input
return this
Expand Down
48 changes: 7 additions & 41 deletions src/TerraformEnvironmentStage.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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'
Copy link
Collaborator

@kmanning kmanning Jul 24, 2020

Choose a reason for hiding this comment

The 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:

  • public void apply(TerraformEnvironmentStage stage) {
    def environment = stage.getEnvironment()
    def parameterStorePath = pathForEnvironment(environment)
    stage.decorate(PLAN, addEnvVariables(parameterStorePath))
    stage.decorate(APPLY, addEnvVariables(parameterStorePath))
    }
  • public void apply(TerraformEnvironmentStage stage) {
    stage.decorate(PLAN, addColor())
    stage.decorate(APPLY, addColor())
    }
  • @Override
    public void apply(TerraformEnvironmentStage stage) {
    def environment = stage.getEnvironment()
    def parameterStorePath = pathForEnvironment(environment)
    def options = [
    path: parameterStorePath,
    credentialsId: "${environment.toUpperCase()}_PARAMETER_STORE_ACCESS"
    ]
    stage.decorate(PLAN, addParameterStoreBuildWrapper(options))
    stage.decorate(APPLY, addParameterStoreBuildWrapper(options))
    }
  • @Override
    public void apply(TerraformEnvironmentStage stage) {
    def environment = stage.getEnvironment()
    stage.decorate(TerraformEnvironmentStage.APPLY, addCrq(environment))
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kmanning I am making some changes now. I will have to add stage.decorate(DESTROY, someMethod(options)) to the above plugins. This will add functionality if DESTROY is enabled. Otherwise this will not change the existing functionality.


TerraformEnvironmentStage(String environment) {
this.environment = environment
Expand Down Expand Up @@ -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) {
Expand Down
Loading