terraspace 1.0.2 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0161019335d6d328b768dc5f50962776c3f1f9c9d28f1e963b082fe457f45ab
4
- data.tar.gz: 520f8388b0814c8fe29781768cd7c390ddfdf011ba48150d8b2052f8b674b738
3
+ metadata.gz: 57c0c4f1c1fce5c1dd8fcd7447f5bc5a82e17d66e5353f4d092d492c137021e5
4
+ data.tar.gz: 81a4ba08410e898a1dd057625ca556392e746c4b48a3453874472c60424c3949
5
5
  SHA512:
6
- metadata.gz: e4b2c9e7285e22d0e3b1e97191a181694c85c0ff185798be9320ee3783c422d380b3ee1ca4b1a15893baf215c74618890b87416f0ab5dd5ec8524a8af438bae4
7
- data.tar.gz: fd33e4677f1f0aefeb2bad8e9470d8eca758d16da98615d8889076930b27b4ccba43dae144521bf5ea324cb4d0c0e7cd93681c8c2925ba41ff644858672d78cd
6
+ metadata.gz: e629795fd75ad23d343ec28390e76756650f11740c6b380ad3df87cb216f7a998acfea282ebb1a1399343c3db2b5abe452705bb82fd61e76a8cd1a2ca258300b
7
+ data.tar.gz: 7104217cb56ee3ba2317d4d2abe0d4f760fe35de359903349a97bef4c2de3c92aaff3e0605789fd9e970f8a841a2203dedc9d740d9bddf127d2555a3f3818f30
data/CHANGELOG.md CHANGED
@@ -3,6 +3,18 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  This project *loosely tries* to adhere to [Semantic Versioning](http://semver.org/), even before v1.0.
5
5
 
6
+ ## [1.0.6] - 2022-01-24
7
+ - [#195](https://github.com/boltops-tools/terraspace/pull/195) improve autodetection for plugin expander for backend like remote
8
+
9
+ ## [1.0.5] - 2022-01-23
10
+ - [#194](https://github.com/boltops-tools/terraspace/pull/194) ability to allow and deny envs, regions, and stacks
11
+
12
+ ## [1.0.4] - 2022-01-21
13
+ - [#193](https://github.com/boltops-tools/terraspace/pull/193) improve all include_stacks and exclude_stacks option
14
+
15
+ ## [1.0.3] - 2022-01-20
16
+ - [#192](https://github.com/boltops-tools/terraspace/pull/192) run super-early boot hooks before dotenv load
17
+
6
18
  ## [1.0.2] - 2022-01-17
7
19
  - [#190](https://github.com/boltops-tools/terraspace/pull/190) update terraspace-bundler gem depedency to 0.5.0
8
20
 
@@ -0,0 +1,12 @@
1
+ class Terraspace::App::CallableOption
2
+ module Concern
3
+ def callable_option(options={})
4
+ callable_option = Terraspace::App::CallableOption.new(
5
+ config_name: options[:config_name],
6
+ config_value: options[:config_value],
7
+ passed_args: options[:passed_args],
8
+ )
9
+ callable_option.object
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,58 @@
1
+ # Class represents a terraspace option that is possibly callable. Examples:
2
+ #
3
+ # config.allow.envs
4
+ # config.allow.regions
5
+ # config.deny.envs
6
+ # config.deny.regions
7
+ # config.all.include_stacks
8
+ # config.all.exclude_stacks
9
+ #
10
+ # Abstraction is definitely obtuse. Using it to get rid of duplication.
11
+ #
12
+ class Terraspace::App
13
+ class CallableOption
14
+ include Terraspace::Util::Logging
15
+
16
+ def initialize(options={})
17
+ @options = options
18
+ # Example:
19
+ # config_name: config.allow.envs
20
+ # config_value: ["dev"]
21
+ # args: [@stack_name] # passed to object.call
22
+ @config_name = options[:config_name]
23
+ @config_value = options[:config_value]
24
+ @passed_args = options[:passed_args]
25
+ end
26
+
27
+ # Returns either an Array or nil
28
+ def object
29
+ case @config_value
30
+ when nil
31
+ return nil
32
+ when Array
33
+ return @config_value
34
+ when -> (c) { c.respond_to?(:public_instance_methods) && c.public_instance_methods.include?(:call) }
35
+ object= @config_value.new
36
+ when -> (c) { c.respond_to?(:call) }
37
+ object = @config_value
38
+ else
39
+ raise "Invalid option for #{@config_name}"
40
+ end
41
+
42
+ if object
43
+ result = @passed_args.empty? ? object.call : object.call(*@passed_args)
44
+ unless result.is_a?(Array) || result.is_a?(NilClass)
45
+ message = "ERROR: The #{@config_name} needs to return an Array or nil"
46
+ logger.info message.color(:yellow)
47
+ logger.info <<~EOL
48
+ The #{@config_name} when assigned a class, object, or proc must implement
49
+ the call method and return an Array or nil.
50
+ The current return value is a #{result.class}
51
+ EOL
52
+ raise message
53
+ end
54
+ end
55
+ result
56
+ end
57
+ end
58
+ end
@@ -12,17 +12,29 @@ module Terraspace
12
12
 
13
13
  def defaults
14
14
  config = ActiveSupport::OrderedOptions.new
15
+
15
16
  config.all = ActiveSupport::OrderedOptions.new
16
17
  config.all.concurrency = 5
17
18
  config.all.exit_on_fail = ActiveSupport::OrderedOptions.new
18
19
  config.all.exit_on_fail.down = true
19
20
  config.all.exit_on_fail.up = true
20
- config.all.ignore_stacks = nil
21
- config.all.include_stacks = nil
21
+
22
22
  config.allow = ActiveSupport::OrderedOptions.new
23
23
  config.allow.envs = nil
24
24
  config.allow.regions = nil
25
+ config.allow.stacks = nil
26
+ config.deny = ActiveSupport::OrderedOptions.new
27
+ config.deny.envs = nil
28
+ config.deny.regions = nil
29
+ config.deny.stacks = nil
30
+
31
+ config.all.exclude_stacks = nil
32
+ config.all.include_stacks = nil
33
+ config.all.consider_allow_deny_stacks = true
34
+
25
35
  config.auto_create_backend = true
36
+ config.autodetect = ActiveSupport::OrderedOptions.new
37
+ config.autodetect.expander = nil
26
38
  config.build = ActiveSupport::OrderedOptions.new
27
39
  config.build.cache_dir = ":CACHE_ROOT/:REGION/:ENV/:BUILD_DIR"
28
40
  config.build.cache_root = nil # defaults to /full/path/to/.terraspace-cache
@@ -2,13 +2,9 @@ module Terraspace
2
2
  class Autodetect
3
3
  def plugin
4
4
  plugins = Terraspace::Plugin.meta.keys
5
- if plugins.size == 1
6
- plugins.first
7
- else
8
- precedence = %w[aws azurerm google]
9
- precedence.find do |p|
10
- plugins.include?(p)
11
- end
5
+ precedence = %w[aws azurerm google]
6
+ precedence.find do |p|
7
+ plugins.include?(p)
12
8
  end
13
9
  end
14
10
  end
@@ -1,8 +1,8 @@
1
1
  module Terraspace
2
2
  module Booter
3
3
  def boot
4
- Dotenv.new.load!
5
4
  run_hooks
5
+ Dotenv.new.load!
6
6
  Terraspace::Bundle.require # load plugins
7
7
  load_plugin_default_configs
8
8
  Terraspace::App::Inits.run_all
@@ -0,0 +1,59 @@
1
+ class Terraspace::Builder::Allow
2
+ class Base
3
+ include Terraspace::App::CallableOption::Concern
4
+
5
+ def initialize(mod)
6
+ @mod = mod # Only Region subclass uses @mod but keeping interface same for Env for simplicity
7
+ @stack_name = mod.name
8
+ end
9
+
10
+ def check!
11
+ messages = []
12
+ unless allowed?
13
+ messages << message # message is interface method
14
+ end
15
+ unless messages.empty?
16
+ puts "ERROR: The configs do not allow this.".color(:red)
17
+ puts messages
18
+ exit 1
19
+ end
20
+ end
21
+
22
+ def allowed?
23
+ if allows.nil? && denys.nil?
24
+ true
25
+ elsif denys.nil?
26
+ allows.include?(check_value)
27
+ elsif allows.nil?
28
+ !denys.include?(check_value)
29
+ else
30
+ allows.include?(check_value) && !denys.include?(check_value)
31
+ end
32
+ end
33
+
34
+ def allows
35
+ callable_option(
36
+ config_name: "config.allow.#{config_name}",
37
+ config_value: config.dig(:allow, config_name),
38
+ passed_args: [@stack_name],
39
+ )
40
+ end
41
+
42
+ def denys
43
+ callable_option(
44
+ config_name: "config.deny.#{config_name}",
45
+ config_value: config.dig(:deny, config_name),
46
+ passed_args: [@stack_name],
47
+ )
48
+ end
49
+
50
+ private
51
+ def config
52
+ Terraspace.config
53
+ end
54
+
55
+ def config_name
56
+ self.class.to_s.split('::').last.underscore.pluralize.to_sym # ActiveSuport::HashWithIndifferentAccess#dig requires symbol
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,17 @@
1
+ class Terraspace::Builder::Allow
2
+ class Env < Base
3
+ # interface method
4
+ def message
5
+ messages = []
6
+ messages << "This env is not allowed to be used: TS_ENV=#{Terraspace.env}"
7
+ messages << "Allow envs: #{allows.join(', ')}" if allows
8
+ messages << "Deny envs: #{denys.join(', ')}" if denys
9
+ messages.join("\n")
10
+ end
11
+
12
+ # interface method
13
+ def check_value
14
+ Terraspace.env
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,31 @@
1
+ class Terraspace::Builder::Allow
2
+ class Region < Base
3
+ # interface method
4
+ def message
5
+ messages = []
6
+ word = config_name.to_s # IE: regions or locations
7
+ messages << "This #{word.singularize} is not allowed to be used: Detected current #{word.singularize}=#{current_region}"
8
+ messages << "Allow #{word}: #{allows.join(', ')}" if allows
9
+ messages << "Deny #{word}: #{denys.join(', ')}" if denys
10
+ messages.join("\n")
11
+ end
12
+
13
+ # interface method
14
+ def check_value
15
+ current_region
16
+ end
17
+
18
+ def current_region
19
+ expander = Terraspace::Compiler::Expander.autodetect(@mod).expander
20
+ expander.region
21
+ end
22
+
23
+ def config_name
24
+ if config.allow.locations || config.deny.locations
25
+ :locations # ActiveSuport::HashWithIndifferentAccess#dig requires symbol
26
+ else
27
+ super # :regions
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ class Terraspace::Builder::Allow
2
+ class Stack < Base
3
+ # interface method
4
+ def message
5
+ messages = []
6
+ messages << "This stack is not allowed to be used for TS_ENV=#{Terraspace.env}"
7
+ messages << "Allow stacks: #{allows.join(', ')}" if allows
8
+ messages << "Deny stacks: #{denys.join(', ')}" if denys
9
+ messages.join("\n")
10
+ end
11
+
12
+ # interface method
13
+ def check_value
14
+ @mod.name
15
+ end
16
+ end
17
+ end
@@ -5,39 +5,9 @@ class Terraspace::Builder
5
5
  end
6
6
 
7
7
  def check!
8
- messages = []
9
- unless env_allowed?
10
- messages << "This env is not allowed to be used: TS_ENV=#{Terraspace.env}"
11
- messages << "Allowed envs: #{config.allow.envs.join(', ')}"
12
- end
13
- unless region_allowed?
14
- messages << "This region is not allowed to be used: Detected current region=#{current_region}"
15
- messages << "Allowed regions: #{config.allow.regions.join(', ')}"
16
- end
17
- unless messages.empty?
18
- puts "ERROR: The configs do not allow this.".color(:red)
19
- puts messages
20
- exit 1
21
- end
22
- end
23
-
24
- def env_allowed?
25
- return true unless config.allow.envs
26
- config.allow.envs.include?(Terraspace.env)
27
- end
28
-
29
- def region_allowed?
30
- return true unless config.allow.regions
31
- config.allow.regions.include?(current_region)
32
- end
33
-
34
- def current_region
35
- expander = Terraspace::Compiler::Expander.autodetect(@mod).expander
36
- expander.region
37
- end
38
-
39
- def config
40
- Terraspace.config
8
+ Env.new(@mod).check!
9
+ Stack.new(@mod).check!
10
+ Region.new(@mod).check!
41
11
  end
42
12
  end
43
13
  end
@@ -12,7 +12,7 @@ class Terraspace::Compiler::Expander
12
12
  @mod = mod
13
13
  end
14
14
 
15
- COMMENT = /^\s+#/
15
+ COMMENT = /^\s*#/
16
16
  # Works for both backend.rb DSL and backend.tf ERB
17
17
  def detect
18
18
  return nil unless src_path # no backend file. returning nil means a local backend
@@ -3,8 +3,8 @@ module Terraspace::Compiler
3
3
  extend Memoist
4
4
  delegate :expand, :expansion, to: :expander
5
5
 
6
- def initialize(mod, name)
7
- @mod, @name = mod, name
6
+ def initialize(mod, backend=nil)
7
+ @mod, @backend = mod, backend
8
8
  end
9
9
 
10
10
  def expander
@@ -12,27 +12,51 @@ module Terraspace::Compiler
12
12
  end
13
13
  memoize :expander
14
14
 
15
+ # IE: TerraspacePluginAws::Interfaces::Expander
15
16
  def expander_class
16
- # IE: TerraspacePluginAws::Interfaces::Expander
17
- klass_name = Terraspace::Plugin.klass("Expander", backend: @name)
18
- klass_name ? klass_name.constantize : Terraspace::Plugin::Expander::Generic
19
- rescue NameError
17
+ class_name = expander_class_name
18
+ class_name ? class_name.constantize : Terraspace::Plugin::Expander::Generic
19
+ rescue NameError => e
20
+ logger.debug "#{e.class}: #{e.message}"
20
21
  Terraspace::Plugin::Expander::Generic
21
22
  end
22
23
 
24
+ def expander_class_name
25
+ plugin = Terraspace.config.autodetect.expander # contains plugin name. IE: aws, azurerm, google
26
+ if plugin
27
+ # early return for user override of autodetection
28
+ return Terraspace::Plugin.klass("Expander", plugin: plugin) # can return nil
29
+ end
30
+
31
+ backend = @backend || parse_backend
32
+ class_name = Terraspace::Plugin.klass("Expander", backend: backend) # can return nil
33
+ unless class_name
34
+ backend = plugin_backend
35
+ class_name = Terraspace::Plugin.klass("Expander", backend: backend) # can return nil
36
+ end
37
+ class_name
38
+ end
39
+
40
+ # autodetect by parsing backend.tf or backend.rb
41
+ def parse_backend
42
+ Backend.new(@mod).detect
43
+ end
44
+
45
+ # autodetect by looking up loaded plugins
46
+ def plugin_backend
47
+ plugin = Terraspace::Autodetect.new.plugin # IE: aws, azurerm, google
48
+ if plugin
49
+ data = Terraspace::Plugin.meta[plugin]
50
+ data[:backend] # IE: s3, azurerm, gcs
51
+ end
52
+ end
53
+
23
54
  class << self
24
55
  extend Memoist
25
-
26
56
  def autodetect(mod, opts={})
27
- backend = opts[:backend] || find_backend(mod)
28
- new(mod, backend)
57
+ new(mod, opts)
29
58
  end
30
59
  memoize :autodetect
31
-
32
- def find_backend(mod)
33
- Backend.new(mod).detect
34
- end
35
- memoize :find_backend
36
60
  end
37
61
  end
38
62
  end
@@ -1,28 +1,79 @@
1
1
  module Terraspace::Compiler
2
2
  class Select
3
+ include Terraspace::App::CallableOption::Concern
4
+ include Terraspace::Util::Logging
5
+
3
6
  def initialize(path)
4
7
  @path = path
5
8
  @stack_name = extract_stack_name(path)
6
9
  end
7
10
 
8
11
  def selected?
9
- all = Terraspace.config.all
10
- # Key difference between include_stacks vs all.include_stacks option is that
11
- # the option can be nil. The local variable is guaranteed to be an Array.
12
- # This simplifies the logic.
13
- include_stacks = all.include_stacks || []
14
- ignore_stacks = all.ignore_stacks || []
15
-
16
- if all.include_stacks.nil?
17
- !ignore_stacks.include?(@stack_name)
12
+ ignore_stacks_deprecation_warning
13
+ if include_stacks.nil? && exclude_stacks.nil?
14
+ true
15
+ elsif include_stacks.nil?
16
+ !exclude_stacks.include?(@stack_name)
17
+ elsif exclude_stacks.nil?
18
+ include_stacks.include?(@stack_name)
18
19
  else
19
- stacks = include_stacks - ignore_stacks
20
+ stacks = include_stacks - exclude_stacks
20
21
  stacks.include?(@stack_name)
21
22
  end
22
23
  end
23
24
 
25
+ def include_stacks
26
+ if config.all.include_stacks
27
+ config_name = "config.all.include_stacks"
28
+ config_value = config.dig(:all, :include_stacks)
29
+ elsif config.all.consider_allow_deny_stacks
30
+ config_name = "config.allow.stacks"
31
+ config_value = config.dig(:allow, :stacks)
32
+ else
33
+ return
34
+ end
35
+ callable_option(
36
+ config_name: config_name,
37
+ config_value: config_value,
38
+ passed_args: [@stack_name],
39
+ )
40
+ end
41
+
42
+ def exclude_stacks
43
+ if config.all.exclude_stacks
44
+ config_name = "config.all.exclude_stacks"
45
+ config_value = config.dig(:all, :exclude_stacks)
46
+ elsif config.all.consider_allow_deny_stacks
47
+ config_name = "config.deny.stacks"
48
+ config_value = config.dig(:deny, :stacks)
49
+ else
50
+ return
51
+ end
52
+ callable_option(
53
+ config_name: config_name,
54
+ config_value: config_value,
55
+ passed_args: [@stack_name],
56
+ )
57
+ end
58
+
59
+ private
60
+ def config
61
+ Terraspace.config
62
+ end
63
+
24
64
  def extract_stack_name(path)
25
65
  path.sub(%r{.*(app|vendor)/stacks/}, '')
26
66
  end
67
+
68
+ @@ignore_stacks_deprecation_warning = nil
69
+ def ignore_stacks_deprecation_warning
70
+ return unless config.all.ignore_stacks
71
+ return if @@ignore_stacks_deprecation_warning
72
+ puts <<~EOL.color(:yellow)
73
+ DEPRECATED: config.all.ignore_stacks
74
+ Instead use: config.all.exclude_stacks
75
+ EOL
76
+ @@ignore_stacks_deprecation_warning = true
77
+ end
27
78
  end
28
79
  end
@@ -1,3 +1,3 @@
1
1
  module Terraspace
2
- VERSION = "1.0.2"
2
+ VERSION = "1.0.6"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: terraspace
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tung Nguyen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-01-17 00:00:00.000000000 Z
11
+ date: 2022-01-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -478,12 +478,18 @@ files:
478
478
  - lib/terraspace/all/runner.rb
479
479
  - lib/terraspace/all/summary.rb
480
480
  - lib/terraspace/app.rb
481
+ - lib/terraspace/app/callable_option.rb
482
+ - lib/terraspace/app/callable_option/concern.rb
481
483
  - lib/terraspace/app/inits.rb
482
484
  - lib/terraspace/autodetect.rb
483
485
  - lib/terraspace/autoloader.rb
484
486
  - lib/terraspace/booter.rb
485
487
  - lib/terraspace/builder.rb
486
488
  - lib/terraspace/builder/allow.rb
489
+ - lib/terraspace/builder/allow/base.rb
490
+ - lib/terraspace/builder/allow/env.rb
491
+ - lib/terraspace/builder/allow/region.rb
492
+ - lib/terraspace/builder/allow/stack.rb
487
493
  - lib/terraspace/bundle.rb
488
494
  - lib/terraspace/cli.rb
489
495
  - lib/terraspace/cli/all.rb