stack_master 0.6.0 → 0.7.0
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 +4 -4
- data/README.md +22 -0
- data/features/apply.feature +1 -1
- data/features/apply_with_s3.feature +106 -0
- data/features/step_definitions/stack_steps.rb +5 -0
- data/features/support/env.rb +1 -0
- data/lib/stack_master.rb +78 -58
- data/lib/stack_master/aws_driver/s3.rb +66 -0
- data/lib/stack_master/cli.rb +1 -0
- data/lib/stack_master/commands/apply.rb +52 -5
- data/lib/stack_master/commands/init.rb +2 -0
- data/lib/stack_master/commands/list_stacks.rb +2 -0
- data/lib/stack_master/commands/outputs.rb +2 -0
- data/lib/stack_master/commands/status.rb +3 -0
- data/lib/stack_master/parameter_resolver.rb +1 -1
- data/lib/stack_master/parameter_resolvers/secret.rb +2 -0
- data/lib/stack_master/stack.rb +20 -13
- data/lib/stack_master/stack_definition.rb +63 -13
- data/lib/stack_master/stack_differ.rb +2 -0
- data/lib/stack_master/stack_events/fetcher.rb +1 -1
- data/lib/stack_master/stack_events/presenter.rb +1 -1
- data/lib/stack_master/stack_events/streamer.rb +1 -1
- data/lib/stack_master/template_compiler.rb +3 -1
- data/lib/stack_master/template_compilers/cfndsl.rb +4 -2
- data/lib/stack_master/template_compilers/json.rb +4 -0
- data/lib/stack_master/template_compilers/sparkle_formation.rb +5 -0
- data/lib/stack_master/template_compilers/yaml.rb +4 -0
- data/lib/stack_master/test_driver/cloud_formation.rb +50 -36
- data/lib/stack_master/test_driver/s3.rb +34 -0
- data/lib/stack_master/testing.rb +1 -0
- data/lib/stack_master/utils.rb +19 -0
- data/lib/stack_master/version.rb +1 -1
- data/spec/fixtures/stack_master.yml +4 -1
- data/spec/stack_master/aws_driver/s3_spec.rb +130 -0
- data/spec/stack_master/commands/apply_spec.rb +26 -0
- data/spec/stack_master/config_spec.rb +8 -1
- data/spec/stack_master/parameter_resolver_spec.rb +5 -0
- data/spec/stack_master/sparkle_formation/user_data_file_spec.rb +2 -0
- data/spec/stack_master/stack_events/presenter_spec.rb +1 -1
- data/spec/stack_master/stack_spec.rb +23 -5
- data/spec/stack_master/template_compiler_spec.rb +1 -0
- data/spec/stack_master/template_compilers/cfndsl_spec.rb +2 -0
- data/spec/stack_master/test_driver/s3_spec.rb +17 -0
- data/stack_master.gemspec +0 -1
- metadata +10 -16
| @@ -42,7 +42,7 @@ module StackMaster | |
| 42 42 | 
             
                end
         | 
| 43 43 |  | 
| 44 44 | 
             
                def resolve_parameter_value(key, parameter_value)
         | 
| 45 | 
            -
                  return parameter_value.to_s if Numeric === parameter_value
         | 
| 45 | 
            +
                  return parameter_value.to_s if Numeric === parameter_value || parameter_value == true || parameter_value == false
         | 
| 46 46 | 
             
                  return parameter_value.join(',') if Array === parameter_value
         | 
| 47 47 | 
             
                  return parameter_value unless Hash === parameter_value
         | 
| 48 48 | 
             
                  validate_parameter_value!(key, parameter_value)
         | 
    
        data/lib/stack_master/stack.rb
    CHANGED
    
    | @@ -1,19 +1,21 @@ | |
| 1 1 | 
             
            module StackMaster
         | 
| 2 2 | 
             
              class Stack
         | 
| 3 3 | 
             
                MAX_TEMPLATE_SIZE = 51200
         | 
| 4 | 
            +
                MAX_S3_TEMPLATE_SIZE = 460800
         | 
