terraspace 1.0.2 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
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