Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
622724c
initial commit
jleopold28 Aug 31, 2020
c9c9112
change branch for apply
jleopold28 Aug 31, 2020
389e975
testing workspace
jleopold28 Aug 31, 2020
60b50bd
test pwd
jleopold28 Aug 31, 2020
368bb09
testing echo
jleopold28 Aug 31, 2020
a657e42
Testing pwd with appending file name
jleopold28 Aug 31, 2020
460e8a8
fix syntax
jleopold28 Aug 31, 2020
41a5966
adding envi=ronment name
jleopold28 Aug 31, 2020
edfba49
testing archive artifacts
jleopold28 Sep 1, 2020
2d58569
Testing archive
jleopold28 Sep 1, 2020
3f326cc
testing download archive
jleopold28 Sep 1, 2020
088f269
fix import
jleopold28 Sep 1, 2020
f17714b
testing with jobName
jleopold28 Sep 1, 2020
bae17af
eccho vars
jleopold28 Sep 1, 2020
203bdc4
testing splitting job name
jleopold28 Sep 1, 2020
aa84e0e
testing new split method
jleopold28 Sep 1, 2020
4c78ff1
fix loop typo
jleopold28 Sep 1, 2020
72581b3
adding new getArtifactUrl method
jleopold28 Sep 1, 2020
bf31317
cleanup
jleopold28 Sep 1, 2020
bc95cc7
adding tests
jleopold28 Sep 1, 2020
8e44fce
fix style
jleopold28 Sep 1, 2020
6b6a3b8
remove plan and apply plugin additions
jleopold28 Sep 1, 2020
0b1bf1b
update tests
jleopold28 Sep 1, 2020
6999cf9
Adding tests for url
jleopold28 Sep 1, 2020
0de7b3a
specify directoy instead of argument
jleopold28 Sep 1, 2020
c5e91f9
revert changes to conditional apply
jleopold28 Sep 1, 2020
afdc7a0
Test PR 7
jleopold28 Sep 1, 2020
b3faf85
switch branch to master
jleopold28 Sep 1, 2020
064b63f
Testing stash
jleopold28 Sep 1, 2020
4bb35c2
simplify
jleopold28 Sep 1, 2020
ae9dc44
stash based on filename
jleopold28 Sep 1, 2020
07ec943
remove tests for url generation
jleopold28 Sep 1, 2020
4b3575d
update tests
jleopold28 Sep 2, 2020
4ad3227
fix merge conflicts
jleopold28 Sep 2, 2020
f32e800
update docs
jleopold28 Sep 2, 2020
06501bf
update tests to verify closure
jleopold28 Sep 2, 2020
4e80af6
fix naming
jleopold28 Sep 2, 2020
712ab07
adding delegate
jleopold28 Sep 2, 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Unreleased

