cf_deployer 1.2.8
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 +7 -0
- data/.gitignore +29 -0
- data/ChangeLog.md +16 -0
- data/DETAILS.md +268 -0
- data/FAQ.md +61 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +51 -0
- data/LICENSE +22 -0
- data/QUICKSTART.md +96 -0
- data/README.md +36 -0
- data/Rakefile +32 -0
- data/bin/cf_deploy +10 -0
- data/cf_deployer.gemspec +23 -0
- data/lib/cf_deployer/application.rb +74 -0
- data/lib/cf_deployer/application_error.rb +4 -0
- data/lib/cf_deployer/aws_constants.rb +3 -0
- data/lib/cf_deployer/cli.rb +111 -0
- data/lib/cf_deployer/component.rb +103 -0
- data/lib/cf_deployer/config_loader.rb +189 -0
- data/lib/cf_deployer/config_validation.rb +138 -0
- data/lib/cf_deployer/defaults.rb +10 -0
- data/lib/cf_deployer/deployment_strategy/auto_scaling_group_swap.rb +102 -0
- data/lib/cf_deployer/deployment_strategy/base.rb +88 -0
- data/lib/cf_deployer/deployment_strategy/blue_green.rb +70 -0
- data/lib/cf_deployer/deployment_strategy/cname_swap.rb +108 -0
- data/lib/cf_deployer/deployment_strategy/create_or_update.rb +57 -0
- data/lib/cf_deployer/driver/auto_scaling_group.rb +86 -0
- data/lib/cf_deployer/driver/cloud_formation_driver.rb +85 -0
- data/lib/cf_deployer/driver/dry_run.rb +27 -0
- data/lib/cf_deployer/driver/elb_driver.rb +17 -0
- data/lib/cf_deployer/driver/instance.rb +29 -0
- data/lib/cf_deployer/driver/route53_driver.rb +79 -0
- data/lib/cf_deployer/driver/verisign_driver.rb +21 -0
- data/lib/cf_deployer/hook.rb +32 -0
- data/lib/cf_deployer/logger.rb +34 -0
- data/lib/cf_deployer/stack.rb +154 -0
- data/lib/cf_deployer/status_presenter.rb +195 -0
- data/lib/cf_deployer/version.rb +3 -0
- data/lib/cf_deployer.rb +97 -0
- data/spec/fakes/instance.rb +32 -0
- data/spec/fakes/route53_client.rb +23 -0
- data/spec/fakes/stack.rb +65 -0
- data/spec/functional/deploy_spec.rb +73 -0
- data/spec/functional/kill_inactive_spec.rb +57 -0
- data/spec/functional_spec_helper.rb +3 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/unit/application_spec.rb +191 -0
- data/spec/unit/component_spec.rb +142 -0
- data/spec/unit/config_loader_spec.rb +356 -0
- data/spec/unit/config_validation_spec.rb +480 -0
- data/spec/unit/deployment_strategy/auto_scaling_group_swap_spec.rb +435 -0
- data/spec/unit/deployment_strategy/base_spec.rb +44 -0
- data/spec/unit/deployment_strategy/cname_swap_spec.rb +294 -0
- data/spec/unit/deployment_strategy/create_or_update_spec.rb +113 -0
- data/spec/unit/deployment_strategy/deployment_strategy_spec.rb +29 -0
- data/spec/unit/driver/auto_scaling_group_spec.rb +127 -0
- data/spec/unit/driver/cloud_formation_spec.rb +32 -0
- data/spec/unit/driver/elb_spec.rb +11 -0
- data/spec/unit/driver/instance_spec.rb +30 -0
- data/spec/unit/driver/route53_spec.rb +85 -0
- data/spec/unit/driver/verisign_spec.rb +18 -0
- data/spec/unit/hook_spec.rb +64 -0
- data/spec/unit/stack_spec.rb +150 -0
- data/spec/unit/status_presenter_spec.rb +108 -0
- metadata +197 -0
    
        data/spec/fakes/stack.rb
    ADDED
    
    | @@ -0,0 +1,65 @@ | |
| 1 | 
            +
            module Fakes
         | 
| 2 | 
            +
              class Stack
         | 
| 3 | 
            +
                attr_reader :outputs, :parameters
         | 
| 4 | 
            +
                def initialize(options)
         | 
| 5 | 
            +
                  @exists = options[:exists?].nil? ? true : options[:exists?]
         | 
| 6 | 
            +
                  @outputs = options[:outputs] || {}
         | 
| 7 | 
            +
                  @parameters = options[:parameters] || {}
         | 