| 4 5 |  | 
| 5 | 
            -
                 | 
| 6 | 
            +
                attr_reader :stack_name,
         | 
| 7 | 
            +
                            :region,
         | 
| 8 | 
            +
                            :stack_id,
         | 
| 9 | 
            +
                            :stack_status,
         | 
| 10 | 
            +
                            :parameters,
         | 
| 11 | 
            +
                            :template_body,
         | 
| 12 | 
            +
                            :notification_arns,
         | 
| 13 | 
            +
                            :outputs,
         | 
| 14 | 
            +
                            :stack_policy_body,
         | 
| 15 | 
            +
                            :tags,
         | 
| 16 | 
            +
                            :files
         | 
| 6 17 |  | 
| 7 | 
            -
                 | 
| 8 | 
            -
                attribute :region, String
         | 
| 9 | 
            -
                attribute :stack_id, String
         | 
| 10 | 
            -
                attribute :stack_status, String
         | 
| 11 | 
            -
                attribute :parameters, Hash
         | 
| 12 | 
            -
                attribute :template_body, String
         | 
| 13 | 
            -
                attribute :notification_arns, Array[String]
         | 
| 14 | 
            -
                attribute :outputs, Array
         | 
| 15 | 
            -
                attribute :stack_policy_body, String
         | 
| 16 | 
            -
                attribute :tags, Hash
         | 
| 18 | 
            +
                include Utils::Initializable
         | 
| 17 19 |  | 
| 18 20 | 
             
                def template_hash
         | 
| 19 21 | 
             
                  if template_body
         | 
| @@ -87,8 +89,13 @@ module StackMaster | |
| 87 89 | 
             
                      stack_policy_body: stack_policy_body)
         | 
| 88 90 | 
             
                end
         | 
| 89 91 |  | 
| 90 | 
            -
                def  | 
| 91 | 
            -
                   | 
| 92 | 
            +
                def max_template_size(use_s3)
         | 
| 93 | 
            +
                  return MAX_S3_TEMPLATE_SIZE if use_s3
         | 
| 94 | 
            +
                  MAX_TEMPLATE_SIZE
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                def too_big?(use_s3 = false)
         | 
| 98 | 
            +
                  maybe_compressed_template_body.size > max_template_size(use_s3)
         | 
| 92 99 | 
             
                end
         | 
| 93 100 |  | 
| 94 101 | 
             
                def aws_parameters
         | 
| @@ -1,34 +1,84 @@ | |
| 1 1 | 
             
            module StackMaster
         | 
| 2 2 | 
             
              class StackDefinition
         | 
| 3 | 
            -
                 | 
| 3 | 
            +
                attr_accessor :region,
         | 
| 4 | 
            +
                              :stack_name,
         | 
| 5 | 
            +
                              :template,
         | 
| 6 | 
            +
                              :tags,
         | 
| 7 | 
            +
                              :notification_arns,
         | 
| 8 | 
            +
                              :base_dir,
         | 
| 9 | 
            +
                              :secret_file,
         | 
| 10 | 
            +
                              :stack_policy_file,
         | 
| 11 | 
            +
                              :additional_parameter_lookup_dirs,
         | 
| 12 | 
            +
                              :s3,
         | 
| 13 | 
            +
                              :files
         | 
| 4 14 |  | 
| 5 | 
            -
                 | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
                   | 
| 9 | 
            -
                   | 
| 10 | 
            -
                   | 
| 11 | 
            -
                   | 
| 12 | 
            -
                   | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            +
                include Utils::Initializable
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def initialize(attributes = {})
         | 
| 18 | 
            +
                  @additional_parameter_lookup_dirs = []
         | 
| 19 | 
            +
                  @notification_arns = []
         | 
| 20 | 
            +
                  @s3 = {}
         | 
| 21 | 
            +
                  @files = []
         | 
| 22 | 
            +
                  super
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def ==(other)
         | 
| 26 | 
            +
                  self.class === other &&
         | 
| 27 | 
            +
                    @region == other.region &&
         | 
