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 21 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
6 changes: 5 additions & 1 deletion src/ConditionalApplyPlugin.groovy
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import static TerraformEnvironmentStage.CONFIRM
import static TerraformEnvironmentStage.APPLY
import static TerraformDestroyStage.DESTROY

public class ConditionalApplyPlugin implements TerraformEnvironmentStagePlugin {

private String branch

ConditionalApplyPlugin() {
branch = 'master'
branch = 'destroy_test' // TODO: CHANGE THIS BACK TO MASTER!
}

@Override
public void apply(TerraformEnvironmentStage stage) {
stage.decorateAround(CONFIRM, onlyOnExpectedBranch())
stage.decorateAround(APPLY, onlyOnExpectedBranch())
if (stage instanceof TerraformDestroyStage){
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
14 changes: 10 additions & 4 deletions src/ConfirmApplyPlugin.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import static TerraformEnvironmentStage.CONFIRM
class ConfirmApplyPlugin implements TerraformEnvironmentStagePlugin {

public static enabled = true
public static command_name

ConfirmApplyPlugin() {
}
Expand All @@ -13,19 +14,24 @@ class ConfirmApplyPlugin implements TerraformEnvironmentStagePlugin {

@Override
public void apply(TerraformEnvironmentStage stage) {
command_name = "apply"
if (stage instanceof TerraformDestroyStage){
command_name = "destroy"
}

if (enabled) {
stage.decorate(CONFIRM, addConfirmation())
stage.decorate(CONFIRM, addConfirmation(command_name))
}
kmanning marked this conversation as resolved.
Show resolved Hide resolved
}

public static Closure addConfirmation() {
public static Closure addConfirmation(String command) {
return { closure ->
// ask for human input
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',
message: "Are you absolutely sure the plan above is correct, and should be IMMEDIATELY DEPLOYED via \"terraform ${command}\"?",
ok: "Run terraform ${command.toUpperCase()} now",
submitterParameter: 'approver'
Copy link
Collaborator

@kmanning kmanning Jul 17, 2020

Choose a reason for hiding this comment

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

Ooohh, what you did above is super clever. What if we took it a step further?

class ConfirmApplyPlugin {
   ...
   private  confirmMessage = 'Are you absolutely sure...' <--  the stuff that's currently hard coded.
   ...
        input (
            message: confirmMessage,
            ok: okMessage,
            submitterParameter: submitterParameter
        )
    ....
    public static withConfirmMessage(String newMessage) { this.confirmMessage = newMessage }
    public static withOkMessage(String newMessage) { this.okayMessage = newMessage }
    public static withSubmitterParameter(String newParam) { this.submitterParameter = newParam }
}

Now this has nothing to do with the DestroyPlugin. If we wanted to manipulate the Confirmation message for ANY reason, we could do so.

)
}
Expand Down
12 changes: 11 additions & 1 deletion src/TargetPlugin.groovy
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import static TerraformEnvironmentStage.ALL

class TargetPlugin implements TerraformPlanCommandPlugin, TerraformApplyCommandPlugin, TerraformEnvironmentStagePlugin {
class TargetPlugin implements TerraformPlanCommandPlugin, TerraformApplyCommandPlugin, TerraformDestroyCommandPlugin, TerraformEnvironmentStagePlugin {
public static void init() {
TargetPlugin plugin = new TargetPlugin()

TerraformPlanCommand.addPlugin(plugin)
TerraformApplyCommand.addPlugin(plugin)
TerraformDestroyCommand.addPlugin(plugin)
TerraformEnvironmentStage.addPlugin(plugin)
}

Expand All @@ -27,6 +28,15 @@ class TargetPlugin implements TerraformPlanCommandPlugin, TerraformApplyCommandP
.each { item -> command.withArgument("-target ${item}") }
}

@Override
public void apply(TerraformDestroyCommand command) {
def targets = Jenkinsfile.instance.getEnv().RESOURCE_TARGETS ?: ''
targets.split(',')
.collect { item -> item.trim() }
.findAll { item -> item != '' }
.each { item -> command.withArgument("-target ${item}") }
}

@Override
kmanning marked this conversation as resolved.
Show resolved Hide resolved
public void apply(TerraformEnvironmentStage stage) {
stage.decorate(ALL, addBuildParams())
Expand Down
88 changes: 88 additions & 0 deletions src/TerraformDestroyCommand.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
class TerraformDestroyCommand {
private boolean input = false
kmanning marked this conversation as resolved.
Show resolved Hide resolved
private String terraformBinary = "terraform"
private String command = "destroy"
String environment
private prefixes = []
private suffixes = []
private args = []
private static plugins = []
private appliedPlugins = []
private String directory

public TerraformDestroyCommand(String environment) {
this.environment = environment
}

public TerraformDestroyCommand withInput(boolean input) {
this.input = input
return this
}

public TerraformDestroyCommand withArgument(String arg) {
this.args << arg
return this
}

public TerraformDestroyCommand withPrefix(String prefix) {
prefixes << prefix
return this
}

public TerraformDestroyCommand withSuffix(String suffix) {
suffixes << suffix
return this
}

public TerraformDestroyCommand withDirectory(String directory) {
this.directory = directory
return this
}

public String toString() {
applyPluginsOnce()

def pieces = []
pieces += prefixes
pieces << terraformBinary
pieces << command
if (!input) {
pieces << "-input=false"
}
pieces += args
if (directory) {
pieces << directory
}

pieces += suffixes

return pieces.join(' ')
}

private applyPluginsOnce() {
def remainingPlugins = plugins - appliedPlugins

for (TerraformDestroyCommandPlugin plugin in remainingPlugins) {
plugin.apply(this)
appliedPlugins << plugin
}
}

public static void addPlugin(TerraformDestroyCommandPlugin plugin) {
plugins << plugin
}

public static TerraformDestroyCommand instanceFor(String environment) {
return new TerraformDestroyCommand(environment)
.withInput(false)
.withArgument("-auto-approve")
}

public static getPlugins() {
return plugins
}

public static resetPlugins() {
this.plugins = []
}
}
3 changes: 3 additions & 0 deletions src/TerraformDestroyCommandPlugin.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
interface TerraformDestroyCommandPlugin {
public void apply(TerraformDestroyCommand command)
}
52 changes: 52 additions & 0 deletions src/TerraformDestroyStage.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
class TerraformDestroyStage extends TerraformEnvironmentStage {
private TerraformDestroyCommand destroyCommand
public static final String DESTROY = 'destroy'

TerraformDestroyStage(String environment) {
super(environment)
}

@Override
private Closure pipelineConfiguration() {
initCommand = TerraformInitCommand.instanceFor(environment)
planCommand = TerraformPlanCommand.instanceFor(environment)
planCommand = planCommand.withArgument("-destroy")
destroyCommand = TerraformDestroyCommand.instanceFor(environment)

applyPlugins()

def String environment = this.environment
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
}
}
}
}
}
}
}

}
20 changes: 10 additions & 10 deletions src/TerraformEnvironmentStage.groovy
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
class TerraformEnvironmentStage implements Stage {
private Jenkinsfile jenkinsfile
private String environment
private StageDecorations decorations
private TerraformInitCommand initCommand
private TerraformPlanCommand planCommand
private TerraformApplyCommand applyCommand
private localPlugins
protected Jenkinsfile jenkinsfile
protected String environment
protected StageDecorations decorations
protected TerraformInitCommand initCommand
protected TerraformPlanCommand planCommand
protected TerraformApplyCommand applyCommand
protected localPlugins

private static final DEFAULT_PLUGINS = [ new ConditionalApplyPlugin(), new ConfirmApplyPlugin(), new DefaultEnvironmentPlugin() ]
private static globalPlugins = DEFAULT_PLUGINS.clone()
protected static final DEFAULT_PLUGINS = [ new ConditionalApplyPlugin(), new ConfirmApplyPlugin(), new DefaultEnvironmentPlugin() ]
protected static globalPlugins = DEFAULT_PLUGINS.clone()

kmanning marked this conversation as resolved.
Show resolved Hide resolved
public static final String ALL = 'all'
public static final String PLAN = 'plan'
Expand Down Expand Up @@ -126,7 +126,7 @@ class TerraformEnvironmentStage implements Stage {
return reconcileLocalAndGlobalPlugins()
}

private reconcileLocalAndGlobalPlugins(TerraformEnvironmentStagePlugin newPlugin = null) {
protected reconcileLocalAndGlobalPlugins(TerraformEnvironmentStagePlugin newPlugin = null) {
if (localPlugins == null) {
if (newPlugin == null) {
// No local plugins were added - only global plugins take effect
Expand Down