| 8 | 
            +
                  @name = options[:name] || 'Unnamed'
         | 
| 9 | 
            +
                  @status = options[:status] || :ready
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def inspect
         | 
| 13 | 
            +
                  "#{self.class}<#{@name}>"
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
                alias_method :to_s, :inspect
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def output(key)
         | 
| 18 | 
            +
                  raise 'Stack is dead' unless @exists
         | 
| 19 | 
            +
                  @outputs[key]
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def set_output(key, value)
         | 
| 23 | 
            +
                  @outputs[key] = value
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def live!
         | 
| 27 | 
            +
                  @exists = true
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def die!
         | 
| 31 | 
            +
                  @exists = false
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def exists?
         | 
| 35 | 
            +
                  @exists
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def delete
         | 
| 39 | 
            +
                  @exists = false
         | 
| 40 | 
            +
                  @deployed = false
         | 
| 41 | 
            +
                  @deleted = true
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                def deploy
         | 
| 45 | 
            +
                  @exists = true
         | 
| 46 | 
            +
                  @deployed = true
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                def deployed?
         | 
| 50 | 
            +
                  @deployed
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def deleted?
         | 
| 54 | 
            +
                  @deleted
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                def name
         | 
| 58 | 
            +
                  @name
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                def status
         | 
| 62 | 
            +
                  @status
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
              end
         | 
| 65 | 
            +
            end
         | 
| @@ -0,0 +1,73 @@ | |
| 1 | 
            +
            require 'functional_spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe 'Deploy' do
         | 
| 4 | 
            +
              context 'create-or-update' do
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                let(:asg_driver) { double('asg_driver') }
         | 
| 7 | 
            +
                let(:stack) { Fakes::Stack.new(name: 'stack', outputs: {'AutoScalingGroupID' => 'myASG'})}
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                it 'should create a stack in Cloud Formation' do
         | 
| 10 | 
            +
                  stack.die!
         | 
| 11 | 
            +
                  allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-create-or-update-test-web', 'web', anything()) { stack }
         | 
| 12 | 
            +
                  allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('myASG') { asg_driver }
         | 
| 13 | 
            +
                  allow(asg_driver).to receive(:describe) {{desired: 1, min: 1, max: 2}}
         | 
| 14 | 
            +
                  allow(asg_driver).to receive(:warm_up).with(1)
         | 
| 15 | 
            +
                  CfDeployer::CLI.start(['deploy', 'test', 'web', '-f', 'samples/create-or-update/cf_deployer.yml'])
         | 
| 16 | 
            +
                  expect(stack).to be_deployed
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              context 'cname-swap' do
         | 
| 21 | 
            +
                let(:blue_asg_driver) { double('blue_asg_driver') }
         | 
| 22 | 
            +
                let(:green_asg_driver) { double('green_asg_driver') }
         | 
| 23 | 
            +
                let(:dns_driver) { double('route53') }
         | 
| 24 | 
            +
                let(:elb_driver) { double('elb') }
         | 
| 25 | 
            +
                let(:blue_stack)  { Fakes::Stack.new(name: 'BLUE', outputs: {'ELBName' => 'BLUE-elb', 'AutoScalingGroupName' => 'blueASG'}, parameters: {:name => 'blue'}) }
         | 
| 26 | 
            +
                let(:green_stack) { Fakes::Stack.new(name: 'GREEN', outputs: {'ELBName' => 'GREEN-elb', 'AutoScalingGroupName' => 'greenASG'}, parameters: {:name => 'green'}) }
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                before :each do
         | 
| 29 | 
            +
                  allow(Kernel).to receive(:sleep)
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
                it 'should recreate inactive stack and set CNAME map to its ELB dns' do
         | 
| 32 | 
            +
                  allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-cname-swap-dev-web-B', 'web', anything()) { blue_stack }
         | 
| 33 | 
            +
                  allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-cname-swap-dev-web-G', 'web', anything()) { green_stack }
         | 
| 34 | 
            +
                  allow(CfDeployer::Driver::Elb).to receive(:new) { elb_driver }
         | 
| 35 | 
            +
                  allow(CfDeployer::Driver::Route53).to receive(:new) { dns_driver }
         | 
| 36 | 
            +
                  allow(dns_driver).to receive(:find_alias_target).with('zhao.com', 'test1.zhao.com'){ 'BLUE-elb.zhao.com' }
         | 
| 37 | 
            +
                  allow(elb_driver).to receive(:find_dns_and_zone_id).with('BLUE-elb') { {:dns_name => 'blue-elb.zhao.com', :canonical_hosted_zone_name_id => 'BLUE111'}}
         | 