| 28 | 
            +
                    @stack_name == other.stack_name &&
         | 
| 29 | 
            +
                    @template == other.template &&
         | 
| 30 | 
            +
                    @tags == other.tags &&
         | 
| 31 | 
            +
                    @notification_arns == other.notification_arns &&
         | 
| 32 | 
            +
                    @base_dir == other.base_dir &&
         | 
| 33 | 
            +
                    @secret_file == other.secret_file &&
         | 
| 34 | 
            +
                    @stack_policy_file == other.stack_policy_file &&
         | 
| 35 | 
            +
                    @additional_parameter_lookup_dirs == other.additional_parameter_lookup_dirs &&
         | 
| 36 | 
            +
                    @s3 == other.s3
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                def template_dir
         | 
| 40 | 
            +
                  File.join(base_dir, 'templates')
         | 
| 15 41 | 
             
                end
         | 
| 16 42 |  | 
| 17 43 | 
             
                def template_file_path
         | 
| 18 | 
            -
                  File.join( | 
| 44 | 
            +
                  File.join(template_dir, template)
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                def files_dir
         | 
| 48 | 
            +
                  File.join(base_dir, 'files')
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                def s3_files
         | 
| 52 | 
            +
                  files.inject({}) do |hash, file|
         | 
| 53 | 
            +
                    path = File.join(files_dir, file)
         | 
| 54 | 
            +
                    hash[file] = {
         | 
| 55 | 
            +
                      path: path,
         | 
| 56 | 
            +
                      body: File.read(path)
         | 
| 57 | 
            +
                    }
         | 
| 58 | 
            +
                    hash
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                def s3_template_file_name
         | 
| 63 | 
            +
                  Utils.change_extension(template, 'json')
         | 
| 19 64 | 
             
                end
         | 
| 20 65 |  | 
| 21 66 | 
             
                def parameter_files
         | 
| 22 | 
            -
                  [ default_parameter_file_path, region_parameter_file_path ] | 
| 67 | 
            +
                  [ default_parameter_file_path, region_parameter_file_path, additional_parameter_lookup_file_paths ].flatten.compact
         | 
| 23 68 | 
             
                end
         | 
| 24 69 |  | 
| 25 70 | 
             
                def stack_policy_file_path
         | 
| 26 71 | 
             
                  File.join(base_dir, 'policies', stack_policy_file) if stack_policy_file
         | 
| 27 72 | 
             
                end
         | 
| 28 73 |  | 
| 74 | 
            +
                def s3_configured?
         | 
| 75 | 
            +
                  !s3.nil?
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 29 78 | 
             
                private
         | 
| 30 79 |  | 
| 31 80 | 
             
                def additional_parameter_lookup_file_paths
         | 
| 81 | 
            +
                  return unless additional_parameter_lookup_dirs
         | 
| 32 82 | 
             
                  additional_parameter_lookup_dirs.map do |a|
         | 
| 33 83 | 
             
                    File.join(base_dir, 'parameters', a, "#{underscored_stack_name}.yml")
         | 
| 34 84 | 
             
                  end
         | 
| @@ -3,7 +3,9 @@ module StackMaster | |
| 3 3 | 
             
                TemplateCompilationFailed = Class.new(RuntimeError)
         | 
| 4 4 |  | 
| 5 5 | 
             
                def self.compile(config, template_file_path)
         | 
| 6 | 
            -
                  template_compiler_for_file(template_file_path, config) | 
| 6 | 
            +
                  compiler = template_compiler_for_file(template_file_path, config)
         | 
| 7 | 
            +
                  compiler.require_dependencies
         | 
| 8 | 
            +
                  compiler.compile(template_file_path)
         | 
| 7 9 | 
             
                rescue
         | 
| 8 10 | 
             
                  raise TemplateCompilationFailed.new("Failed to compile #{template_file_path}.")
         | 
| 9 11 | 
             
                end
         | 
| @@ -1,9 +1,11 @@ | |
| 1 1 | 
             
            module StackMaster::TemplateCompilers
         | 
| 2 2 | 
             
              class Cfndsl
         | 
| 3 | 
            +
                def self.require_dependencies
         | 
| 4 | 
            +
                  require 'cfndsl'
         | 
| 5 | 
            +
                end
         | 
| 3 6 |  | 
| 4 7 | 
             
                def self.compile(template_file_path)
         | 
| 5 | 
            -
                   | 
| 6 | 
            -
                  CfnDsl.eval_file_with_extras(template_file_path).to_json
         | 
| 8 | 
            +
                  ::CfnDsl.eval_file_with_extras(template_file_path).to_json
         | 
| 7 9 | 
             
                end
         | 
| 8 10 |  | 
| 9 11 | 
             
                StackMaster::TemplateCompiler.register(:cfndsl, self)
         | 
| @@ -3,6 +3,10 @@ module StackMaster::TemplateCompilers | |
| 3 3 | 
             
                MAX_TEMPLATE_SIZE = 51200
         | 
| 4 4 | 
             
                private_constant :MAX_TEMPLATE_SIZE
         | 
| 5 5 |  | 
| 6 | 
            +
                def self.require_dependencies
         | 
| 7 | 
            +
                  require 'json'
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 6 10 | 
             
                def self.compile(template_file_path)
         | 
| 7 11 | 
             
                  template_body = File.read(template_file_path)
         | 
| 8 12 | 
             
                  if template_body.size > MAX_TEMPLATE_SIZE
         | 
| @@ -1,5 +1,10 @@ | |
| 1 1 | 
             
            module StackMaster::TemplateCompilers
         | 
| 2 2 | 
             
              class SparkleFormation
         | 
| 3 | 
            +
                def self.require_dependencies
         | 
| 4 | 
            +
                  require 'sparkle_formation'
         | 
| 5 | 
            +
                  require 'stack_master/sparkle_formation/user_data_file'
         | 
| 6 | 
            +
                end
         | 
| 7 | 
            +
             | 
| 3 8 | 
             
                def self.compile(template_file_path)
         | 
| 4 9 | 
             
                  ::SparkleFormation.sparkle_path = File.dirname(template_file_path)
         | 
| 5 10 | 
             
                  JSON.pretty_generate(::SparkleFormation.compile(template_file_path))
         | 
| @@ -1,48 +1,62 @@ | |
| 1 1 | 
             
            module StackMaster
         | 
| 2 2 | 
             
              module TestDriver
         | 
| 3 3 | 
             
                class Stack
         | 
| 4 | 
            -
                   | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 4 | 
            +
                  attr_reader :stack_id,
         | 
| 5 | 
            +
                              :stack_name,
         | 
| 6 | 
            +
                              :description,
         | 
| 7 | 
            +
                              :parameters,
         | 
| 8 | 
            +
                              :creation_time,
         | 
| 9 | 
            +
                              :last_update_time,
         | 
| 10 | 
            +
                              :stack_status,
         | 
| 11 | 
            +
                              :stack_status_reason,
         | 
| 12 | 
            +
                              :disable_rollback,
         | 
| 13 | 
            +
                              :notification_arns,
         | 
| 14 | 
            +
                              :timeout_in_minutes,
         | 
| 15 | 
            +
                              :capabilities,
         | 
| 16 | 
            +
                              :outputs,
         | 
| 17 | 
            +
                              :tags
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  include Utils::Initializable
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  def parameters
         | 
| 22 | 
            +
                    @parameters.map do |hash|
         | 
| 23 | 
            +
                      OpenStruct.new(parameter_key: hash[:parameter_key],
         | 
| 24 | 
            +
                                     parameter_value: hash[:parameter_value])
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
                  end
         | 
| 19 27 | 
             
                end
         | 
| 20 28 |  | 
| 21 29 | 
             
                class StackEvent
         | 
| 22 | 
            -
                   | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 30 | 
            +
                  attr_reader :stack_id,
         | 
| 31 | 
            +
                              :event_id,
         | 
| 32 | 
            +
                              :stack_name,
         | 
| 33 | 
            +
                              :logical_resource_id,
         | 
| 34 | 
            +
                              :physical_resource_id,
         | 
| 35 | 
            +
                              :resource_type,
         | 
| 36 | 
            +
                              :timestamp,
         | 
| 37 | 
            +
                              :resource_status,
         | 
| 38 | 
            +
                              :resource_status_reason,
         | 
| 39 | 
            +
                              :resource_properties
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  include Utils::Initializable
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  def timestamp
         | 
| 44 | 
            +
                    Time.parse(@timestamp) if @timestamp
         | 
| 45 | 
            +
                  end
         | 
| 33 46 | 
             
                end
         | 
| 34 47 |  | 
| 35 48 | 
             
                class StackResource
         | 
| 36 | 
            -
                   | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 49 | 
            +
                  attr_reader :stack_name,
         | 
| 50 | 
            +
                              :stack_id,
         | 
| 51 | 
            +
                              :logical_resource_id,
         | 
| 52 | 
            +
                              :physical_resource_id,
         | 
| 53 | 
            +
                              :resource_type,
         | 
| 54 | 
            +
                              :timestamp,
         | 
| 55 | 
            +
                              :resource_status,
         | 
| 56 | 
            +
                              :resource_status_reason,
         | 
| 57 | 
            +
                              :description
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  include Utils::Initializable
         | 
| 46 60 | 
             
                end
         | 
| 47 61 |  | 
| 48 62 | 
             
                class CloudFormation
         | 
| @@ -0,0 +1,34 @@ | |
| 1 | 
            +
            module StackMaster
         | 
| 2 | 
            +
              module TestDriver
         | 
| 3 | 
            +
                class S3
         | 
| 4 | 
            +
                  def initialize
         | 
| 5 | 
            +
                    reset
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def set_region(_)
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  def reset
         | 
| 12 | 
            +
                    @files = Hash.new { |hash, key| hash[key] = Hash.new }
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def upload_files(bucket: nil, prefix: nil, region: nil, files: {})
         | 
| 16 | 
            +
                    return if files.empty?
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    files.each do |template, file|
         | 
| 19 | 
            +
                      object_key = [prefix, template].compact.join('/')
         | 
| 20 | 
            +
                      @files[bucket][object_key] = file[:body]
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  def url(bucket:, prefix:, region:, template:)
         | 
| 25 | 
            +
                    ["https://s3-#{region}.amazonaws.com", bucket, prefix, template].compact.join('/')
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  # test only method
         | 
| 29 | 
            +
                  def find_file(bucket:, object_key:)
         | 
| 30 | 
            +
                    @files[bucket][object_key]
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
            end
         | 
    
        data/lib/stack_master/testing.rb
    CHANGED
    
    
    
        data/lib/stack_master/utils.rb
    CHANGED
    
    | @@ -1,7 +1,26 @@ | |
| 1 1 | 
             
            module StackMaster
         | 
| 2 2 | 
             
              module Utils
         | 
| 3 | 
            +
                module Initializable
         | 
| 4 | 
            +
                  def initialize(attributes = {})
         | 
| 5 | 
            +
                    self.attributes = attributes
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def attributes=(attributes)
         | 
| 9 | 
            +
                    attributes.each do |k, v|
         | 
| 10 | 
            +
                      instance_variable_set("@#{k}", v)
         | 
| 11 | 
            +
                    end
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 3 15 | 
             
                extend self
         | 
| 4 16 |  | 
| 17 | 
            +
                def change_extension(file_name, extension)
         | 
| 18 | 
            +
                  [
         | 
| 19 | 
            +
                    File.basename(file_name, '.*'),
         | 
| 20 | 
            +
                    extension
         | 
| 21 | 
            +
                  ].join('.')
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 5 24 | 
             
                def hash_to_aws_parameters(params)
         | 
| 6 25 | 
             
                  params.inject([]) do |params, (key, value)|
         | 
| 7 26 | 
             
                    params << { parameter_key: key, parameter_value: value }
         |