Skip to content

Commit

Permalink
Merge pull request #216 from manheim/v5.6-dev
Browse files Browse the repository at this point in the history
Create 5.6 release
  • Loading branch information
kmanning committed Apr 9, 2020
2 parents 8358b57 + 4e96477 commit 817be51
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 9 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Unreleased

# v5.6

* [Issue #203](https://github.com/manheim/terraform-pipeline/issues/201) ParameterStoreBuildWrapperPlugin - allow path to be customized
* [Issue #192](https://github.com/manheim/terraform-pipeline/issues/192) Apply CredentialsPlugins to all Stages, not just BuildStage

# v5.5

* [Issue #151](https://github.com/manheim/terraform-pipeline/issues/151) Fix CPS mismatch errors, reduce meta magic in Jenkinsfile.groovy and Stage decorations
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ The example above gives you a bare-bones pipeline, and there may be Jenkinsfile
* [ConditionalApplyPlugin](./docs/ConditionalApplyPlugin.md): only allow apply on master branch.
* [DefaultEnvironmentPlugin](./docs/DefaultEnvironmentPlugin.md): automatically set `TF_VAR_environment` variable.
### Credentials and Configuration Management
* [CredentialsPlugin](./docs/CredentialsPlugin.md): Inject Jenkins credentials into your BuildStage.
* [CredentialsPlugin](./docs/CredentialsPlugin.md): Inject Jenkins credentials into your stages.
* [FileParametersPlugin](./docs/FileParametersPlugin.md): Use properties files to inject environment-specific variables.
* [ParameterStoreBuildWrapperPlugin](./docs/ParameterStoreBuildWrapperPlugin.md): Inject environment-specific variables using `withAwsParameterStore`.
* [ParameterStoreExecPlugin](./docs/ParameterStoreExecPlugin.md): Inject environment-specific variables using parameter-store-exec.
Expand Down
12 changes: 7 additions & 5 deletions docs/CredentialsPlugin.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
## [CredentialsPlugin](../src/CredentialsPlugin.groovy)

Enable this plugin to inject credentials into your BuildStage using the [Jenkins Credentials Plugin](https://wiki.jenkins.io/display/JENKINS/Credentials+Plugin).
Enable this plugin to inject credentials into your stages using the [Jenkins Credentials Plugin](https://wiki.jenkins.io/display/JENKINS/Credentials+Plugin).

One-time setup:
* Install the [Jenkins Credentials Plugin](https://wiki.jenkins.io/display/JENKINS/Credentials+Plugin) on your Jenkins master.
* Define a credential that you want to inject. Currently, only usernamePassword credentials are supported.

Specify the credential that you want to inject during the BuildStage. Optionally provide custom username/password environment variables that will contain the credential values for your use.
Specify the credential that you want to inject in your stages. Optionally provide custom username/password environment variables that will contain the credential values for your use.

```
// Jenkinsfile
@Library(['terraform-pipeline@v5.0']) _
Jenkinsfile.init(this)
// MY_CREDENTIALS_USERNAME and MY_CREDENTIALS_PASSWORD will contain the respective username/password values of the 'my-credentials' credential.
CredentialsPlugin.withBuildCredentials('my-credentials').init()
def validate = new TerraformValidateStage()
// MY_CREDENTIALS_USERNAME and MY_CREDENTIALS_PASSWORD will contain the respective username/password values of the 'my-credentials' credential.
def build = new BuildStage()
def deployQA = new TerraformEnvironmentStage('qa')
def deployQa = new TerraformEnvironmentStage('qa')
def testQa = new RegressionStage()
def deployUat = new TerraformEnvironmentStage('uat')
def deployProd = new TerraformEnvironmentStage('prod')
validate.then(build)
.then(deployQA)
.then(deployQa)
.then(testQa)
.then(deployUat)
.then(deployProd)
.build()
Expand Down
29 changes: 29 additions & 0 deletions docs/ParameterStoreBuildWrapperPlugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,32 @@ validate.then(deployQA)
.then(deployProd)
.build()
```

Optionally, you can override the default ParameterStore path with your own custom pattern.

```
// Jenkinsfile
@Library(['terraform-pipeline@v5.0']) _
Jenkinsfile.init(this)
// Enable ParameterStoreBuildWrapperPlugin with a custom path pattern
ParameterStoreBuildWrapperPlugin.withPathPattern { options -> "/${options['organization']}/${options['environment']}/${options['repoName']}" }
.init()
def validate = new TerraformValidateStage()
// Inject all parameters in /<GitOrg>/qa/<GitRepo>
def deployQA = new TerraformEnvironmentStage('qa')
// Inject all parameters in /<GitOrg>/uat/<GitRepo>
def deployUat = new TerraformEnvironmentStage('uat')
// Inject all parameters in /<GitOrg>/prod/<GitRepo>
def deployProd = new TerraformEnvironmentStage('prod')
validate.then(deployQA)
.then(deployUat)
.then(deployProd)
.build()
```
20 changes: 19 additions & 1 deletion src/CredentialsPlugin.groovy
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
class CredentialsPlugin implements BuildStagePlugin {
class CredentialsPlugin implements BuildStagePlugin, RegressionStagePlugin, TerraformEnvironmentStagePlugin, TerraformValidateStagePlugin {
private static globalBuildCredentials = []
private buildCredentials = []

public static void init() {
def plugin = new CredentialsPlugin()

BuildStage.addPlugin(plugin)
RegressionStage.addPlugin(plugin)
TerraformEnvironmentStage.addPlugin(plugin)
TerraformValidateStage.addPlugin(plugin)
}

public CredentialsPlugin() {
Expand All @@ -24,6 +27,21 @@ class CredentialsPlugin implements BuildStagePlugin {
buildStage.decorate(addBuildCredentials())
}

@Override
public void apply(RegressionStage regressionStage) {
regressionStage.decorate(addBuildCredentials())
}

@Override
public void apply(TerraformEnvironmentStage environmentStage) {
environmentStage.decorate(addBuildCredentials())
}

@Override
public void apply(TerraformValidateStage validateStage) {
validateStage.decorate(addBuildCredentials())
}

private addBuildCredentials() {
def credentials = this.buildCredentials
return { closure ->
Expand Down
19 changes: 18 additions & 1 deletion src/ParameterStoreBuildWrapperPlugin.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@ import static TerraformEnvironmentStage.PLAN
import static TerraformEnvironmentStage.APPLY

class ParameterStoreBuildWrapperPlugin implements TerraformEnvironmentStagePlugin {
private static globalPathPattern
private static defaultPathPattern = { options -> "/${options['organization']}/${options['repoName']}/${options['environment']}/" }

public static void init() {
TerraformEnvironmentStage.addPlugin(new ParameterStoreBuildWrapperPlugin())
}

public static withPathPattern(Closure newPathPattern) {
globalPathPattern = newPathPattern
return this
}

@Override
public void apply(TerraformEnvironmentStage stage) {
def environment = stage.getEnvironment()
Expand All @@ -23,8 +31,13 @@ class ParameterStoreBuildWrapperPlugin implements TerraformEnvironmentStagePlugi
String pathForEnvironment(String environment) {
String organization = Jenkinsfile.instance.getOrganization()
String repoName = Jenkinsfile.instance.getRepoName()
def patternOptions = [ environment: environment,
repoName: repoName,
organization: organization ]

return "/${organization}/${repoName}/${environment}/"
def pathPattern = globalPathPattern ?: defaultPathPattern

return pathPattern(patternOptions)
}

public static Closure addParameterStoreBuildWrapper(Map options = []) {
Expand All @@ -40,4 +53,8 @@ class ParameterStoreBuildWrapperPlugin implements TerraformEnvironmentStagePlugi
}
}
}

public static reset() {
globalPathPattern = null
}
}
4 changes: 4 additions & 0 deletions src/TerraformEnvironmentStage.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ class TerraformEnvironmentStage implements Stage {
}
}

public void decorate(Closure decoration) {
decorations.add(ALL, decoration)
}

public decorate(String stageName, Closure decoration) {
decorations.add(stageName, decoration)
}
Expand Down
4 changes: 4 additions & 0 deletions src/TerraformValidateStage.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ class TerraformValidateStage implements Stage {
}
}

public decorate(Closure decoration) {
decorations.add(ALL, decoration)
}

public decorate(String stageName, Closure decoration) {
decorations.add(stageName, decoration)
}
Expand Down
67 changes: 67 additions & 0 deletions test/CredentialsPluginTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import static org.hamcrest.Matchers.instanceOf
import static org.hamcrest.Matchers.is
import static org.hamcrest.Matchers.notNullValue
import static org.junit.Assert.assertThat
import static org.mockito.Mockito.mock
import static org.mockito.Mockito.spy
import static org.mockito.Mockito.verify
import static org.mockito.Mockito.any

import org.junit.After
import org.junit.Test
Expand All @@ -17,6 +21,9 @@ class CredentialsPluginTest {
@After
void resetPlugins() {
BuildStage.resetPlugins()
RegressionStage.resetPlugins()
TerraformEnvironmentStage.resetPlugins()
TerraformValidateStage.resetPlugins()
CredentialsPlugin.reset()
}

Expand All @@ -28,6 +35,24 @@ class CredentialsPluginTest {

assertThat(actualPlugins, hasItem(instanceOf(CredentialsPlugin.class)))
}

@Test
void modifiesRegressionStage() {
CredentialsPlugin.init()

Collection actualPlugins = RegressionStage.getPlugins()

assertThat(actualPlugins, hasItem(instanceOf(CredentialsPlugin.class)))
}

@Test
void modifiesTerraformEnvironmentStage() {
CredentialsPlugin.init()

Collection actualPlugins = TerraformEnvironmentStage.getPlugins()

assertThat(actualPlugins, hasItem(instanceOf(CredentialsPlugin.class)))
}
}

public class WithBuildCredentials {
Expand Down Expand Up @@ -139,5 +164,47 @@ class CredentialsPluginTest {
assertThat(results['passwordVariable'], is(equalTo(customPasswordVariable)))
}
}

class Apply {
@Test
void decoratesTheBuildStage() {
def buildStage = mock(BuildStage.class)
def plugin = spy(new CredentialsPlugin())

plugin.apply(buildStage)

verify(buildStage).decorate(any(Closure.class))
}

@Test
void decoratesTheRegressionStage() {
def testStage = mock(RegressionStage.class)
def plugin = spy(new CredentialsPlugin())

plugin.apply(testStage)

verify(testStage).decorate(any(Closure.class))
}

@Test
void decoratesTheTerraformEnvironmentStage() {
def environment = mock(TerraformEnvironmentStage.class)
def plugin = spy(new CredentialsPlugin())

plugin.apply(environment)

verify(environment).decorate(any(Closure.class))
}

@Test
void decoratesTheTerraformValidateStage() {
def environment = mock(TerraformValidateStage.class)
def plugin = spy(new CredentialsPlugin())

plugin.apply(environment)

verify(environment).decorate(any(Closure.class))
}
}
}

32 changes: 31 additions & 1 deletion test/ParameterStoreBuildWrapperPluginTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class ParameterStoreBuildWrapperPluginTest {
@After
public void reset() {
Jenkinsfile.instance = null
ParameterStoreBuildWrapperPlugin.reset()
}

private configureJenkins(Map config = [:]) {
Expand All @@ -51,7 +52,36 @@ class ParameterStoreBuildWrapperPluginTest {
ParameterStoreBuildWrapperPlugin plugin = new ParameterStoreBuildWrapperPlugin()

String actual = plugin.pathForEnvironment(environment)
assertEquals(actual, "/${organization}/${repoName}/${environment}/".toString())
assertEquals("/${organization}/${repoName}/${environment}/".toString(), actual)
}

@Test
void usesCustomPatternWhenProvided() {
String organization = "MyOrg"
String repoName = "MyRepo"
String environment = "qa"
Closure customPattern = { options -> "/foo/${options['organization']}/${options['environment']}/${options['repoName']}" }

configureJenkins(repoName: repoName, organization: organization)
ParameterStoreBuildWrapperPlugin.withPathPattern(customPattern)
ParameterStoreBuildWrapperPlugin plugin = new ParameterStoreBuildWrapperPlugin()

String actual = plugin.pathForEnvironment(environment)
assertEquals("/foo/${organization}/${environment}/${repoName}".toString(), actual)
}
}

class WithPathPattern {
@After
public void reset() {
ParameterStoreBuildWrapperPlugin.reset()
}

@Test
void isFluent() {
def result = ParameterStoreBuildWrapperPlugin.withPathPattern { options -> 'somePattern' }

assertEquals(ParameterStoreBuildWrapperPlugin.class, result)
}
}
}
Expand Down

0 comments on commit 817be51

Please sign in to comment.