simple_deploy 0.4.8 → 0.5.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.
data/.gitignore CHANGED
@@ -2,3 +2,4 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ *.swp
data/CHANGELOG CHANGED
@@ -1,3 +1,13 @@
1
+ ## v0.5.0:
2
+
3
+ * Updated the update command so it accepts a force option
4
+ * Refactored deployments to read bucket_prefix and domain from attributes instead of the yaml file
5
+ * Added a protect command that allows users to toggle whether stacks should be protected from destroy calls
6
+ * Refactored the deployment locking logic
7
+ * Corrected the destroy command so it no longer creates a deployment
8
+ * Gracefully exit if the .simple_deploy.yml file is missing or corrupt
9
+ * Corrected the update command so it now checks the deployment lock at the beginning of the process
10
+
1
11
  ## v0.4.8:
2
12
 
3
13
  * Added as_command_args option to attributes cli
data/Rakefile CHANGED
@@ -1,10 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
-
3
2
  require 'rspec/core/rake_task'
4
3
 
5
- task :default => [:spec]
6
-
7
4
  desc 'Run specs'
5
+
8
6
  RSpec::Core::RakeTask.new do |t|
9
- t.rspec_opts = %w(-fs --color)
7
+ t.rspec_opts = %w(--color)
10
8
  end
@@ -3,11 +3,10 @@ module SimpleDeploy
3
3
 
4
4
  def initialize(args)
5
5
  @bucket_prefix = args[:bucket_prefix]
6
- @config = args[:config]
7
6
  @id = args[:id]
8
7
  @name = args[:name]
9
8
  @region = args[:region]
10
- @domain = @config.artifact_domain @name
9
+ @domain = args[:domain]
11
10
 
12
11
  @bucket = "#{@bucket_prefix}-#{@region}"
13
12
  @key = "#{@id}.tar.gz"
@@ -41,7 +41,7 @@ EOS
41
41
  :name => name,
42
42
  :logger => logger
43
43
 
44
- stack.update(:attributes => new_attributes) if new_attributes.any?
44
+ stack.update :force => opts[:force], :attributes => new_attributes if new_attributes.any?
45
45
 
46
46
  if stack.deploy opts[:force]
47
47
  notifier.send_deployment_complete_message unless opts[:quiet]
@@ -32,7 +32,11 @@ EOS
32
32
  :config => config,
33
33
  :logger => logger
34
34
 
35
- stack.destroy
35
+ if stack.destroy
36
+ exit 0
37
+ else
38
+ exit 1
39
+ end
36
40
  end
37
41
  end
38
42
  end
@@ -0,0 +1,42 @@
1
+
2
+ require 'trollop'
3
+
4
+ module SimpleDeploy
5
+ module CLI
6
+ class Protect
7
+ def protect
8
+ opts = Trollop::options do
9
+ version SimpleDeploy::VERSION
10
+ banner <<-EOS
11
+
12
+ Protect a stack.
13
+
14
+ simple_deploy protect -n STACK_NAME -e ENVIRONMENT -a PROTECTION=ON_OFF
15
+
16
+ EOS
17
+ opt :help, "Display Help"
18
+ opt :environment, "Set the target environment", :type => :string
19
+ opt :log_level, "Log level: debug, info, warn, error", :type => :string,
20
+ :default => 'info'
21
+ opt :name, "Stack name of stack to protect", :type => :string
22
+ end
23
+
24
+ CLI::Shared.valid_options? :provided => opts,
25
+ :required => [:environment, :name]
26
+
27
+ config = Config.new.environment opts[:environment]
28
+
29
+ logger = SimpleDeployLogger.new :log_level => opts[:log_level]
30
+
31
+ attributes = CLI::Shared.parse_attributes :attributes => opts[:attributes],
32
+ :logger => logger
33
+
34
+ stack = Stack.new :environment => opts[:environment],
35
+ :name => opts[:name],
36
+ :config => config,
37
+ :logger => logger
38
+ stack.update :attributes => attributes
39
+ end
40
+ end
41
+ end
42
+ end
@@ -17,6 +17,7 @@ EOS
17
17
  opt :attributes, "= seperated attribute and it's value", :type => :string,
18
18
  :multi => true
19
19
  opt :environment, "Set the target environment", :type => :string
20
+ opt :force, "Force an update to proceed"
20
21
  opt :log_level, "Log level: debug, info, warn, error", :type => :string,
21
22
  :default => 'info'
22
23
  opt :name, "Stack name(s) of stack to deploy", :type => :string,
@@ -38,7 +39,7 @@ EOS
38
39
  :name => name,
39
40
  :config => config,
40
41
  :logger => logger
41
- stack.update :attributes => attributes
42
+ stack.update :force => opts[:force], :attributes => attributes
42
43
  end
43
44
  end
44
45
  end
@@ -11,6 +11,7 @@ require 'simple_deploy/cli/instances'
11
11
  require 'simple_deploy/cli/list'
12
12
  require 'simple_deploy/cli/outputs'
13
13
  require 'simple_deploy/cli/parameters'
14
+ require 'simple_deploy/cli/protect'
14
15
  require 'simple_deploy/cli/resources'
15
16
  require 'simple_deploy/cli/ssh'
16
17
  require 'simple_deploy/cli/status'
@@ -43,6 +44,8 @@ module SimpleDeploy
43
44
  CLI::Outputs.new.show
44
45
  when 'parameters'
45
46
  CLI::Parameters.new.show
47
+ when 'protect'
48
+ CLI::Protect.new.protect
46
49
  when 'resources'
47
50
  CLI::Resources.new.show
48
51
  when 'status'
@@ -26,14 +26,6 @@ module SimpleDeploy
26
26
  name_to_url_map[artifact]
27
27
  end
28
28
 
29
- def artifact_domain(artifact)
30
- config['artifacts'][artifact]['domain'] ||= artifact
31
- end
32
-
33
- def artifact_bucket_prefix(artifact)
34
- config['artifacts'][artifact]['bucket_prefix']
35
- end
36
-
37
29
  def deploy_script
