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
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 24f9543391ced08e41349c5cd75aaecacce6aab2
         | 
| 4 | 
            +
              data.tar.gz: 0130dc01c3bfacea757155caf6896aef4daaebaf
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 95bf919587f2d04956c97ef82caa0f9d79747b2272e1c97075549950a5d5606dbf084cfba5ef65a4e61935a4d3c11fd13c06183f02e9305f55e8f98353ded7be
         | 
| 7 | 
            +
              data.tar.gz: d5523800109d4f3ac1a5a04a4f2f3246ad51d2014b76234c2b35c54f18e39b24a8b7fe4e5b9696682d1b299f8bf5b969961b0d49355c54381d2cc247e7cde26d
         | 
    
        data/README.md
    CHANGED
    
    | @@ -95,6 +95,28 @@ stacks: | |
| 95 95 | 
             
                    purpose: vpc
         | 
| 96 96 | 
             
            ```
         | 
| 97 97 |  | 
| 98 | 
            +
            ## S3
         | 
| 99 | 
            +
             | 
| 100 | 
            +
            StackMaster can optionally use S3 to store the templates before creating a stack.
         | 
| 101 | 
            +
            This requires to configure an S3 bucket in stack_master.yml:
         | 
| 102 | 
            +
             | 
| 103 | 
            +
            ```yaml
         | 
| 104 | 
            +
            stack_defaults:
         | 
| 105 | 
            +
              s3:
         | 
| 106 | 
            +
                bucket: my_bucket_name
         | 
| 107 | 
            +
                prefix: cfn_templates/my-awesome-app
         | 
| 108 | 
            +
                region: us-west-2
         | 
| 109 | 
            +
            ```
         | 
| 110 | 
            +
             | 
| 111 | 
            +
            Additional files can be configured to be uploaded to S3 alongside the templates:
         | 
| 112 | 
            +
            ```yaml
         | 
| 113 | 
            +
            stacks:
         | 
| 114 | 
            +
              production:
         | 
| 115 | 
            +
                myapp-vpc:
         | 
| 116 | 
            +
                  template: myapp_vpc.rb
         | 
| 117 | 
            +
                  files:
         | 
| 118 | 
            +
                    - userdata.sh
         | 