| 38 | 
            +
                  allow(elb_driver).to receive(:find_dns_and_zone_id).with('GREEN-elb') { {:dns_name => 'green-elb.zhao.com', :canonical_hosted_zone_name_id => 'GREEN111'}}
         | 
| 39 | 
            +
                  allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
         | 
| 40 | 
            +
                  allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
         | 
| 41 | 
            +
                  allow(green_asg_driver).to receive(:describe) {{desired: 0, min: 0, max: 0}}
         | 
| 42 | 
            +
                  allow(blue_asg_driver).to receive(:describe) {{desired: 2, min: 1, max: 5}}
         | 
| 43 | 
            +
                  allow(dns_driver).to receive(:set_alias_target).with('zhao.com', 'test1.zhao.com', 'GREEN111', 'green-elb.zhao.com')
         | 
| 44 | 
            +
                  expect(green_asg_driver).to receive(:warm_up).with(2)
         | 
| 45 | 
            +
                  CfDeployer::CLI.start(['deploy', 'dev', 'web', '-f', 'samples/cname-swap/cf_deployer.yml'])
         | 
| 46 | 
            +
                  expect(green_stack).to be_deleted
         | 
| 47 | 
            +
                  expect(green_stack).to be_deployed
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
              context 'autoscaling-swap' do
         | 
| 52 | 
            +
                let(:blue_asg_driver) { double('blue_asg_driver') }
         | 
| 53 | 
            +
                let(:green_asg_driver) { double('green_asg_driver') }
         | 
| 54 | 
            +
                let(:blue_stack)  { Fakes::Stack.new(name: 'BLUE', outputs: {'web-elb-name' => 'BLUE-elb', 'AutoScalingGroupName' => 'blueASG'}, parameters: { name: 'blue'}) }
         | 
| 55 | 
            +
                let(:green_stack)  { Fakes::Stack.new(name: 'GREEN', outputs: {'web-elb-name' => 'GREEN-elb', 'AutoScalingGroupName' => 'greenASG'}, parameters: { name: 'green'}) }
         | 
| 56 | 
            +
                let(:base_stack)  { Fakes::Stack.new(name: 'base') }
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                it 'should re-create and warm up inactive stack and cool down the active stack' do
         | 
| 59 | 
            +
                  allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-asg-swap-dev-web-B', 'web', anything) { blue_stack }
         | 
| 60 | 
            +
                  allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-asg-swap-dev-web-G', 'web', anything) { green_stack }
         | 
| 61 | 
            +
                  allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-asg-swap-dev-base', 'base', anything) { base_stack }
         | 
| 62 | 
            +
                  allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
         | 
| 63 | 
            +
                  allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
         | 
| 64 | 
            +
                  allow(blue_asg_driver).to receive(:describe) {{desired: 0, min: 0, max: 0}}
         | 
| 65 | 
            +
                  allow(green_asg_driver).to receive(:describe) {{desired: 2, min: 1, max: 5}}
         | 
| 66 | 
            +
                  expect(blue_asg_driver).to receive(:warm_up).with(2)
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  CfDeployer::CLI.start(['deploy', 'dev', 'web', '-f', 'samples/simple/cf_deployer.yml'])
         | 
| 69 | 
            +
                  expect(blue_stack).to be_deleted
         | 
| 70 | 
            +
                  expect(blue_stack).to be_deployed
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
              end
         | 
| 73 | 
            +
            end
         | 
| @@ -0,0 +1,57 @@ | |
| 1 | 
            +
            require 'functional_spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe 'Kill Inactive' do
         | 
| 4 | 
            +
              context 'create-or-update' do
         | 
| 5 | 
            +
                let(:stack) { Fakes::Stack.new(name: 'stack', outputs: {'AutoScalingGroupID' => 'myASG'})}
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                it 'should raise an error that there is no inactive for create-or-update staks' do
         | 
| 8 | 
            +
                  stack.die!
         | 
| 9 | 
            +
                  allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-create-or-update-test-web', 'web', anything()) { stack }
         | 
| 10 | 
            +
                  expect { CfDeployer::CLI.start(['kill_inactive', 'test', 'web', '-f', 'samples/create-or-update/cf_deployer.yml']) }.to raise_error CfDeployer::ApplicationError, 'There is no inactive version to kill for Create or Update Deployments.'
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              context 'cname-swap' do
         | 