38
30
  '/opt/intu/admin/bin/configure.sh'
39
31
  end
@@ -59,7 +51,14 @@ module SimpleDeploy
59
51
 
60
52
  def load_config_file
61
53
  config_file = "#{ENV['HOME']}/.simple_deploy.yml"
62
- self.config = YAML::load( File.open( config_file ) )
54
+
55
+ begin
56
+ self.config = YAML::load( File.open( config_file ) )
57
+ rescue Errno::ENOENT
58
+ raise "#{config_file} not found"
59
+ rescue Psych::SyntaxError => e
60
+ raise "#{config_file} is corrupt"
61
+ end
63
62
  end
64
63
 
65
64
  def env_home
@@ -11,14 +11,14 @@ module SimpleDeploy
11
11
  @logger = @config.logger
12
12
  end
13
13
 
14
- def cleared_to_deploy?(force=false)
15
- return true unless deployment_in_progress?
14
+ def clear_for_deployment?
15
+ !deployment_in_progress?
16
+ end
16
17
 
17
- if force
18
+ def clear_deployment_lock(force=false)
19
+ if deployment_in_progress? && force
18
20
  @logger.info "Forcing. Clearing deployment status."
19
21
  unset_deployment_in_progress
20
- else
21
- false
22
22
  end
23
23
  end
24
24
 
@@ -20,6 +20,10 @@ module SimpleDeploy
20
20
  end
21
21
 
22
22
  def create_deployment
23
+ if @instances.nil? || @instances.empty?
24
+ raise "There are no running instances to deploy to"
25
+ end
26
+
23
27
  @deployment = Capistrano::Configuration.new :output => @logger
24
28
  @deployment.logger.level = 3
25
29
  @logger.info "Creating deployment to #{@name}."
@@ -32,7 +36,7 @@ module SimpleDeploy
32
36
  end
33
37
 
34
38
  def execute(force=false)
35
- if status.cleared_to_deploy?(force)
39
+ if clear_for_deployment?
36
40
  status.set_deployment_in_progress
37
41
  @logger.info 'Starting deployment.'
38
42
  @deployment.simpledeploy
@@ -45,6 +49,14 @@ module SimpleDeploy
45
49
  end
46
50
  end
47
51
 
52
+ def clear_for_deployment?
53
+ status.clear_for_deployment?
54
+ end
55
+
56
+ def clear_deployment_lock(force=false)
57
+ status.clear_deployment_lock(force)
58
+ end
59
+
48
60
  def ssh
49
61
  @stack.instances.map do |instance|
50
62
  info = instance['instancesSet'].first
@@ -76,12 +88,13 @@ module SimpleDeploy
76
88
  h = {}
77
89
  @config.artifacts.each do |artifact|
78
90
  variable = @config.artifact_deploy_variable artifact
79
- bucket_prefix = @config.artifact_bucket_prefix artifact
91
+ bucket_prefix = @attributes["#{artifact}_bucket_prefix"]
92
+ domain = @attributes["#{artifact}_domain"]
80
93
 
81
94
  artifact = Artifact.new :name => artifact,
82
95
  :id => @attributes[artifact],
83
96
  :region => @region,
84
- :config => @config,
97
+ :domain => domain,
85
98
  :bucket_prefix => bucket_prefix
86
99
 
87
100
  h[variable] = artifact.endpoints['s3']
@@ -108,7 +121,7 @@ module SimpleDeploy
108
121
  @deployment.set :gateway, ssh_gateway
109
122
  @logger.info "Proxying via gateway #{ssh_gateway}."
110
123
  else
111
- @logger.info "Not using an ssh gateway."
124
+ @logger.debug "Not using an ssh gateway."
112
125
  end
113
126
  end
114
127
 
@@ -4,6 +4,7 @@ module SimpleDeploy
4
4
  def initialize(args)
5
5
  @config = args[:config]
6
6
  @environment = args[:environment]
7
+ @main_attributes = args[:main_attributes]
7
8
  @region = @config.region @environment
8
9
  @logger = @config.logger
9
10
  end
@@ -31,7 +32,7 @@ module SimpleDeploy
31
32
  id = attribute[name]
32
33
  a = @config.artifacts.select { |a| a['name'] == name }.first
33
34
 
34
- bucket_prefix = @config.artifact_bucket_prefix name
35
+ bucket_prefix = @main_attributes["#{name}_bucket_prefix"]
35
36
  url_parameter = @config.artifact_cloud_formation_url name
36
37
 
37
38
  artifact = Artifact.new :name => name,
@@ -19,10 +19,16 @@ module SimpleDeploy
19
19
  end
20
20
 
21
21
  def update(args)
22
- @logger.info "Updating #{@name}."
23
- attributes = stack_attribute_formater.updated_attributes args[:attributes]
24
- stack.update :attributes => attributes
25
- @logger.info "Update complete for #{@name}."
22
+ if !deployment.clear_for_deployment? && args[:force]
23
+ deployment.clear_deployment_lock true
24
+ end
25
+
26
+ if deployment.clear_for_deployment?
27
+ @logger.info "Updating #{@name}."
28
+ attributes = stack_attribute_formater.updated_attributes args[:attributes]
29
+ stack.update :attributes => attributes
30
+ @logger.info "Update complete for #{@name}."
31
+ end
26
32
  end
27
33
 
28
34
  def deploy(force = false)
@@ -35,9 +41,14 @@ module SimpleDeploy
35
41
  end
36
42
 
37
43
  def destroy
38
- deployment.create_deployment
39
- stack.destroy
40
- @logger.info "#{@name} destroyed."
44
+ if attributes['protection'] != 'on'
45
+ stack.destroy
46
+ @logger.info "#{@name} destroyed."
47
+ true
48
+ else
49
+ @logger.warn "#{@name} could not be destroyed because it is protected. Run the protect subcommand to unprotect it"
50
+ false
51
+ end
41
52
  end
42
53
 
43
54
  def events(limit)
@@ -87,7 +98,8 @@ module SimpleDeploy
87
98
 
88
99
  def stack_attribute_formater
