diff --git a/CHANGELOG.md b/CHANGELOG.md index 697dcecd..7c607737 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,9 @@ * [Issue #344](https://github.com/manheim/terraform-pipeline/issues/344) Add PLAN_ONLY parameter to PlanOnlyPlugin * **BREAKING CHANGE** This change is a breaking change. Prior to this update, applying the PlanOnlyPlugin would restrict the pipeline to only running `terraform plan`. This update changes behavior to simply providing a `PLAN_ONLY` boolean parameter that can be set to restrict the build behavior. It defaults to `false`. -* [Issue #347](https://github.com/manheim/terraform-pipeline/pull/348) - Feature: TerraformOutputOnlyPlugin - can restrict a pipeline run to - displaying the current state outputs only via new job parameters. +* [Issue #347](https://github.com/manheim/terraform-pipeline/pull/348) Feature: TerraformOutputOnlyPlugin - can restrict a pipeline run to displaying the current state outputs only via new job parameters. * [Issue #318](https://github.com/manheim/terraform-pipeline/issues/318) Feature: Show environment on confirm command page +* [Issue #349](https://github.com/manheim/terraform-pipeline/pull/349) Extract plugin handling to a `Pluggable` trait to DRY up the existing TerraformCommand classes. # v5.15 diff --git a/src/Pluggable.groovy b/src/Pluggable.groovy new file mode 100644 index 00000000..51557de5 --- /dev/null +++ b/src/Pluggable.groovy @@ -0,0 +1,51 @@ +/** + * The `Pluggable` trait can be used to add plugin management to a class. It + * takes as a type parameter the plugin type it accepts. + */ +trait Pluggable implements Resettable { + private static plugins = [] + private appliedPlugins = [] + + /** + * Assures that all plugins are applied, and are applied at most once. It + * can be safely called multiple times. + */ + public applyPlugins() { + def remainingPlugins = plugins - appliedPlugins + + for (T plugin in remainingPlugins) { + plugin.apply(this) + appliedPlugins << plugin + } + } + + /** + * Accepts a plugin of the appropriate type and adds it to the list of plugins. + * + * @param plugin The plugin to add + */ + public static void addPlugin(T plugin) { + plugins << plugin + } + + public static void setPlugins(plugins) { + this.plugins = plugins + } + + public static getPlugins() { + return plugins + } + + /** + * Reset plugins will reset the plugin list to a default set of plugins. + * + * @param defaultPlugins list of plugins to set, default: [] + */ + public static void resetPlugins(defaultPlugins = []) { + this.plugins = defaultPlugins.clone() + } + + public static void reset() { + this.resetPlugins() + } +} diff --git a/src/TerraformApplyCommand.groovy b/src/TerraformApplyCommand.groovy index 10fae1ec..6f38ac62 100644 --- a/src/TerraformApplyCommand.groovy +++ b/src/TerraformApplyCommand.groovy @@ -1,4 +1,4 @@ -class TerraformApplyCommand implements TerraformCommand, Resettable { +class TerraformApplyCommand implements TerraformCommand, Pluggable { private boolean input = false private String terraformBinary = "terraform" private String command = "apply" @@ -6,8 +6,6 @@ class TerraformApplyCommand implements TerraformCommand, Resettable { private prefixes = [] private suffixes = [] private args = [] - private static plugins = [] - private appliedPlugins = [] private String directory private Closure variablePattern private Closure mapPattern @@ -76,8 +74,7 @@ class TerraformApplyCommand implements TerraformCommand, Resettable { } public String toString() { - applyPluginsOnce() - + applyPlugins() def pieces = [] pieces += prefixes pieces << terraformBinary @@ -95,33 +92,12 @@ class TerraformApplyCommand implements TerraformCommand, Resettable { return pieces.join(' ') } - private applyPluginsOnce() { - def remainingPlugins = plugins - appliedPlugins - - for (TerraformApplyCommandPlugin plugin in remainingPlugins) { - plugin.apply(this) - appliedPlugins << plugin - } - } - - public static void addPlugin(TerraformApplyCommandPlugin plugin) { - plugins << plugin - } - public static TerraformApplyCommand instanceFor(String environment) { return new TerraformApplyCommand(environment) .withInput(false) .withArgument("-auto-approve") } - public static getPlugins() { - return plugins - } - - public static reset() { - this.plugins = [] - } - public String getEnvironment() { return environment } diff --git a/src/TerraformCommand.groovy b/src/TerraformCommand.groovy index f6887b74..3f6205db 100644 --- a/src/TerraformCommand.groovy +++ b/src/TerraformCommand.groovy @@ -1,3 +1,6 @@ +/** + * `TerraformCommand` is an interface that can be implemented by a class that wraps a Terraform command. + */ interface TerraformCommand { public String getEnvironment() } diff --git a/src/TerraformFormatCommand.groovy b/src/TerraformFormatCommand.groovy index 13b9fa79..3771eb96 100644 --- a/src/TerraformFormatCommand.groovy +++ b/src/TerraformFormatCommand.groovy @@ -1,6 +1,4 @@ -class TerraformFormatCommand implements Resettable { - private static globalPlugins = [] - private appliedPlugins = [] +class TerraformFormatCommand implements Pluggable { private static boolean check = false private static boolean recursive = false private static boolean diff = false @@ -10,8 +8,7 @@ class TerraformFormatCommand implements Resettable { private Closure diffOptionPattern public String toString() { - applyPluginsOnce() - + applyPlugins() def pattern def parts = [] parts << 'terraform fmt' @@ -29,19 +26,6 @@ class TerraformFormatCommand implements Resettable { return parts.join(' ') } - public static addPlugin(TerraformFormatCommandPlugin plugin) { - this.globalPlugins << plugin - } - - private applyPluginsOnce() { - def remainingPlugins = globalPlugins - appliedPlugins - - for (TerraformFormatCommandPlugin plugin in remainingPlugins) { - plugin.apply(this) - appliedPlugins << plugin - } - } - public static withCheck(newValue = true) { check = newValue return this @@ -76,10 +60,10 @@ class TerraformFormatCommand implements Resettable { return this } - public static reset() { + public static void reset() { check = false recursive = false diff = false - globalPlugins = [] + resetPlugins() } } diff --git a/src/TerraformInitCommand.groovy b/src/TerraformInitCommand.groovy index 623a78db..dd5b6085 100644 --- a/src/TerraformInitCommand.groovy +++ b/src/TerraformInitCommand.groovy @@ -1,4 +1,4 @@ -class TerraformInitCommand implements Resettable { +class TerraformInitCommand implements TerraformCommand, Pluggable { private boolean input = false private String terraformBinary = "terraform" private String command = "init" @@ -9,9 +9,6 @@ class TerraformInitCommand implements Resettable { private boolean doBackend = true private String directory - private static globalPlugins = [] - private appliedPlugins = [] - public TerraformInitCommand(String environment) { this.environment = environment } @@ -47,8 +44,7 @@ class TerraformInitCommand implements Resettable { } public String toString() { - applyPluginsOnce() - + applyPlugins() def pieces = [] pieces += prefixes pieces << terraformBinary @@ -72,29 +68,7 @@ class TerraformInitCommand implements Resettable { return pieces.join(' ') } - private applyPluginsOnce() { - def remainingPlugins = globalPlugins - appliedPlugins - - for (TerraformInitCommandPlugin plugin in remainingPlugins) { - plugin.apply(this) - appliedPlugins << plugin - } - } - - public static void addPlugin(TerraformInitCommandPlugin plugin) { - this.globalPlugins << plugin - } - public static TerraformInitCommand instanceFor(String environment) { return new TerraformInitCommand(environment) } - - public static getPlugins() { - return this.globalPlugins - } - - public static reset() { - globalPlugins = [] - // This is awkward - what about the applied plugins...? - } } diff --git a/src/TerraformOutputCommand.groovy b/src/TerraformOutputCommand.groovy index 86c84493..b6b1524c 100644 --- a/src/TerraformOutputCommand.groovy +++ b/src/TerraformOutputCommand.groovy @@ -1,13 +1,10 @@ -class TerraformOutputCommand implements TerraformCommand, Resettable { - private static final DEFAULT_PLUGINS = [] +class TerraformOutputCommand implements TerraformCommand, Pluggable { private String command = "output" - private boolean json = false - private String redirectFile private String terraformBinary = "terraform" String environment + private boolean json = false + private String redirectFile private String stateFilePath - private static plugins = DEFAULT_PLUGINS.clone() - private appliedPlugins = [] public TerraformOutputCommand(String environment) { this.environment = environment @@ -24,8 +21,7 @@ class TerraformOutputCommand implements TerraformCommand, Resettable { } public String toString() { - applyPluginsOnce() - + applyPlugins() def pieces = [] pieces << terraformBinary pieces << command @@ -41,31 +37,10 @@ class TerraformOutputCommand implements TerraformCommand, Resettable { return pieces.join(' ') } - private applyPluginsOnce() { - def remainingPlugins = plugins - appliedPlugins - - for (TerraformOutputCommandPlugin plugin in remainingPlugins) { - plugin.apply(this) - appliedPlugins << plugin - } - } - - public static addPlugin(TerraformOutputCommandPlugin plugin) { - plugins << plugin - } - public static TerraformOutputCommand instanceFor(String environment) { return new TerraformOutputCommand(environment).withJson(false) } - public static getPlugins() { - return plugins - } - - public static reset() { - this.plugins = DEFAULT_PLUGINS.clone() - } - public String getEnvironment() { return environment } diff --git a/src/TerraformPlanCommand.groovy b/src/TerraformPlanCommand.groovy index 26bc9464..32cda4c7 100644 --- a/src/TerraformPlanCommand.groovy +++ b/src/TerraformPlanCommand.groovy @@ -1,5 +1,4 @@ -class TerraformPlanCommand implements TerraformCommand, Resettable { - private static final DEFAULT_PLUGINS = [] +class TerraformPlanCommand implements TerraformCommand, Pluggable { private boolean input = false private String terraformBinary = "terraform" private String command = "plan" @@ -7,8 +6,6 @@ class TerraformPlanCommand implements TerraformCommand, Resettable { private prefixes = [] private suffixes = [] private arguments = [] - private static plugins = DEFAULT_PLUGINS.clone() - private appliedPlugins = [] private String directory private String errorFile private Closure variablePattern @@ -78,8 +75,7 @@ class TerraformPlanCommand implements TerraformCommand, Resettable { } public String toString() { - applyPluginsOnce() - + applyPlugins() def pieces = [] pieces = pieces + prefixes pieces << terraformBinary @@ -103,32 +99,11 @@ class TerraformPlanCommand implements TerraformCommand, Resettable { return pieces.join(' ') } - private applyPluginsOnce() { - def remainingPlugins = plugins - appliedPlugins - - for (TerraformPlanCommandPlugin plugin in remainingPlugins) { - plugin.apply(this) - appliedPlugins << plugin - } - } - - public static addPlugin(TerraformPlanCommandPlugin plugin) { - plugins << plugin - } - public static TerraformPlanCommand instanceFor(String environment) { return new TerraformPlanCommand(environment) .withInput(false) } - public static getPlugins() { - return plugins - } - - public static reset() { - this.plugins = DEFAULT_PLUGINS.clone() - } - public String getEnvironment() { return environment } diff --git a/src/TerraformValidateCommand.groovy b/src/TerraformValidateCommand.groovy index 1c15f3be..c66d9bf6 100644 --- a/src/TerraformValidateCommand.groovy +++ b/src/TerraformValidateCommand.groovy @@ -1,11 +1,9 @@ -class TerraformValidateCommand implements Resettable { +class TerraformValidateCommand implements Pluggable { private String terraformBinary = "terraform" private String command = "validate" private arguments = [] private prefixes = [] private suffixes = [] - private static globalPlugins = [] - private appliedPlugins = [] private String directory public TerraformValidateCommand() { @@ -32,7 +30,7 @@ class TerraformValidateCommand implements Resettable { } public String toString() { - applyPluginsOnce() + applyPlugins() def pieces = [] pieces = pieces + prefixes pieces << terraformBinary @@ -48,28 +46,7 @@ class TerraformValidateCommand implements Resettable { return pieces.join(' ') } - private applyPluginsOnce() { - def remainingPlugins = globalPlugins - appliedPlugins - - for (TerraformValidateCommandPlugin plugin in remainingPlugins) { - plugin.apply(this) - appliedPlugins << plugin - } - } - - public static addPlugin(TerraformValidateCommandPlugin plugin) { - this.globalPlugins << plugin - } - public static TerraformValidateCommand instance() { return new TerraformValidateCommand() } - - public static getPlugins() { - return this.globalPlugins - } - - public static reset() { - this.globalPlugins = [] - } } diff --git a/test/ResetStaticStateExtension.groovy b/test/ResetStaticStateExtension.groovy index e8749990..5abd1bed 100644 --- a/test/ResetStaticStateExtension.groovy +++ b/test/ResetStaticStateExtension.groovy @@ -1,3 +1,4 @@ +import org.codehaus.groovy.transform.trait.Traits import org.junit.jupiter.api.extension.AfterEachCallback import org.junit.jupiter.api.extension.BeforeEachCallback import org.junit.jupiter.api.extension.ExtensionContext @@ -27,6 +28,6 @@ public class ResetStaticStateExtension implements BeforeEachCallback, @Memoized public findAllResettableClasses() { - return new Reflections(Resettable.getPackage().getName()).getSubTypesOf( Resettable.class ) + return new Reflections(Resettable.getPackage().getName()).getSubTypesOf( Resettable.class ).findAll { !Traits.isTrait(it) } } }