| 15 | 
            +
                let(:blue_stack)  { Fakes::Stack.new(name: 'BLUE', outputs: {'ELBName' => 'BLUE-elb', 'AutoScalingGroupID' => 'blueASG'}, parameters: {:name => 'blue'}) }
         | 
| 16 | 
            +
                let(:green_stack) { Fakes::Stack.new(name: 'GREEN', outputs: {'ELBName' => 'GREEN-elb', 'AutoScalingGroupID' => 'greenASG'}, parameters: {:name => 'green'}) }
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                it 'should delete the stack that is not being pointed to by dns' do
         | 
| 19 | 
            +
                  blue_stack.live!
         | 
| 20 | 
            +
                  green_stack.live!
         | 
| 21 | 
            +
                  elb_driver = double('elb_driver')
         | 
| 22 | 
            +
                  allow(CfDeployer::Driver::Elb).to receive(:new) { elb_driver }
         | 
| 23 | 
            +
                  allow(elb_driver).to receive(:find_dns_and_zone_id).with('BLUE-elb') { {:dns_name => 'blue-elb.aws.amazon.com', :canonical_hosted_zone_name_id => 'BLUE111'}}
         | 
| 24 | 
            +
                  allow(elb_driver).to receive(:find_dns_and_zone_id).with('GREEN-elb') { {:dns_name => 'green-elb.aws.amazon.com', :canonical_hosted_zone_name_id => 'GREEN111'}}
         | 
| 25 | 
            +
                  allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-cname-swap-test-web-B', 'web', anything) { blue_stack }
         | 
| 26 | 
            +
                  allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-cname-swap-test-web-G', 'web', anything) { green_stack }
         | 
| 27 | 
            +
                  dns_driver = double('route53 driver')
         | 
| 28 | 
            +
                  allow(CfDeployer::Driver::Route53).to receive(:new) { dns_driver }
         | 
| 29 | 
            +
                  allow(dns_driver).to receive(:find_alias_target).with('zhao.com', 'test1.zhao.com'){ 'BLUE-elb.aws.amazon.com' }
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  CfDeployer::CLI.start(['kill_inactive', 'test', 'web', '-f', 'samples/cname-swap/cf_deployer.yml'])
         | 
| 32 | 
            +
                  expect(green_stack).to be_deleted
         | 
| 33 | 
            +
                  expect(blue_stack).not_to be_deleted
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              context 'asg-swap' do
         | 
| 38 | 
            +
                let(:blue_stack)  { Fakes::Stack.new(name: 'BLUE', outputs: {'ELBName' => 'BLUE-elb', 'AutoScalingGroupName' => 'blueASG'}, parameters: {:name => 'blue'}) }
         | 
| 39 | 
            +
                let(:green_stack) { Fakes::Stack.new(name: 'GREEN', outputs: {'ELBName' => 'GREEN-elb', 'AutoScalingGroupName' => 'greenASG'}, parameters: {:name => 'green'}) }
         | 
| 40 | 
            +
                let(:blue_asg_driver) { double('blue_asg_driver') }
         | 
| 41 | 
            +
                let(:green_asg_driver) { double('green_asg_driver') }
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                it 'should delete the stack that has no active instances' do
         | 
| 44 | 
            +
                  blue_stack.live!
         | 
| 45 | 
            +
                  green_stack.live!
         | 
| 46 | 
            +
                  allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-asg-swap-test-web-B', 'web', anything) { blue_stack }
         | 
| 47 | 
            +
                  allow(CfDeployer::Stack).to receive(:new).with('cf-deployer-sample-asg-swap-test-web-G', 'web', anything) { green_stack }
         | 
| 48 | 
            +
                  allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('greenASG') { green_asg_driver }
         | 
| 49 | 
            +
                  allow(CfDeployer::Driver::AutoScalingGroup).to receive(:new).with('blueASG') { blue_asg_driver }
         | 
| 50 | 
            +
                  allow(green_asg_driver).to receive(:describe) { {desired: 0, min: 0, max: 0} }
         | 
| 51 | 
            +
                  allow(blue_asg_driver).to receive(:describe) { {desired: 1, min: 1, max: 2} }
         | 
| 52 | 
            +
                  CfDeployer::CLI.start(['kill_inactive', 'test', 'web', '-f', 'samples/simple/cf_deployer.yml'])
         | 
| 53 | 
            +
                  expect(green_stack).to be_deleted
         | 
| 54 | 
            +
                  expect(blue_stack).not_to be_deleted
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
            end
         | 
    
        data/spec/spec_helper.rb
    ADDED
    
    