89
100
  @saf ||= StackAttributeFormater.new :config => @config,
90
- :environment => @environment
101
+ :environment => @environment,
102
+ :main_attributes => attributes
91
103
  end
92
104
 
93
105
  def deployment
@@ -1,3 +1,3 @@
1
1
  module SimpleDeploy
2
- VERSION = "0.4.8"
2
+ VERSION = "0.5.0"
3
3
  end
data/script/ci_setup CHANGED
@@ -11,4 +11,4 @@ rvm use "1.9.3-p125@simple_deploy" --create
11
11
  bundle
12
12
 
13
13
  # Run spec tests
14
- rspec spec
14
+ rake spec
@@ -5,10 +5,8 @@ describe SimpleDeploy do
5
5
  describe "an artifact" do
6
6
 
7
7
  before do
8
- @config_mock = mock 'config'
9
- @config_mock.should_receive(:artifact_domain).and_return('us-west-1')
10
8
  @artifact = SimpleDeploy::Artifact.new :bucket_prefix => 'test_prefix',
11
- :config => @config_mock,
9
+ :domain => 'us-west-1',
12
10
  :id => 'abc123',
13
11
  :name => 'myapp',
14
12
  :region => 'us-west-1'
@@ -1,4 +1,5 @@
1
1
  require 'spec_helper'
2
+ require 'simple_deploy/cli'
2
3
 
3
4
  describe SimpleDeploy::CLI::Attributes do
4
5
 
