engineyard 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/engineyard.rb +1 -1
- data/lib/engineyard/api.rb +6 -2
- data/lib/engineyard/cli.rb +52 -7
- data/lib/engineyard/cli/api.rb +0 -4
- data/lib/engineyard/cli/recipes.rb +13 -5
- data/lib/engineyard/collection/environments.rb +1 -2
- data/lib/engineyard/error.rb +24 -35
- data/lib/engineyard/model/environment.rb +38 -1
- data/lib/engineyard/model/instance.rb +30 -1
- data/lib/engineyard/thor.rb +10 -4
- data/spec/engineyard/model/environment_spec.rb +64 -2
- data/spec/ey/deploy_spec.rb +24 -51
- data/spec/ey/ey_spec.rb +1 -1
- data/spec/ey/recipes/apply_spec.rb +42 -0
- data/spec/ey/recipes/upload_spec.rb +40 -0
- data/spec/ey/rollback_spec.rb +50 -0
- data/spec/ey/ssh_spec.rb +8 -3
- data/spec/spec_helper.rb +9 -0
- data/spec/support/fake_awsm.ru +19 -2
- data/spec/support/git_repo.rb +21 -0
- data/spec/support/helpers.rb +25 -14
- metadata +8 -6
- data/lib/engineyard/cli/action/deploy.rb +0 -120
- data/spec/ey/upload_recipes_spec.rb +0 -54
data/lib/engineyard.rb
CHANGED
data/lib/engineyard/api.rb
CHANGED
@@ -34,6 +34,10 @@ module EY
|
|
34
34
|
apps.find{|a| repo.urls.include?(a.repository_uri) }
|
35
35
|
end
|
36
36
|
|
37
|
+
def app_for_repo!(repo)
|
38
|
+
app_for_repo(repo) || raise(NoAppError.new(repo))
|
39
|
+
end
|
40
|
+
|
37
41
|
class InvalidCredentials < EY::Error; end
|
38
42
|
class RequestFailed < EY::Error; end
|
39
43
|
|
@@ -69,8 +73,8 @@ module EY
|
|
69
73
|
raise RequestFailed, "SSL is misconfigured on your cloud"
|
70
74
|
end
|
71
75
|
|
72
|
-
if resp.
|
73
|
-
data =
|
76
|
+
if resp.body.empty?
|
77
|
+
data = ''
|
74
78
|
else
|
75
79
|
begin
|
76
80
|
data = JSON.parse(resp.body)
|
data/lib/engineyard/cli.rb
CHANGED
@@ -21,11 +21,39 @@ module EY
|
|
21
21
|
method_option :migrate, :type => :string, :aliases => %w(-m),
|
22
22
|
:default => 'rake db:migrate',
|
23
23
|
:desc => "Run migrations via [MIGRATE], defaults to 'rake db:migrate'; use --no-migrate to avoid running migrations"
|
24
|
-
method_option :install_eysd, :type => :boolean, :aliases => %(-s),
|
25
|
-
:desc => "Force remote install of eysd"
|
26
24
|
def deploy(env_name = nil, branch = nil)
|
27
|
-
|
28
|
-
|
25
|
+
app = api.app_for_repo!(repo)
|
26
|
+
environment = fetch_environment(env_name, app)
|
27
|
+
deploy_branch = environment.resolve_branch(branch, options[:force]) ||
|
28
|
+
repo.current_branch ||
|
29
|
+
raise(DeployArgumentError)
|
30
|
+
|
31
|
+
EY.ui.info "Connecting to the server..."
|
32
|
+
|
33
|
+
environment.ensure_eysd_present! do |action|
|
34
|
+
case action
|
35
|
+
when :installing
|
36
|
+
EY.ui.warn "Instance does not have server-side component installed"
|
37
|
+
EY.ui.info "Installing server-side component..."
|
38
|
+
when :upgrading
|
39
|
+
EY.ui.info "Upgrading server-side component..."
|
40
|
+
else
|
41
|
+
# nothing slow is happening, so there's nothing to say
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
EY.ui.info "Running deploy for '#{environment.name}' on server..."
|
46
|
+
|
47
|
+
if environment.deploy!(app, deploy_branch, options[:migrate])
|
48
|
+
EY.ui.info "Deploy complete"
|
49
|
+
else
|
50
|
+
raise EY::Error, "Deploy failed"
|
51
|
+
end
|
52
|
+
|
53
|
+
rescue NoEnvironmentError => e
|
54
|
+
# Give better feedback about why we couldn't find the environment.
|
55
|
+
exists = api.environments.named(env_name)
|
56
|
+
raise exists ? EnvironmentUnlinkedError.new(env_name) : e
|
29
57
|
end
|
30
58
|
|
31
59
|
desc "environments [--all]", "List cloud environments for this app, or all environments"
|
@@ -37,14 +65,31 @@ module EY
|
|
37
65
|
end
|
38
66
|
map "envs" => :environments
|
39
67
|
|
40
|
-
desc "rebuild [
|
68
|
+
desc "rebuild [ENVIRONMENT]", "Rebuild environment (ensure configuration is up-to-date)"
|
41
69
|
def rebuild(name = nil)
|
42
70
|
env = fetch_environment(name)
|
43
71
|
EY.ui.debug("Rebuilding #{env.name}")
|
44
72
|
env.rebuild
|
45
73
|
end
|
46
74
|
|
47
|
-
desc "
|
75
|
+
desc "rollback [ENVIRONMENT]", "Rollback to the previous deploy"
|
76
|
+
def rollback(name = nil)
|
77
|
+
app = api.app_for_repo!(repo)
|
78
|
+
env = fetch_environment(name)
|
79
|
+
|
80
|
+
if env.app_master
|
81
|
+
EY.ui.info("Rolling back #{env.name}")
|
82
|
+
if env.rollback!(app)
|
83
|
+
EY.ui.info "Rollback complete"
|
84
|
+
else
|
85
|
+
raise EY::Error, "Rollback failed"
|
86
|
+
end
|
87
|
+
else
|
88
|
+
raise NoAppMaster.new(env.name)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
desc "ssh [ENVIRONMENT]", "Open an ssh session to the environment's application server"
|
48
93
|
def ssh(name = nil)
|
49
94
|
env = fetch_environment(name)
|
50
95
|
|
@@ -55,7 +100,7 @@ module EY
|
|
55
100
|
end
|
56
101
|
end
|
57
102
|
|
58
|
-
desc "logs [
|
103
|
+
desc "logs [ENVIRONMENT]", "Retrieve the latest logs for an environment"
|
59
104
|
def logs(name = nil)
|
60
105
|
fetch_environment(name).logs.each do |log|
|
61
106
|
EY.ui.info log.instance_name
|
data/lib/engineyard/cli/api.rb
CHANGED
@@ -23,10 +23,6 @@ module EY
|
|
23
23
|
@token = self.class.fetch_token
|
24
24
|
end
|
25
25
|
|
26
|
-
def fetch_app_for_repo(repo)
|
27
|
-
app_for_repo(repo) || raise(NoAppError.new(repo))
|
28
|
-
end
|
29
|
-
|
30
26
|
def self.fetch_token
|
31
27
|
EY.ui.warn("The engineyard gem is prerelease software. Please do not use")
|
32
28
|
EY.ui.warn("this tool to deploy to mission-critical environments, yet.")
|
@@ -1,14 +1,22 @@
|
|
1
1
|
module EY
|
2
2
|
class CLI
|
3
3
|
class Recipes < EY::Thor
|
4
|
+
|
5
|
+
desc "recipes apply [ENV]", "Apply uploaded chef recipes on ENV"
|
6
|
+
def apply(name = nil)
|
7
|
+
environment = fetch_environment(name)
|
8
|
+
environment.run_custom_recipes
|
9
|
+
EY.ui.say "Uploaded recipes started for #{environment.name}"
|
10
|
+
end
|
11
|
+
|
4
12
|
desc "recipes upload [ENV]", "Upload custom chef recipes from the current directory to ENV"
|
5
13
|
def upload(name = nil)
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
EY.ui.error "Recipes upload failed"
|
10
|
-
end
|
14
|
+
environment = fetch_environment(name)
|
15
|
+
environment.upload_recipes
|
16
|
+
EY.ui.say "Recipes uploaded successfully for #{environment.name}"
|
11
17
|
end
|
18
|
+
|
12
19
|
end
|
13
20
|
end
|
21
|
+
|
14
22
|
end
|
@@ -18,8 +18,7 @@ module EY
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def match_one!(name_part)
|
21
|
-
match_one(name_part) or raise
|
22
|
-
"Environment containing '#{name_part}' can't be found\nYou can create it at #{EY.config.endpoint}"
|
21
|
+
match_one(name_part) or raise NoEnvironmentError.new(name_part)
|
23
22
|
end
|
24
23
|
|
25
24
|
private
|
data/lib/engineyard/error.rb
CHANGED
@@ -9,76 +9,65 @@ module EY
|
|
9
9
|
|
10
10
|
class NoAppError < Error
|
11
11
|
def initialize(repo)
|
12
|
-
@repo = repo
|
13
|
-
end
|
14
|
-
|
15
|
-
def message
|
16
12
|
error = [%|There is no application configured for any of the following remotes:|]
|
17
|
-
|
13
|
+
repo.urls.each{|url| error << %|\t#{url}| }
|
18
14
|
error << %|You can add this application at #{EY.config.endpoint}|
|
19
|
-
|
15
|
+
super error.join("\n")
|
20
16
|
end
|
21
17
|
end
|
22
18
|
|
23
19
|
class NoAppMaster < EY::Error
|
24
20
|
def initialize(env_name)
|
25
|
-
|
21
|
+
super "The environment '#{env_name}' does not have a master instance."
|
26
22
|
end
|
23
|
+
end
|
27
24
|
|
28
|
-
|
29
|
-
|
25
|
+
class BadAppMasterStatus < EY::Error
|
26
|
+
def initialize(master_status)
|
27
|
+
super "Application master's status is not \"running\" (green); it is \"#{master_status}\"."
|
30
28
|
end
|
31
29
|
end
|
32
30
|
|
33
31
|
class EnvironmentError < EY::Error
|
34
32
|
end
|
35
33
|
|
36
|
-
class AmbiguousEnvironmentName < EY::
|
34
|
+
class AmbiguousEnvironmentName < EY::EnvironmentError
|
37
35
|
def initialize(name, matches)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
def message
|
42
|
-
pretty_names = @matches.map {|x| "'#{x}'"}.join(', ')
|
43
|
-
"The name '#{@name}' is ambiguous; it matches all of the following environment names: #{pretty_names}.\nPlease use a longer, unambiguous substring or the entire environment name."
|
36
|
+
pretty_names = matches.map {|x| "'#{x}'"}.join(', ')
|
37
|
+
super "The name '#{name}' is ambiguous; it matches all of the following environment names: #{pretty_names}.\n" +
|
38
|
+
"Please use a longer, unambiguous substring or the entire environment name."
|
44
39
|
end
|
45
40
|
end
|
46
41
|
|
47
|
-
class NoSingleEnvironmentError < EY::
|
42
|
+
class NoSingleEnvironmentError < EY::EnvironmentError
|
48
43
|
def initialize(app)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
def message
|
53
|
-
"Unable to determine a single environment for the current application (found #{@envs.size} environments)"
|
44
|
+
size = app.environments.size
|
45
|
+
super "Unable to determine a single environment for the current application (found #{size} environments)"
|
54
46
|
end
|
55
47
|
end
|
56
48
|
|
57
|
-
class NoEnvironmentError < EY::
|
49
|
+
class NoEnvironmentError < EY::EnvironmentError
|
58
50
|
def initialize(env_name=nil)
|
59
|
-
|
51
|
+
super "No environment named '#{env_name}'\nYou can create one at #{EY.config.endpoint}"
|
60
52
|
end
|
53
|
+
end
|
61
54
|
|
62
|
-
|
63
|
-
|
55
|
+
class EnvironmentUnlinkedError < EY::Error
|
56
|
+
def initialize(env_name)
|
57
|
+
super "Environment '#{env_name}' exists but does not run this application."
|
64
58
|
end
|
65
59
|
end
|
66
60
|
|
67
61
|
class BranchMismatch < EY::Error
|
68
62
|
def initialize(default_branch, branch)
|
69
|
-
super
|
70
|
-
|
71
|
-
end
|
72
|
-
|
73
|
-
def message
|
74
|
-
%|Your deploy branch is set to "#{@default_branch}".\n| +
|
75
|
-
%|If you want to deploy branch "#{@branch}", use --force.|
|
63
|
+
super %|Your deploy branch is set to "#{default_branch}".\n| +
|
64
|
+
%|If you want to deploy branch "#{branch}", use --force.|
|
76
65
|
end
|
77
66
|
end
|
78
67
|
|
79
68
|
class DeployArgumentError < EY::Error
|
80
|
-
def
|
81
|
-
%|"deploy" was called incorrectly. Call as "deploy [ENVIRONMENT] [BRANCH]"\n| +
|
69
|
+
def initialize
|
70
|
+
super %|"deploy" was called incorrectly. Call as "deploy [ENVIRONMENT] [BRANCH]"\n| +
|
82
71
|
%|You can set default environments and branches in ey.yml|
|
83
72
|
end
|
84
73
|
end
|
@@ -21,10 +21,36 @@ module EY
|
|
21
21
|
Instance.from_array(api_get("/environments/#{id}/instances")["instances"], :environment => self)
|
22
22
|
end
|
23
23
|
|
24
|
+
def app_master!
|
25
|
+
master = app_master
|
26
|
+
if master.nil?
|
27
|
+
raise NoAppMaster.new(name)
|
28
|
+
elsif master.status != "running"
|
29
|
+
raise BadAppMasterStatus.new(master.status)
|
30
|
+
end
|
31
|
+
master
|
32
|
+
end
|
33
|
+
|
34
|
+
def ensure_eysd_present!(&blk)
|
35
|
+
app_master!.ensure_eysd_present!(&blk)
|
36
|
+
end
|
37
|
+
|
38
|
+
def deploy!(app, ref, migration_command=nil)
|
39
|
+
app_master!.deploy!(app, ref, migration_command, config)
|
40
|
+
end
|
41
|
+
|
42
|
+
def rollback!(app)
|
43
|
+
app_master!.rollback!(app, config)
|
44
|
+
end
|
45
|
+
|
24
46
|
def rebuild
|
25
47
|
api.request("/environments/#{id}/rebuild", :method => :put)
|
26
48
|
end
|
27
49
|
|
50
|
+
def run_custom_recipes
|
51
|
+
api.request("/environments/#{id}/run_custom_recipes", :method => :put)
|
52
|
+
end
|
53
|
+
|
28
54
|
def upload_recipes(file_to_upload = recipe_file)
|
29
55
|
api.request("/environments/#{id}/recipes",
|
30
56
|
:method => :post,
|
@@ -39,7 +65,7 @@ module EY
|
|
39
65
|
end
|
40
66
|
|
41
67
|
tmp = Tempfile.new("recipes")
|
42
|
-
cmd = "
|
68
|
+
cmd = "tar czf '#{tmp.path}' cookbooks/"
|
43
69
|
|
44
70
|
unless system(cmd)
|
45
71
|
raise EY::Error, "Could not archive recipes.\nCommand `#{cmd}` exited with an error."
|
@@ -48,11 +74,22 @@ module EY
|
|
48
74
|
tmp
|
49
75
|
end
|
50
76
|
|
77
|
+
def resolve_branch(branch, allow_non_default_branch=false)
|
78
|
+
if !allow_non_default_branch && branch && default_branch && (branch != default_branch)
|
79
|
+
raise BranchMismatch.new(default_branch, branch)
|
80
|
+
end
|
81
|
+
branch || default_branch
|
82
|
+
end
|
83
|
+
|
51
84
|
def configuration
|
52
85
|
EY.config.environments[self.name]
|
53
86
|
end
|
54
87
|
alias_method :config, :configuration
|
55
88
|
|
89
|
+
def default_branch
|
90
|
+
EY.config.default_branch(name)
|
91
|
+
end
|
92
|
+
|
56
93
|
def shorten_name_for(app)
|
57
94
|
name.gsub(/^#{Regexp.quote(app.name)}_/, '')
|
58
95
|
end
|
@@ -3,7 +3,7 @@ require 'escape'
|
|
3
3
|
module EY
|
4
4
|
module Model
|
5
5
|
class Instance < ApiStruct.new(:id, :role, :status, :amazon_id, :public_hostname, :environment)
|
6
|
-
EYSD_VERSION = "~>0.
|
6
|
+
EYSD_VERSION = "~>0.4.0"
|
7
7
|
CHECK_SCRIPT = <<-SCRIPT
|
8
8
|
require "rubygems"
|
9
9
|
requirement = Gem::Requirement.new("#{EYSD_VERSION}")
|
@@ -43,6 +43,25 @@ exit(17) # required_version < current_version
|
|
43
43
|
ssh Escape.shell_command(deploy_cmd)
|
44
44
|
end
|
45
45
|
|
46
|
+
def ensure_eysd_present!
|
47
|
+
case ey_deploy_check
|
48
|
+
when :ssh_failed
|
49
|
+
raise EnvironmentError, "SSH connection to #{hostname} failed"
|
50
|
+
when :eysd_missing
|
51
|
+
yield :installing if block_given?
|
52
|
+
install_ey_deploy!
|
53
|
+
when :too_new
|
54
|
+
raise EnvironmentError, "server-side component too new; please upgrade your copy of the engineyard gem."
|
55
|
+
when :too_old
|
56
|
+
yield :upgrading if block_given?
|
57
|
+
upgrade_ey_deploy!
|
58
|
+
when :ok
|
59
|
+
# no action needed
|
60
|
+
else
|
61
|
+
raise EY::Error, "Internal error: Unexpected status from Instance#ey_deploy_check; got #{eysd_status.inspect}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
46
65
|
def ey_deploy_check
|
47
66
|
require 'base64'
|
48
67
|
encoded_script = Base64.encode64(CHECK_SCRIPT).gsub(/\n/, '')
|
@@ -54,6 +73,16 @@ exit(17) # required_version < current_version
|
|
54
73
|
ssh(Escape.shell_command(['sudo', gem_path, 'install', 'ey-deploy', '-v', EYSD_VERSION]))
|
55
74
|
end
|
56
75
|
|
76
|
+
def rollback!(app, extra_configuration=nil)
|
77
|
+
deploy_cmd = [eysd_path, 'deploy', 'rollback', '--app', app.name]
|
78
|
+
|
79
|
+
if extra_configuration
|
80
|
+
deploy_cmd << '--config' << extra_configuration.to_json
|
81
|
+
end
|
82
|
+
|
83
|
+
ssh Escape.shell_command(deploy_cmd)
|
84
|
+
end
|
85
|
+
|
57
86
|
def upgrade_ey_deploy!
|
58
87
|
ssh "sudo #{gem_path} uninstall -a -x ey-deploy"
|
59
88
|
install_ey_deploy!
|
data/lib/engineyard/thor.rb
CHANGED
@@ -20,6 +20,10 @@ module EY
|
|
20
20
|
|
21
21
|
protected
|
22
22
|
|
23
|
+
def self.exit_on_failure?
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
23
27
|
def api
|
24
28
|
@api ||= EY::CLI::API.new
|
25
29
|
end
|
@@ -28,11 +32,13 @@ module EY
|
|
28
32
|
@repo ||= EY::Repo.new
|
29
33
|
end
|
30
34
|
|
31
|
-
|
32
|
-
|
33
|
-
|
35
|
+
# if an app is supplied, it is used as a constraint for implied environment lookup
|
36
|
+
def fetch_environment(env_name, app = nil)
|
37
|
+
env_name ||= EY.config.default_environment
|
38
|
+
if env_name
|
39
|
+
(app || api).environments.match_one!(env_name)
|
34
40
|
else
|
35
|
-
api.
|
41
|
+
(app || api.app_for_repo!(repo)).sole_environment!
|
36
42
|
end
|
37
43
|
end
|
38
44
|
|
@@ -9,9 +9,11 @@ describe "EY::Model::Environment#rebuild" do
|
|
9
9
|
"api" => @api,
|
10
10
|
})
|
11
11
|
|
12
|
-
FakeWeb.register_uri(
|
12
|
+
FakeWeb.register_uri(
|
13
|
+
:put,
|
13
14
|
"https://cloud.engineyard.com/api/v2/environments/#{env.id}/rebuild",
|
14
|
-
:body =>
|
15
|
+
:body => ''
|
16
|
+
)
|
15
17
|
|
16
18
|
env.rebuild
|
17
19
|
|
@@ -19,6 +21,27 @@ describe "EY::Model::Environment#rebuild" do
|
|
19
21
|
end
|
20
22
|
end
|
21
23
|
|
24
|
+
describe "EY::Model::Environment#run_custom_recipes" do
|
25
|
+
it_should_behave_like "it has an api"
|
26
|
+
|
27
|
+
it "hits the rebuild action in the API" do
|
28
|
+
env = EY::Model::Environment.from_hash({
|
29
|
+
"id" => 46534,
|
30
|
+
"api" => @api,
|
31
|
+
})
|
32
|
+
|
33
|
+
FakeWeb.register_uri(
|
34
|
+
:put,
|
35
|
+
"https://cloud.engineyard.com/api/v2/environments/#{env.id}/run_custom_recipes",
|
36
|
+
:body => ''
|
37
|
+
)
|
38
|
+
|
39
|
+
env.run_custom_recipes
|
40
|
+
|
41
|
+
FakeWeb.should have_requested(:put, "https://cloud.engineyard.com/api/v2/environments/#{env.id}/run_custom_recipes")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
22
45
|
describe "EY::Model::Environment.from_array" do
|
23
46
|
it "returns a smart collection, not just a dumb array" do
|
24
47
|
api_data = [
|
@@ -57,6 +80,45 @@ describe "EY::Model::Environment#instances" do
|
|
57
80
|
end
|
58
81
|
end
|
59
82
|
|
83
|
+
describe "EY::Model::Environment#app_master!" do
|
84
|
+
def make_env_with_master(app_master)
|
85
|
+
if app_master
|
86
|
+
app_master = {
|
87
|
+
"id" => 44206,
|
88
|
+
"role" => "solo",
|
89
|
+
}.merge(app_master)
|
90
|
+
end
|
91
|
+
|
92
|
+
EY::Model::Environment.from_hash({
|
93
|
+
"id" => 11830,
|
94
|
+
"name" => "guinea-pigs-are-delicious",
|
95
|
+
"app_master" => app_master,
|
96
|
+
"instances" => [app_master],
|
97
|
+
})
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
it "returns the app master if it's present and running" do
|
102
|
+
env = make_env_with_master("status" => "running")
|
103
|
+
env.app_master!.should_not be_nil
|
104
|
+
env.app_master!.id.should == 44206
|
105
|
+
end
|
106
|
+
|
107
|
+
it "raises an error if the app master is in a non-running state" do
|
108
|
+
env = make_env_with_master("status" => "error")
|
109
|
+
lambda {
|
110
|
+
env.app_master!
|
111
|
+
}.should raise_error(EY::BadAppMasterStatus)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "raises an error if the app master is absent" do
|
115
|
+
env = make_env_with_master(nil)
|
116
|
+
lambda {
|
117
|
+
env.app_master!
|
118
|
+
}.should raise_error(EY::NoAppMaster)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
60
122
|
describe "EY::Model::Environment#shorten_name_for(app)" do
|
61
123
|
def short(environment_name, app_name)
|
62
124
|
env = EY::Model::Environment.from_hash({:name => environment_name})
|
data/spec/ey/deploy_spec.rb
CHANGED
@@ -41,13 +41,13 @@ describe "ey deploy" do
|
|
41
41
|
it "complains when the specified environment does not contain the app" do
|
42
42
|
api_scenario "one app, one environment, not linked"
|
43
43
|
ey "deploy giblets master", :expect_failure => true
|
44
|
-
@err.should match(/
|
44
|
+
@err.should match(/does not run this application/i)
|
45
45
|
end
|
46
46
|
|
47
|
-
it "complains when environment is
|
47
|
+
it "complains when environment is not specified and app is in >1 environment" do
|
48
48
|
api_scenario "one app, two environments"
|
49
49
|
ey "deploy", :expect_failure => true
|
50
|
-
@err.should match(/
|
50
|
+
@err.should match(/single environment.*2/i)
|
51
51
|
end
|
52
52
|
|
53
53
|
it "complains when the app master is in a non-running state" do
|
@@ -94,48 +94,23 @@ describe "ey deploy" do
|
|
94
94
|
end
|
95
95
|
|
96
96
|
context "choosing something to deploy" do
|
97
|
-
|
98
|
-
|
99
|
-
|
97
|
+
define_git_repo('deploy test') do
|
98
|
+
# we'll have one commit on master
|
99
|
+
system("echo 'source :gemcutter' > Gemfile")
|
100
|
+
system("git add Gemfile")
|
101
|
+
system("git commit -m 'initial commit' >/dev/null 2>&1")
|
100
102
|
|
101
|
-
|
102
|
-
|
103
|
-
Dir.tmpdir,
|
104
|
-
"ey_test_git_#{Time.now.tv_sec}_#{Time.now.tv_usec}_#{$$}")
|
105
|
-
|
106
|
-
Dir.mkdir(@local_git_dir)
|
107
|
-
|
108
|
-
Dir.chdir(@local_git_dir) do
|
109
|
-
[
|
110
|
-
# initial repo setup
|
111
|
-
'git init >/dev/null 2>&1',
|
112
|
-
'git config user.email deploy@spec.test',
|
113
|
-
'git config user.name "Deploy Spec"',
|
114
|
-
'git remote add origin "user@git.host/path/to/repo.git"',
|
115
|
-
|
116
|
-
# we'll have one commit on master
|
117
|
-
"echo 'source :gemcutter' > Gemfile",
|
118
|
-
"git add Gemfile",
|
119
|
-
"git commit -m 'initial commit' >/dev/null 2>&1",
|
120
|
-
|
121
|
-
# and a tag
|
122
|
-
"git tag -a -m 'version one' v1",
|
123
|
-
|
124
|
-
# and we need a non-master branch
|
125
|
-
"git checkout -b current-branch >/dev/null 2>&1",
|
126
|
-
].each do |cmd|
|
127
|
-
system("#{cmd}") or raise "#{cmd} failed"
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
103
|
+
# and a tag
|
104
|
+
system("git tag -a -m 'version one' v1")
|
131
105
|
|
132
|
-
|
133
|
-
|
134
|
-
Dir.chdir(@local_git_dir)
|
106
|
+
# and we need a non-master branch
|
107
|
+
system("git checkout -b current-branch >/dev/null 2>&1")
|
135
108
|
end
|
136
109
|
|
137
|
-
|
138
|
-
|
110
|
+
use_git_repo('deploy test')
|
111
|
+
|
112
|
+
before(:all) do
|
113
|
+
api_scenario "one app, one environment", "user@git.host:path/to/repo.git"
|
139
114
|
end
|
140
115
|
|
141
116
|
context "without a configured default branch" do
|
@@ -156,13 +131,12 @@ describe "ey deploy" do
|
|
156
131
|
end
|
157
132
|
|
158
133
|
context "when there is extra configuration" do
|
159
|
-
before(:
|
160
|
-
write_yaml({"environments" => {"giblets" => {"bert" => "ernie"}}}
|
161
|
-
File.join(@local_git_dir, "ey.yml"))
|
134
|
+
before(:each) do
|
135
|
+
write_yaml({"environments" => {"giblets" => {"bert" => "ernie"}}})
|
162
136
|
end
|
163
137
|
|
164
|
-
after(:
|
165
|
-
File.unlink(
|
138
|
+
after(:each) do
|
139
|
+
File.unlink("ey.yml")
|
166
140
|
end
|
167
141
|
|
168
142
|
it "gets passed along to eysd" do
|
@@ -172,13 +146,12 @@ describe "ey deploy" do
|
|
172
146
|
end
|
173
147
|
|
174
148
|
context "with a configured default branch" do
|
175
|
-
before(:
|
176
|
-
write_yaml({"environments" => {"giblets" => {"branch" => "master"}}}
|
177
|
-
File.join(@local_git_dir, "ey.yml"))
|
149
|
+
before(:each) do
|
150
|
+
write_yaml({"environments" => {"giblets" => {"branch" => "master"}}})
|
178
151
|
end
|
179
152
|
|
180
|
-
after(:
|
181
|
-
File.unlink
|
153
|
+
after(:each) do
|
154
|
+
File.unlink "ey.yml"
|
182
155
|
end
|
183
156
|
|
184
157
|
it "deploys the default branch by default" do
|
data/spec/ey/ey_spec.rb
CHANGED
@@ -9,7 +9,7 @@ describe "ey" do
|
|
9
9
|
|
10
10
|
context "run with an argument that is not a command" do
|
11
11
|
it "tells the user that is not a command" do
|
12
|
-
ey "foobarbaz", :
|
12
|
+
ey "foobarbaz", :expect_failure => true
|
13
13
|
@err.should include("Could not find task")
|
14
14
|
end
|
15
15
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "ey recipes apply" do
|
4
|
+
it_should_behave_like "an integration test"
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
api_scenario "one app, one environment"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "works when the environment name is valid" do
|
11
|
+
ey "recipes apply giblets", :debug => true
|
12
|
+
@out.should =~ /Uploaded recipes started for giblets/i
|
13
|
+
end
|
14
|
+
|
15
|
+
it "runs recipes for the current environment by default" do
|
16
|
+
ey "recipes apply", :debug => true
|
17
|
+
@out.should =~ /Uploaded recipes started for giblets/i
|
18
|
+
end
|
19
|
+
|
20
|
+
it "fails when the environment name is bogus" do
|
21
|
+
ey "recipes apply typo", :expect_failure => true
|
22
|
+
@err.should match(/'typo'/)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "ey recipes apply ENV" do
|
27
|
+
it_should_behave_like "an integration test"
|
28
|
+
|
29
|
+
before(:all) do
|
30
|
+
api_scenario "one app, many similarly-named environments"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "works when given an unambiguous substring" do
|
34
|
+
ey "recipes apply prod", :debug => true
|
35
|
+
@out.should =~ /Uploaded recipes started for railsapp_production/
|
36
|
+
end
|
37
|
+
|
38
|
+
it "complains when given an ambiguous substring" do
|
39
|
+
ey "recipes apply staging", :hide_err => true, :expect_failure => true
|
40
|
+
@err.should =~ /'staging' is ambiguous/
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "ey recipes upload" do
|
4
|
+
it_should_behave_like "an integration test"
|
5
|
+
|
6
|
+
define_git_repo('+cookbooks') do |git_dir|
|
7
|
+
git_dir.join("cookbooks").mkdir
|
8
|
+
File.open(git_dir.join("cookbooks/file"), "w"){|f| f << "boo" }
|
9
|
+
end
|
10
|
+
|
11
|
+
use_git_repo('+cookbooks')
|
12
|
+
|
13
|
+
it "posts the recipes to the correct url" do
|
14
|
+
api_scenario "one app, one environment"
|
15
|
+
ey "recipes upload giblets", :debug => true
|
16
|
+
|
17
|
+
@out.should =~ /Recipes uploaded successfully for giblets/i
|
18
|
+
end
|
19
|
+
|
20
|
+
it "errors correctly on bogus env name" do
|
21
|
+
api_scenario "one app, one environment"
|
22
|
+
ey "recipes upload bogusenv", :expect_failure => true
|
23
|
+
|
24
|
+
@err.should =~ /No environment named 'bogusenv'/
|
25
|
+
end
|
26
|
+
|
27
|
+
it "can infer the environment from the current application" do
|
28
|
+
api_scenario "one app, one environment"
|
29
|
+
|
30
|
+
ey "recipes upload", :debug => true
|
31
|
+
@out.should =~ /Recipes uploaded successfully for giblets/i
|
32
|
+
end
|
33
|
+
|
34
|
+
it "complains when it can't infer the environment from the current application" do
|
35
|
+
api_scenario "one app, one environment, not linked"
|
36
|
+
|
37
|
+
ey "recipes upload", :debug => true, :expect_failure => true
|
38
|
+
@err.should =~ /single environment/i
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "ey rollback" do
|
4
|
+
it_should_behave_like "an integration test"
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
api_scenario "one app, one environment"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "works when the environment name is valid" do
|
11
|
+
ey "rollback giblets", :debug => true
|
12
|
+
@out.should match(/rolling back giblets/i)
|
13
|
+
@err.should be_empty
|
14
|
+
@ssh_commands.last.should match(/eysd deploy rollback --app rails232app/)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "rollback the current environment by default" do
|
18
|
+
ey "rollback", :debug => true
|
19
|
+
@out.should match(/rolling back giblets/i)
|
20
|
+
@err.should be_empty
|
21
|
+
@ssh_commands.last.should match(/eysd deploy rollback --app rails232app/)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "fails when the environment name is bogus" do
|
25
|
+
ey "rollback typo", :expect_failure => true
|
26
|
+
@err.should match(/'typo'/)
|
27
|
+
@ssh_commands.should be_empty
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "ey rollback ENV" do
|
32
|
+
it_should_behave_like "an integration test"
|
33
|
+
|
34
|
+
before(:all) do
|
35
|
+
api_scenario "one app, many similarly-named environments"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "works when given an unambiguous substring" do
|
39
|
+
ey "rollback prod", :debug => true
|
40
|
+
@out.should match(/Rolling back railsapp_production/i)
|
41
|
+
@err.should be_empty
|
42
|
+
@ssh_commands.last.should match(/eysd deploy rollback --app rails232app/)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "complains when given an ambiguous substring" do
|
46
|
+
ey "rollback staging", :hide_err => true, :expect_failure => true
|
47
|
+
@err.should =~ /'staging' is ambiguous/
|
48
|
+
@ssh_commands.should be_empty
|
49
|
+
end
|
50
|
+
end
|
data/spec/ey/ssh_spec.rb
CHANGED
@@ -52,12 +52,17 @@ describe "ey ssh ENV" do
|
|
52
52
|
end
|
53
53
|
|
54
54
|
it "works when given an unambiguous substring" do
|
55
|
-
|
56
|
-
|
57
|
-
ey "ssh prod", :prepend_to_path => {'ssh' => print_my_args}
|
55
|
+
ey "ssh prod", :prepend_to_path => {'ssh' => print_my_args_ssh}
|
58
56
|
@raw_ssh_commands.should == ["ssh turkey@174.129.198.124"]
|
59
57
|
end
|
60
58
|
|
59
|
+
it "doesn't require you to be in any app's directory if the name is unambiguous" do
|
60
|
+
Dir.chdir(Dir.tmpdir) do
|
61
|
+
ey "ssh prod", :prepend_to_path => {'ssh' => print_my_args_ssh}
|
62
|
+
@raw_ssh_commands.should == ["ssh turkey@174.129.198.124"]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
61
66
|
it "complains when given an ambiguous substring" do
|
62
67
|
ey "ssh staging", :hide_err => true, :expect_failure => true
|
63
68
|
@err.should match(/'staging' is ambiguous/)
|
data/spec/spec_helper.rb
CHANGED
@@ -29,6 +29,7 @@ support.each{|helper| require helper }
|
|
29
29
|
|
30
30
|
Spec::Runner.configure do |config|
|
31
31
|
config.include Spec::Helpers
|
32
|
+
config.extend Spec::GitRepo
|
32
33
|
|
33
34
|
config.before(:all) do
|
34
35
|
FakeWeb.allow_net_connect = false
|
@@ -58,7 +59,15 @@ Spec::Matchers.define :have_command_like do |regex|
|
|
58
59
|
end
|
59
60
|
end
|
60
61
|
|
62
|
+
EY.define_git_repo("default") do |git_dir|
|
63
|
+
system("echo 'source :gemcutter' > Gemfile")
|
64
|
+
system("git add Gemfile")
|
65
|
+
system("git commit -m 'initial commit' >/dev/null 2>&1")
|
66
|
+
end
|
67
|
+
|
61
68
|
shared_examples_for "an integration test without an eyrc file" do
|
69
|
+
use_git_repo('default')
|
70
|
+
|
62
71
|
before(:all) do
|
63
72
|
FakeFS.deactivate!
|
64
73
|
ENV['EYRC'] = "/tmp/eyrc"
|
data/spec/support/fake_awsm.ru
CHANGED
@@ -55,11 +55,28 @@ class FakeAwsm < Sinatra::Base
|
|
55
55
|
end
|
56
56
|
|
57
57
|
post "/api/v2/environments/:env_id/recipes" do
|
58
|
-
|
58
|
+
if params[:file][:tempfile]
|
59
|
+
files = `tar --list -z -f "#{params[:file][:tempfile].path}"`.split(/\n/)
|
60
|
+
if files.empty?
|
61
|
+
status(400)
|
62
|
+
"No files in uploaded tarball"
|
63
|
+
else
|
64
|
+
status(204)
|
65
|
+
""
|
66
|
+
end
|
67
|
+
else
|
68
|
+
status(400)
|
69
|
+
"Recipe file not uploaded"
|
70
|
+
end
|
59
71
|
end
|
60
72
|
|
61
73
|
put "/api/v2/environments/:env_id/rebuild" do
|
62
|
-
status(
|
74
|
+
status(202)
|
75
|
+
""
|
76
|
+
end
|
77
|
+
|
78
|
+
put "/api/v2/environments/:env_id/run_custom_recipes" do
|
79
|
+
status(202)
|
63
80
|
""
|
64
81
|
end
|
65
82
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Spec
|
2
|
+
module GitRepo
|
3
|
+
def define_git_repo(name, &setup)
|
4
|
+
# EY's ivars don't get cleared between examples, so we can keep
|
5
|
+
# a git repo around longer (and thus make our tests faster)
|
6
|
+
EY.define_git_repo(name, &setup)
|
7
|
+
end
|
8
|
+
|
9
|
+
def use_git_repo(repo_name)
|
10
|
+
before(:each) do
|
11
|
+
@_original_wd ||= []
|
12
|
+
@_original_wd << Dir.getwd
|
13
|
+
Dir.chdir(EY.git_repo_dir(repo_name))
|
14
|
+
end
|
15
|
+
|
16
|
+
after(:each) do
|
17
|
+
Dir.chdir(@_original_wd.pop)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/spec/support/helpers.rb
CHANGED
@@ -17,7 +17,6 @@ module Spec
|
|
17
17
|
ZeroExitStatus = Class.new(UnexpectedExit)
|
18
18
|
|
19
19
|
def ey(cmd = nil, options = {}, &block)
|
20
|
-
require "open3"
|
21
20
|
hide_err = options.has_key?(:hide_err) ? options[:hide_err] : options[:expect_failure]
|
22
21
|
path_prepends = options[:prepend_to_path]
|
23
22
|
|
@@ -75,23 +74,11 @@ module Spec
|
|
75
74
|
@out
|
76
75
|
end
|
77
76
|
|
78
|
-
def api_scenario(scenario, remote =
|
77
|
+
def api_scenario(scenario, remote = "user@git.host:path/to/repo.git")
|
79
78
|
response = ::RestClient.put(EY.fake_awsm + '/scenario', {"scenario" => scenario, "remote" => remote}, {})
|
80
79
|
raise "Setting scenario failed: #{response.inspect}" unless response.code == 200
|
81
80
|
end
|
82
81
|
|
83
|
-
def local_git_remote
|
84
|
-
remotes = []
|
85
|
-
`git remote -v`.each_line do |line|
|
86
|
-
parts = line.split(/\t/)
|
87
|
-
# the remote will look like
|
88
|
-
# "git@github.com:engineyard/engineyard.git (fetch)\n"
|
89
|
-
# so we need to chop it up a bit
|
90
|
-
remotes << parts[1].gsub(/\s.*$/, "") if parts[1]
|
91
|
-
end
|
92
|
-
remotes.first
|
93
|
-
end
|
94
|
-
|
95
82
|
def read_yaml(file="ey.yml")
|
96
83
|
YAML.load_file(File.expand_path(file))
|
97
84
|
end
|
@@ -137,5 +124,29 @@ module EY
|
|
137
124
|
end
|
138
125
|
end
|
139
126
|
alias_method :start_fake_awsm, :fake_awsm
|
127
|
+
|
128
|
+
def define_git_repo(name, &setup)
|
129
|
+
@git_repo_setup ||= {}
|
130
|
+
raise "Attempted to redefine git repo #{name}; don't do that!" if @git_repo_setup.has_key?(name)
|
131
|
+
@git_repo_setup[name] = setup
|
132
|
+
end
|
133
|
+
|
134
|
+
def git_repo_dir(name)
|
135
|
+
@git_repo_dir_cache ||= {}
|
136
|
+
return @git_repo_dir_cache[name] if @git_repo_dir_cache.has_key?(name)
|
137
|
+
raise ArgumentError, "No definition for git repo #{name}" unless @git_repo_setup[name]
|
138
|
+
|
139
|
+
git_dir = Pathname.new("/tmp/engineyard_test_repo_#{Time.now.tv_sec}_#{Time.now.tv_usec}_#{$$}")
|
140
|
+
git_dir.mkdir
|
141
|
+
Dir.chdir(git_dir) do
|
142
|
+
system("git init -q")
|
143
|
+
system('git config user.email ey@spec.test')
|
144
|
+
system('git config user.name "EY Specs"')
|
145
|
+
system("git remote add testremote user@git.host:path/to/repo.git")
|
146
|
+
@git_repo_setup[name].call(git_dir)
|
147
|
+
end
|
148
|
+
@git_repo_dir_cache[name] = git_dir
|
149
|
+
end
|
150
|
+
|
140
151
|
end
|
141
152
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 4
|
8
|
+
- 0
|
9
|
+
version: 0.4.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- EY Cloud Team
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-06-
|
17
|
+
date: 2010-06-11 00:00:00 -07:00
|
18
18
|
default_executable: ey
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -109,7 +109,6 @@ extra_rdoc_files: []
|
|
109
109
|
files:
|
110
110
|
- bin/ey
|
111
111
|
- lib/engineyard/api.rb
|
112
|
-
- lib/engineyard/cli/action/deploy.rb
|
113
112
|
- lib/engineyard/cli/api.rb
|
114
113
|
- lib/engineyard/cli/recipes.rb
|
115
114
|
- lib/engineyard/cli/thor_fixes.rb
|
@@ -177,10 +176,13 @@ test_files:
|
|
177
176
|
- spec/ey/list_environments_spec.rb
|
178
177
|
- spec/ey/logs_spec.rb
|
179
178
|
- spec/ey/rebuild_spec.rb
|
179
|
+
- spec/ey/recipes/apply_spec.rb
|
180
|
+
- spec/ey/recipes/upload_spec.rb
|
181
|
+
- spec/ey/rollback_spec.rb
|
180
182
|
- spec/ey/ssh_spec.rb
|
181
|
-
- spec/ey/upload_recipes_spec.rb
|
182
183
|
- spec/spec_helper.rb
|
183
184
|
- spec/support/bundled_ey
|
184
185
|
- spec/support/fake_awsm.ru
|
186
|
+
- spec/support/git_repo.rb
|
185
187
|
- spec/support/helpers.rb
|
186
188
|
- spec/support/ruby_ext.rb
|
@@ -1,120 +0,0 @@
|
|
1
|
-
module EY
|
2
|
-
class CLI
|
3
|
-
module Action
|
4
|
-
class Deploy
|
5
|
-
|
6
|
-
EYSD_VERSION = "~>0.3.0"
|
7
|
-
|
8
|
-
def self.call(env_name, branch, options)
|
9
|
-
env_name ||= EY.config.default_environment
|
10
|
-
|
11
|
-
app = fetch_app
|
12
|
-
env = fetch_environment(env_name, app)
|
13
|
-
branch = fetch_branch(env.name, branch, options[:force])
|
14
|
-
master = fetch_app_master(env)
|
15
|
-
|
16
|
-
EY.ui.info "Connecting to the server..."
|
17
|
-
ensure_eysd_present(master, options[:install_eysd])
|
18
|
-
|
19
|
-
EY.ui.info "Running deploy for '#{env.name}' on server..."
|
20
|
-
deployed = master.deploy!(app, branch, options[:migrate], env.config)
|
21
|
-
|
22
|
-
if deployed
|
23
|
-
EY.ui.info "Deploy complete"
|
24
|
-
else
|
25
|
-
raise EY::Error, "Deploy failed"
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def self.fetch_app_master(env)
|
32
|
-
master = env.app_master
|
33
|
-
|
34
|
-
if !master
|
35
|
-
raise EnvironmentError, "No running instances for environment #{env.name}\nStart one at #{EY.config.endpoint}"
|
36
|
-
elsif master.status != "running"
|
37
|
-
raise EnvironmentError, "Cannot deploy: application master's status is not \"running\" (green); it is \"#{master.status}\"."
|
38
|
-
end
|
39
|
-
|
40
|
-
master
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.api
|
44
|
-
@api ||= EY::CLI::API.new
|
45
|
-
end
|
46
|
-
|
47
|
-
def self.repo
|
48
|
-
@repo ||= EY::Repo.new
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.fetch_app
|
52
|
-
app = api.app_for_repo(repo)
|
53
|
-
raise NoAppError.new(repo) unless app
|
54
|
-
app
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.fetch_environment(env_name, app)
|
58
|
-
# if the name's not specified and there's not exactly one
|
59
|
-
# environment, we can't figure out which environment to deploy
|
60
|
-
raise DeployArgumentError if !env_name && app.environments.size != 1
|
61
|
-
|
62
|
-
env = if env_name
|
63
|
-
app.environments.match_one(env_name)
|
64
|
-
else
|
65
|
-
app.environments.first
|
66
|
-
end
|
67
|
-
|
68
|
-
# the environment exists, but doesn't have this app
|
69
|
-
if !env && api.environments.named(env_name)
|
70
|
-
raise EnvironmentError, "Environment '#{env_name}' doesn't run this application\nYou can add it at #{EY.config.endpoint}"
|
71
|
-
end
|
72
|
-
|
73
|
-
if !env
|
74
|
-
raise NoEnvironmentError.new(env_name)
|
75
|
-
end
|
76
|
-
|
77
|
-
env
|
78
|
-
end
|
79
|
-
|
80
|
-
def self.fetch_branch(env_name, user_specified_branch, force)
|
81
|
-
default_branch = EY.config.default_branch(env_name)
|
82
|
-
|
83
|
-
branch = if user_specified_branch
|
84
|
-
if default_branch && (user_specified_branch != default_branch) && !force
|
85
|
-
raise BranchMismatch.new(default_branch, user_specified_branch)
|
86
|
-
end
|
87
|
-
user_specified_branch
|
88
|
-
else
|
89
|
-
default_branch || repo.current_branch
|
90
|
-
end
|
91
|
-
|
92
|
-
raise DeployArgumentError unless branch
|
93
|
-
branch
|
94
|
-
end
|
95
|
-
|
96
|
-
def self.ensure_eysd_present(instance, install_eysd)
|
97
|
-
eysd_status = instance.ey_deploy_check
|
98
|
-
case eysd_status
|
99
|
-
when :ssh_failed
|
100
|
-
raise EnvironmentError, "SSH connection to #{instance.hostname} failed"
|
101
|
-
when :eysd_missing
|
102
|
-
EY.ui.warn "Instance does not have server-side component installed"
|
103
|
-
EY.ui.info "Installing server-side component..."
|
104
|
-
instance.install_ey_deploy!
|
105
|
-
when :too_new
|
106
|
-
raise EnvironmentError, "server-side component too new; please upgrade your copy of the engineyard gem."
|
107
|
-
when :too_old
|
108
|
-
EY.ui.info "Upgrading server-side component..."
|
109
|
-
instance.upgrade_ey_deploy!
|
110
|
-
when :ok
|
111
|
-
# no action needed
|
112
|
-
else
|
113
|
-
raise EY::Error, "Internal error: Unexpected status from Instance#ey_deploy_check; got #{eysd_status.inspect}"
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe "ey recipes upload" do
|
4
|
-
it_should_behave_like "an integration test"
|
5
|
-
|
6
|
-
before(:all) do
|
7
|
-
@recipe_dir = Pathname.new("/tmp/#{$$}")
|
8
|
-
@recipe_dir.mkdir
|
9
|
-
Dir.chdir(@recipe_dir) do
|
10
|
-
@recipe_dir.join("cookbooks").mkdir
|
11
|
-
File.open(@recipe_dir.join("cookbooks/file"), "w"){|f| f << "boo" }
|
12
|
-
`git init`
|
13
|
-
`git add .`
|
14
|
-
`git commit -m "OMG"`
|
15
|
-
`git remote add testremote user@host.tld:path/to/repo.git`
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
it "posts the recipes to the correct url" do
|
20
|
-
api_scenario "one app, one environment"
|
21
|
-
Dir.chdir(@recipe_dir) do
|
22
|
-
ey "recipes upload giblets", :debug => true
|
23
|
-
end
|
24
|
-
|
25
|
-
@out.should =~ /recipes uploaded successfully/i
|
26
|
-
end
|
27
|
-
|
28
|
-
it "errors correctly on bogus env name" do
|
29
|
-
api_scenario "one app, one environment"
|
30
|
-
ey "recipes upload bogusenv", :expect_failure => true
|
31
|
-
|
32
|
-
@err.should =~ /can't be found/i
|
33
|
-
end
|
34
|
-
|
35
|
-
it "can infer the environment from the current application" do
|
36
|
-
api_scenario "one app, one environment", "user@host.tld:path/to/repo.git"
|
37
|
-
|
38
|
-
Dir.chdir(@recipe_dir) do
|
39
|
-
ey "recipes upload", :debug => true
|
40
|
-
end
|
41
|
-
|
42
|
-
@out.should =~ /recipes uploaded successfully/i
|
43
|
-
end
|
44
|
-
|
45
|
-
it "complains when it can't infer the environment from the current application" do
|
46
|
-
api_scenario "one app, one environment, not linked", "user@host.tld:path/to/repo.git"
|
47
|
-
|
48
|
-
Dir.chdir(@recipe_dir) do
|
49
|
-
ey "recipes upload", :debug => true, :expect_failure => true
|
50
|
-
end
|
51
|
-
|
52
|
-
@err.should =~ /single environment/i
|
53
|
-
end
|
54
|
-
end
|