| @@ -0,0 +1,191 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe "application" do
         | 
| 4 | 
            +
              before :each do
         | 
| 5 | 
            +
                @context = {
         | 
| 6 | 
            +
                  :application => 'myApp',
         | 
| 7 | 
            +
                  :environment => 'dev',
         | 
| 8 | 
            +
                  :targets => ['queue', 'web', 'db', 'base'],
         | 
| 9 | 
            +
                  :components => {
         | 
| 10 | 
            +
                      :queue => {
         | 
| 11 | 
            +
                        :'depends-on' => [ 'base', 'db' ]
         | 
| 12 | 
            +
                      },
         | 
| 13 | 
            +
                      :web => {
         | 
| 14 | 
            +
                        :'depends-on' => [ 'db', 'queue' ]
         | 
| 15 | 
            +
                      },
         | 
| 16 | 
            +
                      :db => {
         | 
| 17 | 
            +
                        :'depends-on' => [ 'base']
         | 
| 18 | 
            +
                      },
         | 
| 19 | 
            +
                      :base => {
         | 
| 20 | 
            +
                      }
         | 
| 21 | 
            +
                    }
         | 
| 22 | 
            +
                }
         | 
| 23 | 
            +
                @app = CfDeployer::Application.new(@context)
         | 
| 24 | 
            +
                @base = @app.components.find{ |c| c.name == 'base' }
         | 
| 25 | 
            +
                @db = @app.components.find{ |c| c.name == 'db' }
         | 
| 26 | 
            +
                @queue = @app.components.find{ |c| c.name == 'queue' }
         | 
| 27 | 
            +
                @web = @app.components.find{ |c| c.name == 'web' }
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              it "application should get all components" do
         | 
| 31 | 
            +
                @app.components.length.should eq(4)
         | 
| 32 | 
            +
                @base.should_not be_nil
         | 
| 33 | 
            +
                @db.should_not be_nil
         | 
| 34 | 
            +
                @queue.should_not be_nil
         | 
| 35 | 
            +
                @web.should_not be_nil
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              context "order components by dependencies" do
         | 
| 39 | 
            +
                it "should get components ordered by dependencies" do
         | 
| 40 | 
            +
                    expect(@app.components).to eq([@base, @db, @queue, @web])
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              context 'destroy' do
         | 
| 45 | 
            +
                it 'should destroy components starting with components without dependents' do
         | 
| 46 | 
            +
                  log = ""
         | 
| 47 | 
            +
                  allow(@base).to receive(:destroy) { log += "base "}
         | 
| 48 | 
            +
                  allow(@db).to receive(:destroy) { log += "db "}
         | 
| 49 | 
            +
                  allow(@queue).to receive(:destroy) { log += "queue "}
         | 
| 50 | 
            +
                  allow(@web).to receive(:destroy) { log += "web "}
         | 
| 51 | 
            +
                  @app.destroy
         | 
| 52 | 
            +
                  expect(log).to eql("web queue db base ")
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                it 'should destroy specified components' do
         | 
| 56 | 
            +
                  log = ""
         | 
| 57 | 
            +
                  allow(@base).to receive(:destroy) { log += "base "}
         | 
| 58 | 
            +
                  allow(@db).to receive(:destroy) { log += "db "}
         | 
| 59 | 
            +
                  allow(@queue).to receive(:destroy) { log += "queue "}
         | 
| 60 | 
            +
                  allow(@web).to receive(:destroy) { log += "web "}
         | 
| 61 | 
            +
                  @context[:targets] = ['db', 'web', 'queue']
         | 
| 62 | 
            +
                  @app.destroy
         | 
| 63 | 
            +
                  expect(log).to eql("web queue db ")
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
              it "application should get components with their dependencies" do
         | 
| 68 | 
            +
                expect(@base.dependencies).to match_array([])
         | 
| 69 | 
            +
                expect(@db.dependencies).to match_array([@base])
         | 
| 70 | 
            +
                expect(@queue.dependencies).to match_array([@base, @db])
         | 
| 71 | 
            +
                expect(@web.dependencies).to match_array([@db, @queue])
         | 
| 72 | 
            +
              end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
              it 'should get add components with their children' do
         | 
| 75 | 
            +
                expect(@base.children).to match_array([@db, @queue])
         | 
| 76 | 
            +
                expect(@db.children).to match_array([@web, @queue])
         | 
| 77 | 
            +
                expect(@queue.children).to match_array([@web])
         | 
| 78 | 
            +
                expect(@web.children).to match_array([])
         | 