@@ -0,0 +1,112 @@
1
+
2
+ require 'spec_helper'
3
+ require 'simple_deploy/cli'
4
+
5
+ describe SimpleDeploy::CLI::Deploy do
6
+ describe 'deploy' do
7
+ before do
8
+ @logger = stub 'logger', 'info' => 'true', 'error' => 'true'
9
+ @stack = stub :attributes => {}
10
+ @notifier = stub
11
+
12
+ SimpleDeploy::SimpleDeployLogger.should_receive(:new).
13
+ with(:log_level => 'debug').
14
+ and_return(@logger)
15
+ end
16
+
17
+ it "should notify on success" do
18
+ options = { :environment => 'my_env',
19
+ :log_level => 'debug',
20
+ :name => ['my_stack'],
21
+ :force => true,
22
+ :attributes => [] }
23
+
24
+ SimpleDeploy::CLI::Shared.should_receive(:valid_options?).
25
+ with(:provided => options,
26
+ :required => [:environment, :name])
27
+ Trollop.stub(:options).and_return(options)
28
+
29
+ SimpleDeploy::Notifier.should_receive(:new).
30
+ with(:stack_name => 'my_stack',
31
+ :environment => 'my_env',
32
+ :logger => @logger).
33
+ and_return(@notifier)
34
+
35
+ SimpleDeploy::Stack.should_receive(:new).
36
+ with(:environment => 'my_env',
37
+ :logger => @logger,
38
+ :name => 'my_stack').
39
+ and_return(@stack)
40
+
41
+ @stack.should_receive(:deploy).with(true).and_return(true)
42
+ @notifier.should_receive(:send_deployment_complete_message)
43
+
44
+ subject.deploy
45
+ end
46
+
47
+
48
+ it "should exit on error with a status of 1" do
49
+ options = { :environment => 'my_env',
50
+ :log_level => 'debug',
51
+ :name => ['my_stack'],
52
+ :force => true,
53
+ :attributes => [] }
54
+
55
+ SimpleDeploy::CLI::Shared.should_receive(:valid_options?).
56
+ with(:provided => options,
57
+ :required => [:environment, :name])
58
+ Trollop.stub(:options).and_return(options)
59
+
60
+ SimpleDeploy::Notifier.should_receive(:new).
61
+ with(:stack_name => 'my_stack',
62
+ :environment => 'my_env',
63
+ :logger => @logger).
64
+ and_return(@notifier)
65
+
66
+ SimpleDeploy::Stack.should_receive(:new).
67
+ with(:environment => 'my_env',
68
+ :logger => @logger,
69
+ :name => 'my_stack').
70
+ and_return(@stack)
71
+
72
+ @stack.should_receive(:deploy).with(true).and_return(false)
73
+
74
+ begin
75
+ subject.deploy
76
+ rescue SystemExit => e
77
+ e.status.should == 1
78
+ end
79
+ end
80
+
81
+ it "should update the deploy attributes if any are passed" do
82
+ options = { :environment => 'my_env',
83
+ :log_level => 'debug',
84
+ :name => ['my_stack'],
85
+ :force => true,
86
+ :attributes => ['foo=bah'] }
87
+
88
+ SimpleDeploy::CLI::Shared.should_receive(:valid_options?).
89
+ with(:provided => options,
90
+ :required => [:environment, :name])
91
+ Trollop.stub(:options).and_return(options)
92
+
93
+ SimpleDeploy::Notifier.should_receive(:new).
94
+ with(:stack_name => 'my_stack',
95
+ :environment => 'my_env',
96
+ :logger => @logger).
97
+ and_return(@notifier)
98
+
99
+ SimpleDeploy::Stack.should_receive(:new).
100
+ with(:environment => 'my_env',
101
+ :logger => @logger,
102
+ :name => 'my_stack').
103
+ and_return(@stack)
104
+
105
+ @stack.should_receive(:update).with(hash_including(:force => true, :attributes => [{'foo' => 'bah'}]))
106
+ @stack.should_receive(:deploy).with(true).and_return(true)
107
+ @notifier.should_receive(:send_deployment_complete_message)
108
+
109
+ subject.deploy
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,67 @@
1
+
2
+ require 'spec_helper'
3
+ require 'simple_deploy/cli'
4
+
5
+ describe SimpleDeploy::CLI::Destroy do
6
+
7
+ describe 'destroy' do
8
+ before do
9
+ @config = mock 'config'
10
+ @logger = stub 'logger', 'info' => 'true'
11
+ @options = { :environment => 'my_env',
12
+ :log_level => 'debug',
13
+ :name => 'my_stack' }
14
+ @stack = stub :attributes => {}
15
+
16
+ SimpleDeploy::Config.stub(:new).and_return(@config)
17
+ @config.should_receive(:environment).with('my_env').and_return(@config)
18
+ SimpleDeploy::SimpleDeployLogger.should_receive(:new).
19
+ with(:log_level => 'debug').
20
+ and_return(@logger)
21
+ end
22
+
23
+ it "should exit with 0" do
24
+ SimpleDeploy::CLI::Shared.should_receive(:valid_options?).
25
+ with(:provided => @options,
26
+ :required => [:environment, :name])
27
+ Trollop.stub(:options).and_return(@options)
28
+
29
+ @stack.should_receive(:destroy).and_return(true)
30
+
31
+ SimpleDeploy::Stack.should_receive(:new).
32
+ with(:config => @config,
33
+ :environment => 'my_env',
34
+ :logger => @logger,
35
+ :name => 'my_stack').
36
+ and_return(@stack)
37
+
38
+ begin
39
+ subject.destroy
40
+ rescue SystemExit => e
41
+ e.status.should == 0
42
+ end
43
+ end
44
+
45
+ it "should exit with 1" do
46
+ SimpleDeploy::CLI::Shared.should_receive(:valid_options?).
47
+ with(:provided => @options,
48
+ :required => [:environment, :name])
49
+ Trollop.stub(:options).and_return(@options)
50
+
51
+ @stack.should_receive(:destroy).and_return(false)
52
+
53
+ SimpleDeploy::Stack.should_receive(:new).
54
+ with(:config => @config,
55
+ :environment => 'my_env',
56
+ :logger => @logger,
57
+ :name => 'my_stack').
58
+ and_return(@stack)
59
+
60
+ begin
61
+ subject.destroy
62
+ rescue SystemExit => e
63
+ e.status.should == 1
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,67 @@
1
+
2
+ require 'spec_helper'
3
+ require 'simple_deploy/cli'
4
+
5
+ describe SimpleDeploy::CLI::Protect do
6
+
7
+ describe 'protect' do
8
+ before do
9
+ @config = mock 'config'
10
+ @logger = stub 'logger', 'info' => 'true'
11
+
12
+ SimpleDeploy::Config.stub(:new).and_return(@config)
13
+ @config.should_receive(:environment).with('my_env').and_return(@config)
14
+ SimpleDeploy::SimpleDeployLogger.should_receive(:new).
15
+ with(:log_level => 'debug').
16
+ and_return(@logger)
17
+ end
18
+
19
+ it "should enable protection" do
20
+ options = { :environment => 'my_env',
21
+ :log_level => 'debug',
22
+ :name => 'my_stack',
23
+ :attributes => ['protection=on'] }
24
+
25
+ SimpleDeploy::CLI::Shared.should_receive(:valid_options?).
26
+ with(:provided => options,
27
+ :required => [:environment, :name])
28
+ Trollop.stub(:options).and_return(options)
29
+
30
+ stack = stub :attributes => { 'protection' => 'on' }
31
+ stack.should_receive(:update).with(hash_including(:attributes => [{ 'protection' => 'on' }]))
32
+
33
+ SimpleDeploy::Stack.should_receive(:new).
34
+ with(:config => @config,
35
+ :environment => 'my_env',
36
+ :logger => @logger,
37
+ :name => 'my_stack').
38
+ and_return(stack)
39
+
40
+ subject.protect
41
+ end
42
+
43
+ it "should disable protection" do
44
+ options = { :environment => 'my_env',
45
+ :log_level => 'debug',
46
+ :name => 'my_stack',
47
+ :attributes => ['protection=off'] }
48
+
49
+ SimpleDeploy::CLI::Shared.should_receive(:valid_options?).
50
+ with(:provided => options,
51
+ :required => [:environment, :name])
52
+ Trollop.stub(:options).and_return(options)
53
+
54
+ stack = stub :attributes => { 'protection' => 'off' }
55
+ stack.should_receive(:update).with(hash_including(:attributes => [{ 'protection' => 'off' }]))
56
+
57
+ SimpleDeploy::Stack.should_receive(:new).
58
+ with(:config => @config,
59
+ :environment => 'my_env',
60
+ :logger => @logger,
61
+ :name => 'my_stack').
62
+ and_return(stack)
63
+
64
+ subject.protect
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,67 @@
1
+
2
+ require 'spec_helper'
3
+ require 'simple_deploy/cli'
4
+
5
+ describe SimpleDeploy::CLI::Update do
6
+ describe 'update' do
7
+ before do
8
+ @config = mock 'config'
9
+ @logger = stub 'logger', 'info' => 'true'
10
+ @stack = stub :attributes => {}
11
+
12
+ SimpleDeploy::Config.stub(:new).and_return(@config)
13
+ @config.should_receive(:environment).with('my_env').and_return(@config)
14
+ SimpleDeploy::SimpleDeployLogger.should_receive(:new).
15
+ with(:log_level => 'debug').
16
+ and_return(@logger)
17
+ end
18
+
19
+ it "should pass force true" do
20
+ options = { :environment => 'my_env',
21
+ :log_level => 'debug',
22
+ :name => ['my_stack'],
23
+ :force => true,
24
+ :attributes => ['chef_repo_bucket_prefix=intu-lc'] }
25
+
26
+ SimpleDeploy::CLI::Shared.should_receive(:valid_options?).
27
+ with(:provided => options,
28
+ :required => [:environment, :name])
29
+ Trollop.stub(:options).and_return(options)
30
+
31
+ SimpleDeploy::Stack.should_receive(:new).
32
+ with(:config => @config,
33
+ :environment => 'my_env',
34
+ :logger => @logger,
35
+ :name => 'my_stack').
36
+ and_return(@stack)
37
+
38
+ @stack.should_receive(:update).with(hash_including(:force => true))
39
+
40
+ subject.update
41
+ end
42
+
43
+ it "should pass force false" do
44
+ options = { :environment => 'my_env',
45
+ :log_level => 'debug',
46
+ :name => ['my_stack'],
47
+ :force => false,
48
+ :attributes => ['chef_repo_bucket_prefix=intu-lc'] }
49
+
50
+ SimpleDeploy::CLI::Shared.should_receive(:valid_options?).
51
+ with(:provided => options,
52
+ :required => [:environment, :name])
53
+ Trollop.stub(:options).and_return(options)
54
+
55
+ SimpleDeploy::Stack.should_receive(:new).
56
+ with(:config => @config,
57
+ :environment => 'my_env',
58
+ :logger => @logger,
59
+ :name => 'my_stack').
60
+ and_return(@stack)
61
+
62
+ @stack.should_receive(:update).with(hash_including(:force => false))
63
+
64
+ subject.update
65
+ end
66
+ end
67
+ end
data/spec/config_spec.rb CHANGED
@@ -42,18 +42,6 @@ describe SimpleDeploy do
42
42
  @config.artifact_cloud_formation_url('app').should == 'AppArtifactURL'