| 119 | 
            +
            ```
         | 
| 98 120 | 
             
            ## Directories
         | 
| 99 121 |  | 
| 100 122 | 
             
            - `templates` - CloudFormation, SparkleFormation or CfnDsl templates.
         | 
    
        data/features/apply.feature
    CHANGED
    
    | @@ -90,7 +90,7 @@ Feature: Apply command | |
| 90 90 | 
             
                  | Parameters diff: |
         | 
| 91 91 | 
             
                  | KeyName: my-key  |
         | 
| 92 92 | 
             
                  | aborted          |
         | 
| 93 | 
            -
                And the output should not match /2020-10-29 00:00:00 \+[0-9]{4} myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE/ | 
| 93 | 
            +
                And the output should not match /2020-10-29 00:00:00 \+[0-9]{4} myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE/
         | 
| 94 94 | 
             
                Then the exit status should be 0
         | 
| 95 95 |  | 
| 96 96 | 
             
              Scenario: Run apply with region only and create 2 stacks
         | 
| @@ -0,0 +1,106 @@ | |
| 1 | 
            +
            Feature: Apply command
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              Background:
         | 
| 4 | 
            +
                Given a file named "stack_master.yml" with:
         | 
| 5 | 
            +
                  """
         | 
| 6 | 
            +
                  stack_defaults:
         | 
| 7 | 
            +
                    s3:
         | 
| 8 | 
            +
                      bucket: my-bucket
         | 
| 9 | 
            +
                      region: us-east-1
         | 
| 10 | 
            +
                      prefix: cfn_templates/my-app
         | 
| 11 | 
            +
                  stacks:
         | 
| 12 | 
            +
                    us_east_1:
         | 
| 13 | 
            +
                      myapp_vpc:
         | 
| 14 | 
            +
                        template: myapp_vpc.rb
         | 
| 15 | 
            +
                        files:
         | 
| 16 | 
            +
                          - user_data.sh
         | 
| 17 | 
            +
                  """
         | 
| 18 | 
            +
                And a directory named "parameters"
         | 
| 19 | 
            +
                And a file named "parameters/myapp_vpc.yml" with:
         | 
| 20 | 
            +
                  """
         | 
| 21 | 
            +
                  KeyName: my-key
         | 
| 22 | 
            +
                  """
         | 
| 23 | 
            +
                And a file named "parameters/myapp_web.yml" with:
         | 
| 24 | 
            +
                  """
         | 
| 25 | 
            +
                  VpcId: vpc-blah
         | 
| 26 | 
            +
                  """
         | 
| 27 | 
            +
                And a directory named "templates"
         | 
| 28 | 
            +
                And a file named "files/user_data.sh" with:
         | 
| 29 | 
            +
                  """
         | 
| 30 | 
            +
                  #!/bin/bash
         | 
| 31 | 
            +
                  echo "HI"
         | 
| 32 | 
            +
                  """
         | 
| 33 | 
            +
                And a file named "templates/myapp_vpc.rb" with:
         | 
| 34 | 
            +
                  """
         | 
| 35 | 
            +
                  SparkleFormation.new(:myapp_vpc) do
         | 
| 36 | 
            +
                    description "Test template"
         | 
| 37 | 
            +
                    set!('AWSTemplateFormatVersion', '2010-09-09')
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    parameters.key_name do
         | 
| 40 | 
            +
                      description 'Key name'
         | 
| 41 | 
            +
                      type 'String'
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    resources.vpc do
         | 
| 45 | 
            +
                      type 'AWS::EC2::VPC'
         | 
| 46 | 
            +
                      properties do
         | 
| 47 | 
            +
                        cidr_block '10.200.0.0/16'
         | 
| 48 | 
            +
                      end
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                    outputs do
         | 
| 52 | 
            +
                      vpc_id do
         | 
| 53 | 
            +
                        description 'A VPC ID'
         | 
| 54 | 
            +
                        value ref!(:vpc)
         | 
| 55 | 
            +
                      end
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
                  """
         | 
| 59 | 
            +
             | 
| 60 | 
            +
              Scenario: Run apply and create a new stack
         | 
| 61 | 
            +
                Given I stub the following stack events:
         | 
| 62 | 
            +
                  | stack_id | event_id | stack_name | logical_resource_id | resource_status | resource_type              | timestamp           |
         | 
| 63 | 
            +
                  | 1        | 1        | myapp-vpc  | TestSg              | CREATE_COMPLETE | AWS::EC2::SecurityGroup    | 2020-10-29 00:00:00 |
         | 
| 64 | 
            +
                  | 1        | 1        | myapp-vpc  | myapp-vpc           | CREATE_COMPLETE | AWS::CloudFormation::Stack | 2020-10-29 00:00:00 |
         | 
| 65 | 
            +
                When I run `stack_master apply us-east-1 myapp-vpc --trace`
         | 
| 66 | 
            +
                And the output should contain all of these lines:
         | 
| 67 | 
            +
                  | Stack diff:                                                                    |
         | 
| 68 | 
            +
                  | +    "Vpc": {                                                                  |
         | 
| 69 | 
            +
                  | Parameters diff:                                                               |
         | 
| 70 | 
            +
                  | KeyName: my-key                                                                |
         | 
| 71 | 
            +
                And the output should match /2020-10-29 00:00:00 \+[0-9]{4} myapp-vpc AWS::CloudFormation::Stack CREATE_COMPLETE/
         | 
| 72 | 
            +
                And an S3 file in bucket "my-bucket" with key "cfn_templates/my-app/myapp_vpc.json" exists with content:
         | 
| 73 | 
            +
                  """
         | 
| 74 | 
            +
                  {
         | 
| 75 | 
            +
                    "Description": "Test template",
         | 
| 76 | 
            +
                    "AWSTemplateFormatVersion": "2010-09-09",
         | 
| 77 | 
            +
                    "Parameters": {
         | 
| 78 | 
            +
                      "KeyName": {
         | 
| 79 | 
            +
                        "Description": "Key name",
         | 
| 80 | 
            +
                        "Type": "String"
         | 
| 81 | 
            +
                      }
         | 
| 82 | 
            +
                    },
         | 
| 83 | 
            +
                    "Resources": {
         | 
| 84 | 
            +
                      "Vpc": {
         | 
| 85 | 
            +
                        "Type": "AWS::EC2::VPC",
         | 
| 86 | 
            +
                        "Properties": {
         | 
| 87 | 
            +
                          "CidrBlock": "10.200.0.0/16"
         | 
| 88 | 
            +
                        }
         | 
| 89 | 
            +
                      }
         | 
| 90 | 
            +
                    },
         | 
| 91 | 
            +
                    "Outputs": {
         | 
| 92 | 
            +
                      "VpcId": {
         | 
| 93 | 
            +
                        "Description": "A VPC ID",
         | 
| 94 | 
            +
                        "Value": {
         | 
| 95 | 
            +
                          "Ref": "Vpc"
         | 
| 96 | 
            +
                        }
         | 
| 97 | 
            +
                      }
         | 
| 98 | 
            +
                    }
         | 
| 99 | 
            +
                  }
         | 
| 100 | 
            +
                  """
         | 
| 101 | 
            +
                And an S3 file in bucket "my-bucket" with key "cfn_templates/my-app/user_data.sh" exists with content:
         | 
| 102 | 
            +
                  """
         | 
| 103 | 
            +
                  #!/bin/bash
         | 
| 104 | 
            +
                  echo "HI"
         | 
| 105 | 
            +
                  """
         | 
| 106 | 
            +
                Then the exit status should be 0
         | 
| @@ -64,3 +64,8 @@ end | |
| 64 64 | 
             
            Given(/^I stub CloudFormation validate calls to fail validation with message "([^"]*)"$/) do |message|
         | 
| 65 65 | 
             
              allow(StackMaster.cloud_formation_driver).to receive(:validate_template).and_raise(Aws::CloudFormation::Errors::ValidationError.new('', message))
         | 
| 66 66 | 
             
            end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
            When(/^an S3 file in bucket "([^"]*)" with key "([^"]*)" exists with content:$/) do |bucket, key, body|
         | 
| 69 | 
            +
              file = StackMaster.s3_driver.find_file(bucket: bucket, object_key: key)
         | 
| 70 | 
            +
              expect(file).to eq body
         | 
| 71 | 
            +
            end
         | 
    
        data/features/support/env.rb
    CHANGED
    
    
    
        data/lib/stack_master.rb
    CHANGED
    
    | @@ -1,69 +1,82 @@ | |
| 1 1 | 
             
            require "commander"
         | 
| 2 2 | 
             
            require "yaml"
         | 
| 3 | 
            -
            require "virtus"
         | 
| 4 3 | 
             
            require "aws-sdk"
         | 
| 5 | 
            -
            require "diffy"
         | 
| 6 4 | 
             
            require "colorize"
         | 
| 7 | 
            -
            require "table_print"
         | 
| 8 5 | 
             
            require 'active_support/core_ext/string'
         | 
| 9 | 
            -
            require "erb"
         | 
| 10 | 
            -
            require 'sparkle_formation'
         | 
| 11 | 
            -
            require 'dotgpg'
         | 
| 12 | 
            -
            require 'ruby-progressbar'
         | 
| 13 | 
            -
             | 
| 14 | 
            -
            require "stack_master/ctrl_c"
         | 
| 15 | 
            -
            require "stack_master/sparkle_formation/user_data_file"
         | 
| 16 | 
            -
            require "stack_master/command"
         | 
| 17 | 
            -
            require "stack_master/version"
         | 
| 18 | 
            -
            require "stack_master/stack"
         | 
| 19 | 
            -
            require "stack_master/prompter"
         | 
| 20 | 
            -
            require "stack_master/aws_driver/cloud_formation"
         | 
| 21 | 
            -
            require "stack_master/test_driver/cloud_formation"
         | 
| 22 | 
            -
            require "stack_master/stack_events/fetcher"
         | 
| 23 | 
            -
            require "stack_master/stack_events/presenter"
         | 
| 24 | 
            -
            require "stack_master/stack_events/streamer"
         | 
| 25 | 
            -
            require "stack_master/stack_states"
         | 
| 26 | 
            -
            require "stack_master/stack_status"
         | 
| 27 | 
            -
            require "stack_master/sns_topic_finder"
         | 
| 28 | 
            -
            require "stack_master/security_group_finder"
         | 
| 29 | 
            -
            require "stack_master/parameter_loader"
         | 
| 30 | 
            -
            require "stack_master/parameter_resolver"
         | 
| 31 | 
            -
            require "stack_master/resolver_array"
         | 
| 32 | 
            -
            require "stack_master/parameter_resolvers/ami_finder"
         | 
| 33 | 
            -
            require "stack_master/parameter_resolvers/stack_output"
         | 
| 34 | 
            -
            require "stack_master/parameter_resolvers/secret"
         | 
| 35 | 
            -
            require "stack_master/parameter_resolvers/sns_topic_name"
         | 
| 36 | 
            -
            require "stack_master/parameter_resolvers/security_group"
         | 
| 37 | 
            -
            require "stack_master/parameter_resolvers/latest_ami_by_tags"
         | 
| 38 | 
            -
            require "stack_master/parameter_resolvers/latest_ami"
         | 
| 39 | 
            -
            require "stack_master/utils"
         | 
| 40 | 
            -
            require "stack_master/config"
         | 
| 41 | 
            -
            require "stack_master/paged_response_accumulator"
         | 
| 42 | 
            -
            require "stack_master/stack_definition"
         | 
| 43 | 
            -
            require "stack_master/template_compiler"
         | 
| 44 | 
            -
            require "stack_master/template_compilers/sparkle_formation"
         | 
| 45 | 
            -
            require "stack_master/template_compilers/json"
         | 
| 46 | 
            -
            require "stack_master/template_compilers/yaml"
         | 
| 47 | 
            -
            require "stack_master/template_compilers/cfndsl"
         | 
| 48 | 
            -
            require "stack_master/commands/terminal_helper"
         | 
| 49 | 
            -
            require "stack_master/commands/apply"
         | 
| 50 | 
            -
            require "stack_master/change_set"
         | 
| 51 | 
            -
            require "stack_master/commands/events"
         | 
| 52 | 
            -
            require "stack_master/commands/outputs"
         | 
| 53 | 
            -
            require "stack_master/commands/init"
         | 
| 54 | 
            -
            require "stack_master/commands/diff"
         | 
| 55 | 
            -
            require "stack_master/commands/list_stacks"
         | 
| 56 | 
            -
            require "stack_master/commands/validate"
         | 
| 57 | 
            -
            require "stack_master/commands/resources"
         | 
| 58 | 
            -
            require "stack_master/commands/delete"
         | 
| 59 | 
            -
            require "stack_master/commands/status"
         | 
| 60 | 
            -
            require "stack_master/stack_differ"
         | 
| 61 | 
            -
            require "stack_master/validator"
         | 
| 62 | 
            -
            require "stack_master/cli"
         | 
| 63 6 |  | 
| 64 7 | 
             
            module StackMaster
         | 
| 65 8 | 
             
              extend self
         | 
| 66 9 |  | 
| 10 | 
            +
              autoload :Initializable, 'stack_master/utils/initializable'
         | 
| 11 | 
            +
              autoload :ChangeSet, 'stack_master/change_set'
         | 
| 12 | 
            +
              autoload :CLI, 'stack_master/cli'
         | 
| 13 | 
            +
              autoload :CtrlC, 'stack_master/ctrl_c'
         | 
| 14 | 
            +
              autoload :Command, 'stack_master/command'
         | 
| 15 | 
            +
              autoload :Version, 'stack_master/version'
         | 
| 16 | 
            +
              autoload :Stack, 'stack_master/stack'
         | 
| 17 | 
            +
              autoload :Prompter, 'stack_master/prompter'
         | 
| 18 | 
            +
              autoload :StackStates, 'stack_master/stack_states'
         | 
| 19 | 
            +
              autoload :StackStatus, 'stack_master/stack_status'
         | 
| 20 | 
            +
              autoload :SnsTopicFinder, 'stack_master/sns_topic_finder'
         | 
| 21 | 
            +
              autoload :SecurityGroupFinder, 'stack_master/security_group_finder'
         | 
| 22 | 
            +
              autoload :ParameterLoader, 'stack_master/parameter_loader'
         | 
| 23 | 
            +
              autoload :ParameterResolver, 'stack_master/parameter_resolver'
         | 
| 24 | 
            +
              autoload :ResolverArray, 'stack_master/resolver_array'
         | 
| 25 | 
            +
              autoload :Resolver, 'stack_master/resolver_array'
         | 
| 26 | 
            +
              autoload :Utils, 'stack_master/utils'
         | 
| 27 | 
            +
              autoload :Config, 'stack_master/config'
         | 
| 28 | 
            +
              autoload :PagedResponseAccumulator, 'stack_master/paged_response_accumulator'
         | 
| 29 | 
            +
              autoload :StackDefinition, 'stack_master/stack_definition'
         | 
| 30 | 
            +
              autoload :TemplateCompiler, 'stack_master/template_compiler'
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              autoload :StackDiffer, 'stack_master/stack_differ'
         | 
| 33 | 
            +
              autoload :Validator, 'stack_master/validator'
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              require 'stack_master/template_compilers/sparkle_formation'
         | 
| 36 | 
            +
              require 'stack_master/template_compilers/json'
         | 
| 37 | 
            +
              require 'stack_master/template_compilers/yaml'
         | 
| 38 | 
            +
              require 'stack_master/template_compilers/cfndsl'
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              module Commands
         | 
| 41 | 
            +
                autoload :TerminalHelper, 'stack_master/commands/terminal_helper'
         | 
| 42 | 
            +
                autoload :Apply, 'stack_master/commands/apply'
         | 
| 43 | 
            +
                autoload :Events, 'stack_master/commands/events'
         | 
| 44 | 
            +
                autoload :Outputs, 'stack_master/commands/outputs'
         | 
| 45 | 
            +
                autoload :Init, 'stack_master/commands/init'
         | 
| 46 | 
            +
                autoload :Diff, 'stack_master/commands/diff'
         | 
| 47 | 
            +
                autoload :ListStacks, 'stack_master/commands/list_stacks'
         | 
| 48 | 
            +
                autoload :Validate, 'stack_master/commands/validate'
         | 
| 49 | 
            +
                autoload :Resources, 'stack_master/commands/resources'
         | 
| 50 | 
            +
                autoload :Delete, 'stack_master/commands/delete'
         | 
| 51 | 
            +
                autoload :Status, 'stack_master/commands/status'
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
              module ParameterResolvers
         | 
| 55 | 
            +
                autoload :AmiFinder, 'stack_master/parameter_resolvers/ami_finder'
         | 
| 56 | 
            +
                autoload :StackOutput, 'stack_master/parameter_resolvers/stack_output'
         | 
| 57 | 
            +
                autoload :Secret, 'stack_master/parameter_resolvers/secret'
         | 
| 58 | 
            +
                autoload :SnsTopicName, 'stack_master/parameter_resolvers/sns_topic_name'
         | 
| 59 | 
            +
                autoload :SecurityGroup, 'stack_master/parameter_resolvers/security_group'
         | 
| 60 | 
            +
                autoload :LatestAmiByTags, 'stack_master/parameter_resolvers/latest_ami_by_tags'
         | 
| 61 | 
            +
                autoload :LatestAmi, 'stack_master/parameter_resolvers/latest_ami'
         | 
| 62 | 
            +
              end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
              module AwsDriver
         | 
| 65 | 
            +
                autoload :CloudFormation, 'stack_master/aws_driver/cloud_formation'
         | 
| 66 | 
            +
                autoload :S3, 'stack_master/aws_driver/s3'
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
              module TestDriver
         | 
| 70 | 
            +
                autoload :CloudFormation, 'stack_master/test_driver/cloud_formation'
         | 
| 71 | 
            +
                autoload :S3, 'stack_master/test_driver/s3'
         | 
| 72 | 
            +
              end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
              module StackEvents
         | 
| 75 | 
            +
                autoload :Fetcher, 'stack_master/stack_events/fetcher'
         | 
| 76 | 
            +
                autoload :Presenter, 'stack_master/stack_events/presenter'
         | 
| 77 | 
            +
                autoload :Streamer, 'stack_master/stack_events/streamer'
         | 
| 78 | 
            +
              end
         | 
| 79 | 
            +
             | 
| 67 80 | 
             
              def interactive?
         | 
| 68 81 | 
             
                !non_interactive?
         | 
| 69 82 | 
             
              end
         | 
| @@ -106,6 +119,14 @@ module StackMaster | |
| 106 119 | 
             
                @cloud_formation_driver = value
         | 
| 107 120 | 
             
              end
         | 
| 108 121 |  | 
| 122 | 
            +
              def s3_driver
         | 
| 123 | 
            +
                @s3_driver ||= AwsDriver::S3.new
         | 
| 124 | 
            +
              end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
              def s3_driver=(value)
         | 
| 127 | 
            +
                @s3_driver = value
         | 
| 128 | 
            +
              end
         | 
| 129 | 
            +
             | 
| 109 130 | 
             
              def stdout
         | 
| 110 131 | 
             
                @stdout || $stdout
         | 
| 111 132 | 
             
              end
         | 
| @@ -126,4 +147,3 @@ module StackMaster | |
| 126 147 | 
             
                @stderr = io
         | 
| 127 148 | 
             
              end
         | 
| 128 149 | 
             
            end
         | 
| 129 | 
            -
             | 
| @@ -0,0 +1,66 @@ | |
| 1 | 
            +
            require 'digest/md5'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module StackMaster
         | 
| 4 | 
            +
              module AwsDriver
         | 
| 5 | 
            +
                class S3ConfigurationError < StandardError; end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                class S3
         | 
| 8 | 
            +
                  def set_region(region)
         | 
| 9 | 
            +
                    @region = region
         | 
| 10 | 
            +
                    @s3 = nil
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def upload_files(bucket: nil, prefix: nil, region: nil, files: {})
         | 
| 14 | 
            +
                    raise StackMaster::AwsDriver::S3ConfigurationError, 'A bucket must be specified in order to use S3' unless bucket
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    return if files.empty?
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    s3 = new_s3_client(region: region)
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    current_objects = s3.list_objects(
         | 
| 21 | 
            +
                      prefix: prefix,
         | 
| 22 | 
            +
                      bucket: bucket
         | 
| 23 | 
            +
                    ).map(&:contents).flatten.inject({}){|h,obj|
         | 
| 24 | 
            +
                      h.merge(obj.key => obj)
         | 
| 25 | 
            +
                    }
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    StackMaster.stdout.puts "Uploading files to S3:"
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    files.each do |template, file|
         | 
| 30 | 
            +
                      body = file.fetch(:body)
         | 
| 31 | 
            +
                      path = file.fetch(:path)
         | 
| 32 | 
            +
                      object_key = template.dup
         | 
| 33 | 
            +
                      object_key.prepend("#{prefix}/") if prefix
         | 
| 34 | 
            +
                      compiled_template_md5 = Digest::MD5.hexdigest(body).to_s
         | 
| 35 | 
            +
                      s3_md5 = current_objects[object_key] ? current_objects[object_key].etag.gsub("\"", '') : nil
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                      next if compiled_template_md5 == s3_md5
         | 
| 38 | 
            +
                      s3_uri = "s3://#{bucket}/#{object_key}"
         | 
| 39 | 
            +
                      StackMaster.stdout.print "- #{File.basename(path)} => #{s3_uri} "
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                      s3.put_object(
         | 
| 42 | 
            +
                        bucket: bucket,
         | 
| 43 | 
            +
                        key: object_key,
         | 
| 44 | 
            +
                        body: body,
         | 
| 45 | 
            +
                        metadata: { md5: compiled_template_md5 }
         | 
| 46 | 
            +
                      )
         | 
| 47 | 
            +
                      StackMaster.stdout.puts "done."
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  def url(bucket:, prefix:, region:, template:)
         | 
| 52 | 
            +
                    if region == 'us-east-1'
         | 
| 53 | 
            +
                      ["https://s3.amazonaws.com", bucket, prefix, template].compact.join('/')
         | 
| 54 | 
            +
                    else
         | 
| 55 | 
            +
                      ["https://s3-#{region}.amazonaws.com", bucket, prefix, template].compact.join('/')
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  private
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  def new_s3_client(region: nil)
         | 
| 62 | 
            +
                    Aws::S3::Client.new(region: region || @region)
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
            end
         | 
    
        data/lib/stack_master/cli.rb
    CHANGED
    
    
| @@ -4,10 +4,11 @@ module StackMaster | |
| 4 4 | 
             
                  include Command
         | 
| 5 5 | 
             
                  include Commander::UI
         | 
| 6 6 | 
             
                  include StackMaster::Prompter
         | 
| 7 | 
            -
                  TEMPLATE_TOO_LARGE_ERROR_MESSAGE = 'The (space compressed) stack is larger than the limit set by AWS. See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html'
         | 
| 7 | 
            +
                  TEMPLATE_TOO_LARGE_ERROR_MESSAGE = 'The (space compressed) stack is larger than the limit set by AWS. See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html'.freeze
         | 
| 8 8 |  | 
| 9 9 | 
             
                  def initialize(config, stack_definition, options = {})
         | 
| 10 10 | 
             
                    @config = config
         | 
| 11 | 
            +
                    @s3_config = stack_definition.s3
         | 
| 11 12 | 
             
                    @stack_definition = stack_definition
         | 
| 12 13 | 
             
                    @from_time = Time.now
         | 
| 13 14 | 
             
                  end
         | 
| @@ -26,6 +27,10 @@ module StackMaster | |
| 26 27 | 
             
                    @cf ||= StackMaster.cloud_formation_driver
         | 
| 27 28 | 
             
                  end
         | 
| 28 29 |  | 
| 30 | 
            +
                  def s3
         | 
| 31 | 
            +
                    @s3 ||= StackMaster.s3_driver
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 29 34 | 
             
                  def stack
         | 
| 30 35 | 
             
                    @stack ||= Stack.find(region, stack_name)
         | 
| 31 36 | 
             
                  end
         | 
| @@ -38,6 +43,10 @@ module StackMaster | |
| 38 43 | 
             
                    !stack.nil?
         | 
| 39 44 | 
             
                  end
         | 
| 40 45 |  | 
| 46 | 
            +
                  def use_s3?
         | 
| 47 | 
            +
                    !@s3_config.empty?
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
             | 
| 41 50 | 
             
                  def diff_stacks
         | 
| 42 51 | 
             
                    StackDiffer.new(proposed_stack, stack).output_diff
         | 
| 43 52 | 
             
                  end
         | 
| @@ -51,9 +60,10 @@ module StackMaster | |
| 51 60 | 
             
                  end
         | 
| 52 61 |  | 
| 53 62 | 
             
                  def create_stack
         | 
| 54 | 
            -
                    unless ask?( | 
| 63 | 
            +
                    unless ask?('Create stack (y/n)? ')
         | 
| 55 64 | 
             
                      failed!("Stack creation aborted")
         | 
| 56 65 | 
             
                    end
         | 
| 66 | 
            +
                    upload_files
         | 
| 57 67 | 
             
                    cf.create_stack(stack_options.merge(tags: proposed_stack.aws_tags))
         | 
| 58 68 | 
             
                  end
         | 
| 59 69 |  | 
| @@ -73,17 +83,54 @@ module StackMaster | |
| 73 83 | 
             
                      ChangeSet.delete(@change_set.id)
         | 
| 74 84 | 
             
                      halt! "Stack update aborted"
         | 
| 75 85 | 
             
                    end
         | 
| 86 | 
            +
                    upload_files
         | 
| 76 87 | 
             
                    execute_change_set
         | 
| 77 88 | 
             
                  end
         | 
| 78 89 |  | 
| 90 | 
            +
                  def upload_files
         | 
| 91 | 
            +
                    return unless use_s3?
         | 
| 92 | 
            +
                    s3.upload_files(s3_options)
         | 
| 93 | 
            +
                  end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  def template_method
         | 
| 96 | 
            +
                    use_s3? ? :template_url : :template_body
         | 
| 97 | 
            +
                  end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                  def template_value
         | 
| 100 | 
            +
                    if use_s3?
         | 
| 101 | 
            +
                      s3.url(bucket: @s3_config['bucket'], prefix: @s3_config['prefix'], region: @s3_config['region'], template: @stack_definition.s3_template_file_name)
         | 
| 102 | 
            +
                    else
         | 
| 103 | 
            +
                      proposed_stack.maybe_compressed_template_body
         | 
| 104 | 
            +
                    end
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                  def files_to_upload
         | 
| 108 | 
            +
                    return {} unless use_s3?
         | 
| 109 | 
            +
                    @stack_definition.s3_files.tap do |files|
         | 
| 110 | 
            +
                      files[@stack_definition.s3_template_file_name] = {
         | 
| 111 | 
            +
                        path: @stack_definition.template_file_path,
         | 
| 112 | 
            +
                        body: proposed_stack.maybe_compressed_template_body
         | 
| 113 | 
            +
                      }
         | 
| 114 | 
            +
                    end
         | 
| 115 | 
            +
                  end
         | 
| 116 | 
            +
             | 
| 79 117 | 
             
                  def stack_options
         | 
| 80 118 | 
             
                    {
         | 
| 81 119 | 
             
                      stack_name: stack_name,
         | 
| 82 | 
            -
                      template_body: proposed_stack.maybe_compressed_template_body,
         | 
| 83 120 | 
             
                      parameters: proposed_stack.aws_parameters,
         | 
| 84 121 | 
             
                      capabilities: ['CAPABILITY_IAM'],
         | 
| 85 122 | 
             
                      notification_arns: proposed_stack.notification_arns,
         | 
| 86 | 
            -
                      stack_policy_body: proposed_stack.stack_policy_body
         | 
| 123 | 
            +
                      stack_policy_body: proposed_stack.stack_policy_body,
         | 
| 124 | 
            +
                      template_method => template_value
         | 
| 125 | 
            +
                    }
         | 
| 126 | 
            +
                  end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                  def s3_options
         | 
| 129 | 
            +
                    {
         | 
| 130 | 
            +
                      bucket: @s3_config['bucket'],
         | 
| 131 | 
            +
                      prefix: @s3_config['prefix'],
         | 
| 132 | 
            +
                      region: @s3_config['region'],
         | 
| 133 | 
            +
                      files: files_to_upload
         | 
| 87 134 | 
             
                    }
         | 
| 88 135 | 
             
                  end
         | 
| 89 136 |  | 
| @@ -110,7 +157,7 @@ module StackMaster | |
| 110 157 | 
             
                  end
         | 
| 111 158 |  | 
| 112 159 | 
             
                  def ensure_valid_template_body_size!
         | 
| 113 | 
            -
                    if proposed_stack.too_big?
         | 
| 160 | 
            +
                    if proposed_stack.too_big?(use_s3?)
         | 
| 114 161 | 
             
                      failed! TEMPLATE_TOO_LARGE_ERROR_MESSAGE
         | 
| 115 162 | 
             
                    end
         | 
| 116 163 | 
             
                  end
         |