simple_deploy 0.4.8 → 0.5.0

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