43
43
  end
44
44
 
45
- it "should return the domain (the folder in the s3 bucket) for an artifact" do
46
- @config.artifact_domain('test_repo').should == 'test_domain'
47
- end
48
-
49
- it "should return the name of an artifact for those without a set domain" do
50
- @config.artifact_domain('test_repo2').should == 'test_repo2'
51
- end
52
-
53
- it "should return the bucket prefix for the artifact" do
54
- @config.artifact_bucket_prefix('test_repo').should == 'test_prefix'
55
- end
56
-
57
45
  it "should return the environment requested" do
58
46
  @config.environment('test_env').should == ({ 'secret_key' => 'secret', 'access_key' => 'access', 'region' => 'us-west-1' })
59
47
  end
@@ -72,4 +60,37 @@ describe SimpleDeploy do
72
60
 
73
61
  end
74
62
 
63
+ describe "gracefully handling yaml file errors" do
64
+ before do
65
+ if File.exists? "#{ENV['HOME']}/.simple_deploy.yml"
66
+ FileUtils.mv("#{ENV['HOME']}/.simple_deploy.yml",
67
+ "#{ENV['HOME']}/.simple_deploy.yml.bak")
68
+ end
69
+ end
70
+
71
+ after do
72
+ if File.exists? "#{ENV['HOME']}/.simple_deploy.yml.bak"
73
+ FileUtils.mv("#{ENV['HOME']}/.simple_deploy.yml.bak",
74
+ "#{ENV['HOME']}/.simple_deploy.yml")
75
+ end
76
+ end
77
+
78
+ it "should handle a missing file gracefully" do
79
+ expect {
80
+ config = SimpleDeploy::Config.new
81
+ }.to raise_error(RuntimeError, "#{ENV['HOME']}/.simple_deploy.yml not found")
82
+ end
83
+
84
+ it "should handle a corrupt file gracefully" do
85
+ s = "--\nport:\t80\t80"
86
+ File.open("#{ENV['HOME']}/.simple_deploy.yml", 'w') do |out|
87
+ out.write(s)
88
+ end
89
+
90
+ expect {
91
+ config = SimpleDeploy::Config.new
92
+ }.to raise_error(RuntimeError, "#{ENV['HOME']}/.simple_deploy.yml is corrupt")
93
+ FileUtils.rm "#{ENV['HOME']}/.simple_deploy.yml"
94
+ end
95
+ end
75
96
  end
@@ -4,16 +4,25 @@ describe SimpleDeploy do
4
4
 
5
5
  before do
6
6
  @attributes = { 'key' => 'val',
7
- 'ssh_gateway' => '1.2.3.4' }
8
- @config_mock = mock 'config mock'
7
+ 'ssh_gateway' => '1.2.3.4',
8
+ 'chef_repo' => 'chef_repo',
9
+ 'chef_repo_bucket_prefix' => 'chef_repo_bp',
10
+ 'chef_repo_domain' => 'chef_repo_d',
11
+ 'app' => 'app',
12
+ 'app_bucket_prefix' => 'app_bp',
13
+ 'app_domain' => 'app_d',
14
+ 'cookbooks' => 'cookbooks',
15
+ 'cookbooks_bucket_prefix' => 'cookbooks_bp',
16
+ 'cookbooks_domain' => 'cookbooks_d' }
9
17
  @logger_stub = stub 'logger stub'
10
18
  @logger_stub.stub :debug => 'true', :info => 'true'
11
- @stack_mock = mock 'stack mock'
12
19
 
13
- @stack_mock.should_receive(:attributes).and_return @attributes
14
- @config_mock.should_receive(:logger).and_return @logger_stub
15
- @config_mock.should_receive(:region).with('test-us-west-1').
16
- and_return 'us-west-1'
20
+ @config_mock = mock 'config mock'
21
+ @config_mock.stub(:logger) { @logger_stub }
22
+ @config_mock.stub(:region) { 'test-us-west-1' }
23
+
24
+ @stack_mock = mock 'stack mock'
25
+ @stack_mock.stub(:attributes) { @attributes }
17
26
 