* [Issue #293](https://github.com/manheim/terraform-pipeline/issues/293) withEnv & withGlobalEnv docs
* [Issue #175](https://github.com/manheim/terraform-pipeline/issues/175) Pass terraform plan output to apply

# v5.10

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
* [CrqPlugin](./docs/CrqPlugin.md): Use the manheim_remedier gem to open automated Change Requests.
* [DestroyPlugin](./docs/DestroyPlugin.md): Use this to change the pipeline functionality to `terraform destroy`. (Requires manual confirmation)
* [GithubPRPlanPlugin](./docs/GithubPRPlanPlugin.md): Use this to post Terraform plan results in the comments of a Github PullRequest.
* [PassPlanFilePlugin](./docs/PassPlanFilePlugin.md): Pass the plan file into apply stage
* [PlanOnlyPlugin](./docs/PlanOnlyPlugin.md): Use this to change the pipeline functionality to `terraform plan` only.
* [TargetPlugin](./docs/TargetPlugin.md): set `-target` parameter for terraform plan and apply.
* [TerraformDirectoryPlugin](./docs/TerraformDirectoryPlugin.md): Change the default directory containing your terraform code.
Expand Down
28 changes: 28 additions & 0 deletions docs/PassPlanFilePlugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
## [PassPlanFilePlugin](../src/PassPlanFilePlugin.groovy)

Enable this plugin to pass the plan file output to `terraform apply`.

This plugin stashes the plan file during the `plan` step.
When `apply` is called, the plan file is unstashed and passed as an argument.


```
// Jenkinsfile
@Library(['terraform-pipeline@v3.10']) _

Jenkinsfile.init(this, env)

// Pass the plan file to 'terraform apply'
PassPlanFilePlugin.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()
```
50 changes: 50 additions & 0 deletions src/PassPlanFilePlugin.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import static TerraformEnvironmentStage.PLAN
import static TerraformEnvironmentStage.APPLY

class PassPlanFilePlugin implements TerraformPlanCommandPlugin, TerraformApplyCommandPlugin, TerraformEnvironmentStagePlugin {

public static void init() {
PassPlanFilePlugin plugin = new PassPlanFilePlugin()

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

@Override
public void apply(TerraformEnvironmentStage stage) {
stage.decorate(PLAN, stashPlan(stage.getEnvironment()))
stage.decorate(APPLY, unstashPlan(stage.getEnvironment()))
}

@Override
public void apply(TerraformPlanCommand command) {
String env = command.getEnvironment()
command.withArgument("-out=tfplan-" + env)
}

@Override
public void apply(TerraformApplyCommand command) {
String env = command.getEnvironment()
command.withDirectory("tfplan-" + env)
}

public Closure stashPlan(String env) {
return { closure ->
closure()
String planFile = "tfplan-" + env
echo "Stashing ${planFile} file"
stash name: planFile, includes: planFile
}
}

public Closure unstashPlan(String env) {
return { closure ->
String planFile = "tfplan-" + env
echo "Unstashing ${planFile} file"
unstash planFile
closure()
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nice. The change to use stash/unstash looks great =).

}

}
6 changes: 6 additions & 0 deletions test/DummyJenkinsfile.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,10 @@ class DummyJenkinsfile {
closure.delegate = this
closure()
}
public stash(args) {
println "DummyJenkinsfile.stash(${args})"
}
public unstash(args) {
println "DummyJenkinsfile.unstash(${args})"
}
}
134 changes: 134 additions & 0 deletions test/PassPlanFilePluginTest.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import static org.hamcrest.Matchers.containsString
import static org.hamcrest.Matchers.hasItem
import static org.hamcrest.Matchers.instanceOf
import static org.junit.Assert.assertThat
import static org.junit.Assert.assertTrue
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.mock;
import org.junit.Test
import org.junit.Before
import org.junit.After
import org.junit.runner.RunWith
import de.bechte.junit.runners.context.HierarchicalContextRunner

@RunWith(HierarchicalContextRunner.class)
class PassPlanFilePluginTest {
@Before
void resetJenkinsEnv() {
Jenkinsfile.instance = mock(Jenkinsfile.class)
when(Jenkinsfile.instance.getEnv()).thenReturn([:])
}

private configureJenkins(Map config = [:]) {
Jenkinsfile.instance = mock(Jenkinsfile.class)
when(Jenkinsfile.instance.getEnv()).thenReturn(config.env ?: [:])
}

public class Init {
@After
void resetPlugins() {
TerraformPlanCommand.resetPlugins()
TerraformApplyCommand.resetPlugins()
TerraformEnvironmentStage.reset()
}

@Test
void modifiesTerraformEnvironmentStageCommand() {
PassPlanFilePlugin.init()

Collection actualPlugins = TerraformEnvironmentStage.getPlugins()
assertThat(actualPlugins, hasItem(instanceOf(PassPlanFilePlugin.class)))
}

@Test
void modifiesTerraformPlanCommand() {
PassPlanFilePlugin.init()

Collection actualPlugins = TerraformPlanCommand.getPlugins()
assertThat(actualPlugins, hasItem(instanceOf(PassPlanFilePlugin.class)))
}

@Test
void modifiesTerraformApplyCommand() {
PassPlanFilePlugin.init()

Collection actualPlugins = TerraformApplyCommand.getPlugins()
assertThat(actualPlugins, hasItem(instanceOf(PassPlanFilePlugin.class)))
}

}

public class Apply {

@Test
void decoratesTheTerraformEnvironmentStage() {
PassPlanFilePlugin plugin = new PassPlanFilePlugin()
def environment = spy(new TerraformEnvironmentStage())
plugin.apply(environment)

verify(environment, times(1)).decorate(eq(TerraformEnvironmentStage.PLAN), any(Closure.class))
verify(environment, times(1)).decorate(eq(TerraformEnvironmentStage.APPLY), any(Closure.class))
}

@Test
void addsArgumentToTerraformPlan() {
PassPlanFilePlugin plugin = new PassPlanFilePlugin()
TerraformPlanCommand command = new TerraformPlanCommand("dev")
plugin.apply(command)

String result = command.toString()
assertThat(result, containsString("-out=tfplan-dev"))
}

@Test
void addsArgumentToTerraformApply() {
PassPlanFilePlugin plugin = new PassPlanFilePlugin()
TerraformApplyCommand command = new TerraformApplyCommand("dev")
plugin.apply(command)

String result = command.toString()
assertThat(result, containsString("tfplan-dev"))
}

}

public class StashPlan {

@Test
void runsStashPlan() {
def wasCalled = false
def passedClosure = { -> wasCalled = true }
def plugin = new PassPlanFilePlugin()

def stashClosure = plugin.stashPlan('dev')
stashClosure.delegate = new DummyJenkinsfile()
stashClosure.call(passedClosure)

assertTrue(wasCalled)
}

}

public class UnstashPlan {

@Test
void runsUnstashPlan() {
def wasCalled = false
def passedClosure = { -> wasCalled = true }
def plugin = new PassPlanFilePlugin()

def unstashClosure = plugin.unstashPlan('dev')
unstashClosure.delegate = new DummyJenkinsfile()
unstashClosure.call(passedClosure)

assertTrue(wasCalled)
}

}

}