Skip to content

Commit

Permalink
Merge pull request #229 from jleopold28/support_target
Browse files Browse the repository at this point in the history
Support target for plan/apply
  • Loading branch information
kmanning committed Jun 16, 2020
2 parents 0b67157 + c76898d commit 4cf4512
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Unpublished changes

* [Issue #193](https://github.com/manheim/terraform-pipeline/issues/193) Support passing branch plans to Github PRs
* [Issue #102](https://github.com/manheim/terraform-pipeline/issues/102) Support target when running terraform plan and apply

# v5.7

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

## Write your own Plugin

Expand Down
27 changes: 27 additions & 0 deletions docs/TargetPlugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## [TargetPlugin](../src/TargetPlugin.groovy)

Enable this plugin to run plan/apply on selective resource targets

```
// Jenkinsfile
@Library(['terraform-pipeline']) _
Jenkinsfile.init(this, env)
TargetPlugin.init() // Optionally limit plan/apply to specific targets
def validate = new TerraformValidateStage()
def deployQa = new TerraformEnvironmentStage('qa')
def deployUat = new TerraformEnvironmentStage('uat')
def deployProd = new TerraformEnvironmentStage('prod')
// Pipeline can now be built with "Build with Parameters"
// New 'target' option in parameter list can limit plan/apply to specific targets
// By default, builds should skip the 'target' option, and build as-normal
validate.then(deployQa)
.then(deployUat)
.then(deployProd)
.build()
```
43 changes: 43 additions & 0 deletions src/TargetPlugin.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import static TerraformEnvironmentStage.ALL

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

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

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

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

@Override
public void apply(TerraformEnvironmentStage stage) {
stage.decorate(ALL, addBuildParams())
}

public static Closure addBuildParams() {
return { closure ->
def params = [
string(name: 'RESOURCE_TARGETS', defaultValue: '', description: 'comma-separated list of resource addresses to pass to plan and apply "-target=" parameters'),
]
def props = [
parameters(params)
]
properties(props)

closure()
}
}

}
109 changes: 109 additions & 0 deletions test/TargetPluginTest.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
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.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 TerraformEnvironmentStage.ALL;
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 TargetPluginTest {
@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.resetPlugins()
}

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

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

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

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

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

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

public class Apply {

@Test
void addsTargetArgumentToTerraformPlan() {
TargetPlugin plugin = new TargetPlugin()
TerraformPlanCommand command = new TerraformPlanCommand()
configureJenkins(env: [
'RESOURCE_TARGETS': 'aws_dynamodb_table.test-table-2,aws_dynamodb_table.test-table-3'
])

plugin.apply(command)

String result = command.toString()
assertThat(result, containsString(" -target aws_dynamodb_table.test-table-2 -target aws_dynamodb_table.test-table-3"))
}

@Test
void addsTargetArgumentToTerraformApply() {
TargetPlugin plugin = new TargetPlugin()
TerraformApplyCommand command = new TerraformApplyCommand()
configureJenkins(env: [
'RESOURCE_TARGETS': 'aws_dynamodb_table.test-table-2,aws_dynamodb_table.test-table-3'
])

plugin.apply(command)

String result = command.toString()
assertThat(result, containsString(" -target aws_dynamodb_table.test-table-2 -target aws_dynamodb_table.test-table-3"))
}

@Test
void decoratesTheTerraformEnvironmentStage() {
TargetPlugin plugin = new TargetPlugin()
def environment = spy(new TerraformEnvironmentStage())
configureJenkins(env: [
'RESOURCE_TARGETS': 'aws_dynamodb_table.test-table-2,aws_dynamodb_table.test-table-3'
])

plugin.apply(environment)

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

0 comments on commit 4cf4512

Please sign in to comment.