18
27
  options = { :config => @config_mock,
19
28
  :instances => ['1.2.3.4', '4.3.2.1'],
@@ -30,6 +39,23 @@ describe SimpleDeploy do
30
39
  end
31
40
 
32
41
  describe "creating a deploy" do
42
+ it "should gracefully tell the user there are no running instances" do
43
+ options = { :config => @config_mock,
44
+ :instances => [],
45
+ :environment => 'test-us-west-1',
46
+ :ssh_user => 'user',
47
+ :ssh_key => 'key',
48
+ :stack => @stack_mock,
49
+ :name => 'stack-name' }
50
+ stack = SimpleDeploy::Stack::Deployment.new options
51
+
52
+ expect {
53
+ stack.create_deployment
54
+ }.to raise_error(RuntimeError, 'There are no running instances to deploy to')
55
+ end
56
+ end
57
+
58
+ describe "executing a deploy" do
33
59
  before do
34
60
  @deployment_mock = mock 'cap config'
35
61
  @variables_mock = mock 'variables mock'
@@ -50,8 +76,6 @@ describe SimpleDeploy do
50
76
  and_return ['cookbooks']
51
77
  @config_mock.should_receive(:artifact_deploy_variable).with('cookbooks').
52
78
  and_return 'deploy_var'
53
- @config_mock.should_receive(:artifact_bucket_prefix).with('cookbooks').
54
- and_return 'bucket_prefix'
55
79
  SimpleDeploy::Artifact.should_receive(:new).and_return @artifact_mock
56
80
  @artifact_mock.should_receive(:endpoints).
57
81
  and_return('s3' => 's3://bucket/dir/key')
@@ -67,7 +91,7 @@ describe SimpleDeploy do
67
91
  status_mock = mock 'status mock'
68
92
  SimpleDeploy::Stack::Deployment::Status.should_receive(:new).
69
93
  and_return status_mock
70
- status_mock.should_receive(:cleared_to_deploy?).with(false).and_return true
94
+ status_mock.should_receive(:clear_for_deployment?).and_return true
71
95
  status_mock.should_receive(:set_deployment_in_progress)
72
96
  @deployment_mock.should_receive(:simpledeploy)
73
97
  status_mock.should_receive(:unset_deployment_in_progress)
@@ -78,21 +102,37 @@ describe SimpleDeploy do
78
102
  status_mock = mock 'status mock'
79
103
  SimpleDeploy::Stack::Deployment::Status.should_receive(:new).
80
104
  and_return status_mock
81
- status_mock.should_receive(:cleared_to_deploy?).with(true).and_return true
105
+ status_mock.should_receive(:clear_for_deployment?).and_return true
82
106
  status_mock.should_receive(:set_deployment_in_progress)
83
107
  @deployment_mock.should_receive(:simpledeploy)
84
108
  status_mock.should_receive(:unset_deployment_in_progress)
85
109
  @stack.execute(true).should == true
86
110
  end
87
111
 
88
- it "should deploy if the stack is not clear to deploy but forced" do
112
+ it "should not deploy if the stack is not clear to deploy and not forced" do
89
113
  status_mock = mock 'status mock'
90
114
  SimpleDeploy::Stack::Deployment::Status.should_receive(:new).
91
115
  and_return status_mock
92
- status_mock.should_receive(:cleared_to_deploy?).with(false).and_return false
116
+ status_mock.should_receive(:clear_for_deployment?).and_return false
93
117
  @logger_stub.should_receive(:error)
94
118
  @stack.execute.should == false
95
119
  end
96
120
  end
97
121
 
122
+ describe "get_artifact_endpoints" do
123
+ before do
124
+ @config_mock.stub(:artifacts) { ['chef_repo', 'cookbooks', 'app'] }
125
+ @config_mock.should_receive(:artifact_deploy_variable).with('chef_repo').and_return('CHEF_REPO_URL')
126
+ @config_mock.should_receive(:artifact_deploy_variable).with('app').and_return('APP_URL')
127
+ @config_mock.should_receive(:artifact_deploy_variable).with('cookbooks').and_return('COOKBOOKS_URL')
128
+ end
129
+
130
+ it "should create S3 endpoints" do
131
+ endpoints = @stack.send :get_artifact_endpoints
132
+
133
+ endpoints['CHEF_REPO_URL'].should == 's3://chef_repo_bp-test-us-west-1/chef_repo_d/chef_repo.tar.gz'
134
+ endpoints['APP_URL'].should == 's3://app_bp-test-us-west-1/app_d/app.tar.gz'
135
+ endpoints['COOKBOOKS_URL'].should == 's3://cookbooks_bp-test-us-west-1/cookbooks_d/cookbooks.tar.gz'
136
+ end
137
+ end
98
138
  end
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  describe SimpleDeploy do
2
4
  before do
3
5
  @config_mock = mock 'config mock'
@@ -7,7 +9,8 @@ describe SimpleDeploy do
7
9
  @config_mock.should_receive(:logger).and_return @logger_mock
8
10
 
9
11
  options = { :config => @config_mock,
10
- :environment => 'preprod' }
12
+ :environment => 'preprod',
13
+ :main_attributes => { 'chef_repo_bucket_prefix' => 'test-prefix' } }
11
14
  @formater = SimpleDeploy::StackAttributeFormater.new options
12
15
  end
13
16
 
@@ -20,9 +23,6 @@ describe SimpleDeploy do
20
23
  :config => @config_mock,
21
24
  :bucket_prefix => 'test-prefix').
22
25
  and_return artifact_mock
23
- @config_mock.should_receive(:artifact_bucket_prefix).with('chef_repo').
24
- exactly(2).times.
25
- and_return('test-prefix')
26
26
  @config_mock.should_receive(:artifact_cloud_formation_url).with('chef_repo').
27
27
  exactly(2).times.
28
28
  and_return('CookBooksURL')
data/spec/stack_spec.rb CHANGED
@@ -2,35 +2,41 @@ require 'spec_helper'
2
2
 
3
3
  describe SimpleDeploy do
4
4
 
5
- describe "A stack" do
5
+ before do
6
+ @logger_stub = stub 'logger stub', :info => 'true', :warn => 'true'
6
7
 
7
- before do
8
- @config_mock = mock 'config mock'
9
- @logger_mock = mock 'logger mock'
10
- @config_mock.should_receive(:logger).and_return @logger_mock
11
- SimpleDeploy::Config.should_receive(:new).
12
- with(:logger => 'my-logger').
13
- and_return @config_mock
14
- @stack = SimpleDeploy::Stack.new :environment => 'test-env',
15
- :name => 'test-stack',
16
- :logger => 'my-logger',
17
- :config => @config_mock
18
- end
8
+ @environment_config_mock = mock 'environment config mock'
9
+ @config_mock = mock 'config mock'
10
+ @config_mock.should_receive(:logger).and_return @logger_stub
11
+ @config_mock.stub(:environment).and_return(@environment_config_mock)
12
+
13
+ SimpleDeploy::Config.should_receive(:new).
14
+ with(:logger => 'my-logger').
15
+ and_return @config_mock
16
+ @stack = SimpleDeploy::Stack.new :environment => 'test-env',
17
+ :name => 'test-stack',
18
+ :logger => 'my-logger',
19
+ :config => @config_mock
20
+
21
+ @main_attributes = { 'arg1_bucket_prefix' => 'arg1_bp' }
22
+ end
19
23
 