| 79 | 
            +
              end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
              describe '#switch' do
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                let(:app) { CfDeployer::Application.new(@context.merge(:targets => ['base'])) }
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                it 'should switch the specified component' do
         | 
| 86 | 
            +
                  base = app.components.find { |c| c.name == 'base' }
         | 
| 87 | 
            +
                  expect(base).to receive :switch
         | 
| 88 | 
            +
                  app.switch
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                it 'should not switch components not specified' do
         | 
| 92 | 
            +
                  db = app.components.find { |c| c.name == 'db' }
         | 
| 93 | 
            +
                  queue = app.components.find { |c| c.name == 'queue' }
         | 
| 94 | 
            +
                  web = app.components.find { |c| c.name == 'web' }
         | 
| 95 | 
            +
                  base = app.components.find { |c| c.name == 'base' }
         | 
| 96 | 
            +
                  allow(base).to receive :switch
         | 
| 97 | 
            +
                  expect(db).not_to receive :switch
         | 
| 98 | 
            +
                  expect(queue).not_to receive :switch
         | 
| 99 | 
            +
                  expect(web).not_to receive :switch
         | 
| 100 | 
            +
                  app.switch
         | 
| 101 | 
            +
                end
         | 
| 102 | 
            +
              end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
              describe '#kill_inactive' do
         | 
| 105 | 
            +
                it 'should kill the inactive piece of a component' do
         | 
| 106 | 
            +
                  @context[:targets] = ['base']
         | 
| 107 | 
            +
                  app = CfDeployer::Application.new(@context)
         | 
| 108 | 
            +
                  base = app.components.find{ |c| c.name == 'base' }
         | 
| 109 | 
            +
                  expect(base).to receive(:kill_inactive)
         | 
| 110 | 
            +
                  app.kill_inactive
         | 
| 111 | 
            +
                end
         | 
| 112 | 
            +
              end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
              describe '#status' do
         | 
| 115 | 
            +
                it "should get each component's status" do
         | 
| 116 | 
            +
                  expect(@base).to receive(:status)
         | 
| 117 | 
            +
                  expect(@db).to receive(:status)
         | 
| 118 | 
            +
                  expect(@queue).to receive(:status)
         | 
| 119 | 
            +
                  expect(@web).to receive(:status)
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                  @app.status nil, 'all'
         | 
| 122 | 
            +
                end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                it 'should pass the get_resource_statuses flag down to the components' do
         | 
| 125 | 
            +
                  expect(@base).to receive(:status).with(true)
         | 
| 126 | 
            +
                  expect(@db).to receive(:status).with(true)
         | 
| 127 | 
            +
                  expect(@queue).to receive(:status).with(true)
         | 
| 128 | 
            +
                  expect(@web).to receive(:status).with(true)
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                  @app.status nil, 'all'
         | 
| 131 | 
            +
                end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                it 'should filter by component if specified' do
         | 
| 134 | 
            +
                  expect(@base).not_to receive(:status)
         | 
| 135 | 
            +
                  expect(@db).not_to receive(:status)
         | 
| 136 | 
            +
                  expect(@queue).not_to receive(:status)
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                  expect(@web).to receive(:status).with(true)
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                  @app.status 'web', 'all'
         | 
| 141 | 
            +
                end
         | 
| 142 | 
            +
              end
         | 
| 143 | 
            +
             | 
| 144 | 
            +
              context "deploy components" do
         | 
| 145 | 
            +
                 before :each do
         | 
| 146 | 
            +
                    @log = ""
         | 
| 147 | 
            +
                    allow(@base).to receive(:deploy) { @log += "base "}
         | 
| 148 | 
            +
                    allow(@db).to receive(:deploy) { @log += "db "}
         | 
| 149 | 
            +
                    allow(@queue).to receive(:deploy) { @log += "queue "}
         | 
| 150 | 
            +
                    allow(@web).to receive(:deploy) { @log += "web "}
         | 
| 151 | 
            +
                 end
         | 
| 152 | 
            +
             | 
| 153 | 
            +
             | 
| 154 | 
            +
                context "deploy all components" do
         | 
| 155 | 
            +
                  it "should deploy all components if no component specified" do
         | 
| 156 | 
            +
                    @app.deploy
         | 
| 157 | 
            +
                    @log.should eq("base db queue web ")
         | 
| 158 | 
            +
                  end
         | 
| 159 | 
            +
                end
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                context "deploy some components" do
         | 
| 162 | 
            +
                  it "should deploy specified components" do
         | 
| 163 | 
            +
                    @context[:targets] = ['web', 'db']
         | 
| 164 | 
            +
                    @app.deploy
         | 
| 165 | 
            +
                    @log.should eq("db web ")
         | 
| 166 | 
            +
                  end
         | 
| 167 | 
            +
                end
         | 
| 168 | 
            +
              end
         | 
| 169 | 
            +
             | 
| 170 | 
            +
              context '#json' do
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                before :each do
         | 
| 173 | 
            +
                  @log = ''
         | 
| 174 | 
            +
                  allow(@base).to receive(:json) { @log += "base " }
         | 
| 175 | 
            +
                  allow(@db).to receive(:json) { @log += "db " }
         | 
| 176 | 
            +
                  allow(@queue).to receive(:json) { @log += "queue " }
         | 
| 177 | 
            +
                  allow(@web).to receive(:json) { @log += "web " }
         | 
| 178 | 
            +
                end
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                it 'should get json templates for all components' do
         | 
| 181 | 
            +
                  @app.json
         | 
| 182 | 
            +
                  @log.should eq("base db queue web ")
         | 
| 183 | 
            +
                end
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                it 'should get json templates for components specified' do
         | 
| 186 | 
            +
                  @context[:targets] = ['web', 'db']
         | 
| 187 | 
            +
                  @app.json
         | 
| 188 | 
            +
                  @log.should eq('db web ')
         | 
| 189 | 
            +
                end
         | 
| 190 | 
            +
              end
         | 
| 191 | 
            +
            end
         | 
| @@ -0,0 +1,142 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe "component" do
         | 
| 4 | 
            +
              before :each do
         | 
| 5 | 
            +
                @strategy = double('deployment_strategy')
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                allow(CfDeployer::DeploymentStrategy).to receive(:create).and_return(@strategy)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                   @context = {
         | 
| 10 | 
            +
                      :'deployment-strategy' => 'create-or-update',
         | 
| 11 | 
            +
                      :inputs => {
         | 
| 12 | 
            +
                        :'vpc-subnets' => {
         | 
| 13 | 
            +
                          :component => 'base',
         | 
| 14 | 
            +
                          :'output-key' => 'subnets'
         | 
| 15 | 
            +
                        }
         | 
| 16 | 
            +
                       }
         | 
| 17 | 
            +
                      }
         | 
| 18 | 
            +
                @base = CfDeployer::Component.new('myApp', 'uat', 'base', {})
         | 
| 19 | 
            +
                @db = CfDeployer::Component.new('myApp', 'uat', 'db', {})
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                @web = CfDeployer::Component.new('myApp', 'uat', 'web', @context)
         | 
| 22 | 
            +
                @web.dependencies << @base
         | 
| 23 | 
            +
                @web.dependencies << @db
         | 
| 24 | 
            +
                @base.children << @web
         | 
| 25 | 
            +
                @db.children << @web
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              context 'json' do
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                it 'should revolve settings from parent components if parent components has been deployed' do
         | 
| 31 | 
            +
                  allow(@base).to receive(:exists?){ true }
         | 
| 32 | 
            +
                  allow(@base).to receive(:output_value).with('subnets') { 'abcd1234, edfas1234' }
         | 
| 33 | 
            +
                  expect(CfDeployer::ConfigLoader).to receive(:component_json).with('web', @context)
         | 
| 34 | 
            +
                  @web.json
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  expect(@context[:inputs][:'vpc-subnets']).to eq('abcd1234, edfas1234')
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
             | 
| 41 | 
            +
              it "should destroy component" do
         | 
| 42 | 
            +
                expect(@strategy).to receive(:destroy)
         | 
| 43 | 
            +
                @web.destroy
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              it 'should not destroy a component that is depended on' do
         | 
| 47 | 
            +
                allow(@web).to receive(:exists?){ true }
         | 
| 48 | 
            +
                expect(@strategy).not_to receive(:destroy)
         | 
| 49 | 
            +
                expect{ @base.destroy }.to raise_error("Unable to destroy #{@base.name}, it is depended on by other components")
         | 
| 50 | 
            +
              end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              it "should get output value" do
         | 
| 53 | 
            +
                expect(@strategy).to receive(:output_value).with('key1'){ 'value1' }
         | 
| 54 | 
            +
                @web.output_value('key1')
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
              it "deployment should only deploy depends-on if the depends-on do not exists" do
         | 
| 58 | 
            +
                expect(@base).to receive(:exists?){ false }
         | 
| 59 | 
            +
                expect(@db).to receive(:exists?) { true }
         | 