24
+ describe "A stack" do
20
25
  it "should call create stack" do
21
26
  saf_mock = mock 'stack attribute formater mock'
22
27
  stack_mock = mock 'stackster stack mock'
23
- environment_config_mock = mock 'environment config mock'
28
+ stack_mock.stub(:attributes).and_return(@main_attributes)
24
29
  @config_mock.should_receive(:environment).with('test-env').
25
- and_return environment_config_mock
30
+ and_return @environment_config_mock
26
31
  SimpleDeploy::StackAttributeFormater.should_receive(:new).
27
32
  with(:config => @config_mock,
28
- :environment => 'test-env').
33
+ :environment => 'test-env',
34
+ :main_attributes => @main_attributes).
29
35
  and_return saf_mock
30
36
  Stackster::Stack.should_receive(:new).with(:environment => 'test-env',
31
37
  :name => 'test-stack',
32
- :config => environment_config_mock,
33
- :logger => @logger_mock).
38
+ :config => @environment_config_mock,
39
+ :logger => @logger_stub).
34
40
  and_return stack_mock
35
41
  saf_mock.should_receive(:updated_attributes).with({'arg1' => 'val'}).
36
42
  and_return('arg1' => 'new_val')
@@ -41,20 +47,51 @@ describe SimpleDeploy do
41
47
  end
42
48
 
43
49
  it "should call update stack" do
50
+ deployment_stub = stub 'deployment', :clear_for_deployment? => true
51
+ @stack.stub(:deployment).and_return(deployment_stub)
52
+
53
+ saf_mock = mock 'stack attribute formater mock'
54
+ stack_mock = mock 'stackster stack mock'
55
+ stack_mock.stub(:attributes).and_return(@main_attributes)
56
+ @config_mock.should_receive(:environment).with('test-env').
57
+ and_return @environment_config_mock
58
+ SimpleDeploy::StackAttributeFormater.should_receive(:new).
59
+ with(:config => @config_mock,
60
+ :environment => 'test-env',
61
+ :main_attributes => @main_attributes).
62
+ and_return saf_mock
63
+ Stackster::Stack.should_receive(:new).with(:environment => 'test-env',
64
+ :name => 'test-stack',
65
+ :config => @environment_config_mock,
66
+ :logger => @logger_stub).
67
+ and_return stack_mock
68
+ saf_mock.should_receive(:updated_attributes).with({'arg1' => 'val'}).
69
+ and_return('arg1' => 'new_val')
70
+ stack_mock.should_receive(:update).with :attributes => { 'arg1' => 'new_val' }
71
+ @stack.update :attributes => { 'arg1' => 'val' }
72
+ end
73
+
74
+ end
75
+
76
+ describe "updating a stack" do
77
+ it "should update when the deployment is not locked" do
78
+ deployment_stub = stub 'deployment', :clear_for_deployment? => true
79
+ @stack.stub(:deployment).and_return(deployment_stub)
80
+
44
81
  saf_mock = mock 'stack attribute formater mock'
45
82
  stack_mock = mock 'stackster stack mock'
46
- environment_config_mock = mock 'environment config mock'
47
- @logger_mock.should_receive(:info).exactly(2).times
83
+ stack_mock.stub(:attributes).and_return(@main_attributes)
48
84
  @config_mock.should_receive(:environment).with('test-env').
49
- and_return environment_config_mock
85
+ and_return @environment_config_mock
50
86
  SimpleDeploy::StackAttributeFormater.should_receive(:new).
51
87
  with(:config => @config_mock,
52
- :environment => 'test-env').
88
+ :environment => 'test-env',
89
+ :main_attributes => @main_attributes).
53
90
  and_return saf_mock
54
91
  Stackster::Stack.should_receive(:new).with(:environment => 'test-env',
55
92
  :name => 'test-stack',
56
- :config => environment_config_mock,
57
- :logger => @logger_mock).
93
+ :config => @environment_config_mock,
94
+ :logger => @logger_stub).
58
95
  and_return stack_mock
59
96
  saf_mock.should_receive(:updated_attributes).with({'arg1' => 'val'}).
60
97
  and_return('arg1' => 'new_val')
@@ -62,5 +99,88 @@ describe SimpleDeploy do
62
99
  @stack.update :attributes => { 'arg1' => 'val' }
63
100
  end
64
101
 
102
+ it "should not update when the deployment is locked and force is not set" do
103
+ deployment_stub = stub 'deployment', :clear_for_deployment? => false
104
+ @stack.stub(:deployment).and_return(deployment_stub)
105
+
106
+ SimpleDeploy::StackAttributeFormater.should_not_receive(:new)
107
+ Stackster::Stack.should_not_receive(:new)
108
+
109
+ @stack.update :attributes => { 'arg1' => 'val' }
110
+ end
111
+
112
+ it "should update when the deployment is locked and force is set true" do
113
+ deployment_mock = mock 'deployment'
114
+ deployment_mock.should_receive(:clear_for_deployment?).and_return(false, true)
115
+ deployment_mock.should_receive(:clear_deployment_lock).with(true)
116
+ @stack.stub(:deployment).and_return(deployment_mock)
117
+
118
+ saf_mock = mock 'stack attribute formater mock'
119
+ stack_mock = mock 'stackster stack mock'
120
+ stack_mock.stub(:attributes).and_return(@main_attributes)
121
+ SimpleDeploy::StackAttributeFormater.should_receive(:new).
122
+ with(:config => @config_mock,
123
+ :environment => 'test-env',
124
+ :main_attributes => @main_attributes).
125
+ and_return saf_mock
126
+ Stackster::Stack.should_receive(:new).with(:environment => 'test-env',
127
+ :name => 'test-stack',
128
+ :config => @environment_config_mock,
129
+ :logger => @logger_stub).
130
+ and_return stack_mock
131
+ saf_mock.should_receive(:updated_attributes).with({'arg1' => 'val'}).
132
+ and_return('arg1' => 'new_val')
133
+ stack_mock.should_receive(:update).with :attributes => { 'arg1' => 'new_val' }
134
+ @stack.update :force => true, :attributes => { 'arg1' => 'val' }
135
+ end
136
+
137
+ it "should not update when the deployment is locked and force is set false" do
138
+ deployment_stub = stub 'deployment', :clear_for_deployment? => false
139
+ @stack.stub(:deployment).and_return(deployment_stub)
140
+
141
+ SimpleDeploy::StackAttributeFormater.should_not_receive(:new)
142
+ Stackster::Stack.should_not_receive(:new)
143
+
144
+ @stack.update :force => false, :attributes => { 'arg1' => 'val' }
145
+ end
146
+ end
147
+
148
+ describe "destroying a stack" do
149
+ it "should destroy if the stack is not protected" do
150
+ stack_mock = mock 'stackster stack mock', :attributes => { 'protection' => 'off' }
151
+ @stack.stub(:stack) { stack_mock }
152
+
153
+ stack_mock.should_receive(:destroy)
154
+
155
+ @stack.destroy.should be_true
156
+ end
157
+
158
+ it "should not destroy if the stack is protected" do
159
+ stack_mock = mock 'stackster stack mock', :attributes => { 'protection' => 'on' }
160
+ @stack.stub(:stack) { stack_mock }
161
+
162
+ stack_mock.should_not_receive(:destroy)
163
+
164
+ @stack.destroy.should_not be_true
165
+ end
166
+
167
+ it "should destroy if protection is undefined" do
168
+ stack_mock = mock 'stackster stack mock', :attributes => {}
169
+ @stack.stub(:stack) { stack_mock }
170
+
171
+ stack_mock.should_receive(:destroy)
172
+
173
+ @stack.destroy.should be_true
174
+ end
175
+
176
+ it "should not create a deployment" do
177
+ @stack.should_not_receive(:deployment)
178
+
179
+ stack_mock = mock 'stackster stack mock', :attributes => { 'protection' => 'off' }
180
+ @stack.stub(:stack) { stack_mock }
181
+ stack_mock.should_receive(:destroy)
182
+
183
+ @stack.destroy.should be_true
184
+ end
65
185
  end
66
186
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_deploy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.8
4
+ version: 0.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-03 00:00:00.000000000 Z
12
+ date: 2012-09-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &70332846279420 !ruby/object:Gem::Requirement
16
+ requirement: &2152015560 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70332846279420
24
+ version_requirements: *2152015560
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: capistrano
27
- requirement: &70332846278440 !ruby/object:Gem::Requirement
27
+ requirement: &2152014980 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70332846278440
35
+ version_requirements: *2152014980
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: stackster
38
- requirement: &70332846256720 !ruby/object:Gem::Requirement
38
+ requirement: &2152014180 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - =
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 0.2.9
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70332846256720
46
+ version_requirements: *2152014180
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: tinder
49
- requirement: &70332846255020 !ruby/object:Gem::Requirement
49
+ requirement: &2152176220 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *70332846255020
57
+ version_requirements: *2152176220
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: trollop
60
- requirement: &70332846253680 !ruby/object:Gem::Requirement
60
+ requirement: &2152175540 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,7 +65,7 @@ dependencies:
65
65
  version: '0'
66
66
  type: :runtime
67
67
  prerelease: false
68
- version_requirements: *70332846253680
68
+ version_requirements: *2152175540
69
69
  description: I am designed to deploy artifacts uploaded by Heirloom
70
70
  email:
71
71
  - brett@weav.net
@@ -94,6 +94,7 @@ files:
94
94
  - lib/simple_deploy/cli/list.rb
95
95
  - lib/simple_deploy/cli/outputs.rb
96
96
  - lib/simple_deploy/cli/parameters.rb
97
+ - lib/simple_deploy/cli/protect.rb
97
98
  - lib/simple_deploy/cli/resources.rb
98
99
  - lib/simple_deploy/cli/shared.rb
99
100
  - lib/simple_deploy/cli/ssh.rb
@@ -113,6 +114,10 @@ files:
113
114
  - simple_deploy.gemspec
114
115
  - spec/artifact_spec.rb
115
116
  - spec/cli/attributes_spec.rb
117
+ - spec/cli/deploy_spec.rb
118
+ - spec/cli/destroy_spec.rb
119
+ - spec/cli/protect_spec.rb
120
+ - spec/cli/update_spec.rb
116
121
  - spec/cli_spec.rb
117
122
  - spec/config_spec.rb
118
123
  - spec/logger_spec.rb
@@ -135,27 +140,25 @@ required_ruby_version: !ruby/object:Gem::Requirement
135
140
  - - ! '>='
136
141
  - !ruby/object:Gem::Version
137
142
  version: '0'
138
- segments:
139
- - 0
140
- hash: 2975550354035595728
141
143
  required_rubygems_version: !ruby/object:Gem::Requirement
142
144
  none: false
143
145
  requirements:
144
146
  - - ! '>='
145
147
  - !ruby/object:Gem::Version
146
148
  version: '0'
147
- segments:
148
- - 0
149
- hash: 2975550354035595728
150
149
  requirements: []
151
150
  rubyforge_project: simple_deploy
152
- rubygems_version: 1.8.16
151
+ rubygems_version: 1.8.10
153
152
  signing_key:
154
153
  specification_version: 3
155
154
  summary: I help with deployments
156
155
  test_files:
157
156
  - spec/artifact_spec.rb
158
157
  - spec/cli/attributes_spec.rb
158
+ - spec/cli/deploy_spec.rb
159
+ - spec/cli/destroy_spec.rb
160
+ - spec/cli/protect_spec.rb
161
+ - spec/cli/update_spec.rb
159
162
  - spec/cli_spec.rb
160
163
  - spec/config_spec.rb
161
164
  - spec/logger_spec.rb