| 60 | 
            +
                expect(@base).to receive(:deploy)
         | 
| 61 | 
            +
                expect(@db).to_not receive(:deploy)
         | 
| 62 | 
            +
                expect(@strategy).to receive(:deploy)
         | 
| 63 | 
            +
                expect(@base).to receive(:output_value).with('subnets') { 'abcd1234, edfas1234' }
         | 
| 64 | 
            +
                @web.deploy
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                expect(@context[:inputs][:'vpc-subnets']).to eq('abcd1234, edfas1234')
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
              it "should ask strategy if component exists" do
         | 
| 70 | 
            +
                 expect(@strategy).to receive(:exists?){ true }
         | 
| 71 | 
            +
                 @web.exists?.should eq(true)
         | 
| 72 | 
            +
              end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
              it "should find direct dependencies" do
         | 
| 75 | 
            +
                web = CfDeployer::Component.new('myApp', 'uat', 'web', {})
         | 
| 76 | 
            +
                base = CfDeployer::Component.new('myApp', 'uat', 'base', {})
         | 
| 77 | 
            +
                web.dependencies << base
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                web.depends_on?(base).should eq(true)
         | 
| 80 | 
            +
              end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
              it "should find transitive dependencies" do
         | 
| 83 | 
            +
                web = CfDeployer::Component.new('myApp', 'uat', 'web', {})
         | 
| 84 | 
            +
                haproxy = CfDeployer::Component.new('myApp', 'uat', 'haproxy', {})
         | 
| 85 | 
            +
                base = CfDeployer::Component.new('myApp', 'uat', 'base', {})
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                haproxy.dependencies << base
         | 
| 88 | 
            +
                web.dependencies << haproxy
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                web.depends_on?(base).should eq(true)
         | 
| 91 | 
            +
              end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
              it "should find cyclic dependency" do
         | 
| 94 | 
            +
                web = CfDeployer::Component.new('myApp', 'uat', 'web', {})
         | 
| 95 | 
            +
                haproxy = CfDeployer::Component.new('myApp', 'uat', 'haproxy', {})
         | 
| 96 | 
            +
                base = CfDeployer::Component.new('myApp', 'uat', 'base', {})
         | 
| 97 | 
            +
                foo = CfDeployer::Component.new('myApp', 'uat', 'foo', {})
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                haproxy.dependencies << base
         | 
| 100 | 
            +
                web.dependencies << haproxy
         | 
| 101 | 
            +
                base.dependencies << web
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                expect{haproxy.depends_on? foo}.to raise_error("Cyclic dependency")
         | 
| 104 | 
            +
              end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
              describe '#status' do
         | 
| 107 | 
            +
                it "should ask strategy for status" do
         | 
| 108 | 
            +
                   expect(@strategy).to receive(:status){ true }
         | 
| 109 | 
            +
                   @web.status false
         | 
| 110 | 
            +
                end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                it "should pass get_resource_statuses down to the strategy" do
         | 
| 113 | 
            +
                   expect(@strategy).to receive(:status).with(true)
         | 
| 114 | 
            +
                   @web.status(true)
         | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
              end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
              describe '#kill_inactive' do
         | 
| 119 | 
            +
                it 'should tell the strategy to kill the inactive piece' do
         | 
| 120 | 
            +
                  expect(@strategy).to receive(:kill_inactive)
         | 
| 121 | 
            +
                  @web.kill_inactive
         | 
| 122 | 
            +
                end
         | 
| 123 | 
            +
              end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
              describe '#switch' do
         | 
| 126 | 
            +
                context 'if no stack exists' do
         | 
| 127 | 
            +
                  it 'should raise an error that there is no stack for the component' do
         | 
| 128 | 
            +
                    allow(@strategy).to receive(:exists?) { false }
         | 
| 129 | 
            +
                    expect(@strategy).not_to receive(:switch)
         | 
| 130 | 
            +
                    expect { @web.switch }.to raise_error 'No stack exists for component: web'
         | 
| 131 | 
            +
                  end
         | 
| 132 | 
            +
                end
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                context 'a stack exists' do
         | 
| 135 | 
            +
                  it 'should use the deployment strategy to switch' do
         | 
| 136 | 
            +
                    allow(@strategy).to receive(:exists?) { true }
         | 
| 137 | 
            +
                    expect(@strategy).to receive(:switch)
         | 
| 138 | 
            +
                    @web.switch
         | 
| 139 | 
            +
                  end
         | 
| 140 | 
            +
                end
         | 
| 141 | 
            +
              end
         | 
| 142 | 
            +
            end
         |