engineyard 0.2.11 → 0.2.12
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/lib/engineyard.rb +4 -3
- data/lib/engineyard/#repo.rb# +24 -0
- data/lib/engineyard/account.rb +32 -11
- data/lib/engineyard/account/api_struct.rb +25 -0
- data/lib/engineyard/account/app.rb +11 -10
- data/lib/engineyard/account/app_master.rb +1 -7
- data/lib/engineyard/account/environment.rb +31 -16
- data/lib/engineyard/account/instance.rb +6 -0
- data/lib/engineyard/account/log.rb +7 -16
- data/lib/engineyard/action/deploy.rb +138 -0
- data/lib/engineyard/action/list_environments.rb +22 -0
- data/lib/engineyard/action/rebuild.rb +31 -0
- data/lib/engineyard/action/show_logs.rb +26 -0
- data/lib/engineyard/action/ssh.rb +19 -0
- data/lib/engineyard/action/upload_recipes.rb +17 -0
- data/lib/engineyard/action/util.rb +47 -0
- data/lib/engineyard/cli.rb +24 -150
- data/lib/engineyard/cli/thor_fixes.rb +26 -0
- data/lib/engineyard/error.rb +48 -0
- data/lib/engineyard/repo.rb +1 -1
- data/lib/engineyard/ruby_ext.rb +9 -0
- data/spec/engineyard/account/api_struct_spec.rb +37 -0
- data/spec/engineyard/account/environment_spec.rb +20 -0
- data/spec/engineyard/account_spec.rb +18 -0
- data/spec/engineyard/cli_spec.rb +3 -3
- data/spec/ey/deploy_spec.rb +166 -28
- data/spec/ey/ey_spec.rb +3 -3
- data/spec/ey/list_environments_spec.rb +16 -0
- data/spec/ey/logs_spec.rb +2 -17
- data/spec/ey/rebuild_spec.rb +25 -0
- data/spec/ey/ssh_spec.rb +24 -0
- data/spec/ey/upload_recipes_spec.rb +21 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/support/fake_awsm.ru +25 -1
- data/spec/support/helpers.rb +72 -7
- metadata +44 -56
- data/lib/engineyard/cli/error.rb +0 -44
- data/spec/spec.opts +0 -2
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'engineyard/action/util'
|
2
|
+
|
3
|
+
module EY
|
4
|
+
module Action
|
5
|
+
class ShowLogs
|
6
|
+
extend Util
|
7
|
+
|
8
|
+
def self.call(name)
|
9
|
+
env_named(name).logs.each do |log|
|
10
|
+
EY.ui.info log.instance_name
|
11
|
+
|
12
|
+
if log.main
|
13
|
+
EY.ui.info "Main logs:"
|
14
|
+
EY.ui.say log.main
|
15
|
+
end
|
16
|
+
|
17
|
+
if log.custom
|
18
|
+
EY.ui.info "Custom logs:"
|
19
|
+
EY.ui.say log.custom
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'engineyard/action/util'
|
2
|
+
|
3
|
+
module EY
|
4
|
+
module Action
|
5
|
+
class SSH
|
6
|
+
extend Util
|
7
|
+
|
8
|
+
def self.call(name)
|
9
|
+
|
10
|
+
env = account.environment_named(name)
|
11
|
+
if env
|
12
|
+
Kernel.exec "ssh", "#{env.username}@#{env.app_master.public_hostname}", *ARGV[2..-1]
|
13
|
+
else
|
14
|
+
EY.ui.warn %|Could not find an environment named "#{name}"|
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'engineyard/action/util'
|
2
|
+
|
3
|
+
module EY
|
4
|
+
module Action
|
5
|
+
class UploadRecipes
|
6
|
+
extend Util
|
7
|
+
|
8
|
+
def self.call(name)
|
9
|
+
if account.upload_recipes_for(env_named(name))
|
10
|
+
EY.ui.say "Recipes uploaded successfully"
|
11
|
+
else
|
12
|
+
EY.ui.error "Recipes upload failed"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module EY
|
2
|
+
module Action
|
3
|
+
module Util
|
4
|
+
|
5
|
+
protected
|
6
|
+
|
7
|
+
def account
|
8
|
+
# XXX it stinks that we have to use EY::CLI::API explicitly
|
9
|
+
# here; I don't want to have this lateral Action --> CLI reference
|
10
|
+
@account ||= EY::Account.new(EY::CLI::API.new)
|
11
|
+
end
|
12
|
+
|
13
|
+
def repo
|
14
|
+
@repo ||= EY::Repo.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def app_and_envs(all_envs = false)
|
18
|
+
app = account.app_for_repo(repo)
|
19
|
+
|
20
|
+
if all_envs || !app
|
21
|
+
envs = account.environments
|
22
|
+
EY.ui.warn(NoAppError.new(repo).message) unless app || all_envs
|
23
|
+
[nil, envs]
|
24
|
+
else
|
25
|
+
envs = app.environments
|
26
|
+
if envs.empty?
|
27
|
+
EY.ui.warn %|You have no environments set up for the application "#{app.name}"|
|
28
|
+
EY.ui.warn %|You can make one at #{EY.config.endpoint}|
|
29
|
+
end
|
30
|
+
envs.empty? ? [app, nil] : [app, envs]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def env_named(name)
|
35
|
+
env = account.environment_named(name)
|
36
|
+
|
37
|
+
if env.nil?
|
38
|
+
raise EnvironmentError, "Environment '#{env_name}' can't be found\n" +
|
39
|
+
"You can create it at #{EY.config.endpoint}"
|
40
|
+
else
|
41
|
+
env
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/engineyard/cli.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
require 'thor'
|
2
2
|
require 'engineyard'
|
3
|
-
require 'engineyard/
|
3
|
+
require 'engineyard/error'
|
4
|
+
require 'engineyard/cli/thor_fixes'
|
4
5
|
|
5
6
|
module EY
|
6
7
|
class CLI < Thor
|
7
|
-
EYSD_VERSION = "~>0.2.6"
|
8
|
-
|
9
8
|
autoload :API, 'engineyard/cli/api'
|
10
9
|
autoload :UI, 'engineyard/cli/ui'
|
11
10
|
|
@@ -21,131 +20,45 @@ module EY
|
|
21
20
|
:desc => "Force a deploy of the specified branch"
|
22
21
|
method_option :migrate, :type => :string, :aliases => %w(-m),
|
23
22
|
:default => 'rake db:migrate',
|
24
|
-
:desc => "Run migrations via [MIGRATE], defaults to 'rake db:migrate'"
|
23
|
+
:desc => "Run migrations via [MIGRATE], defaults to 'rake db:migrate'; use --no-migrate to avoid running migrations"
|
25
24
|
method_option :install_eysd, :type => :boolean, :aliases => %(-s),
|
26
25
|
:desc => "Force remote install of eysd"
|
27
26
|
def deploy(env_name = nil, branch = nil)
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
env_name ||= EY.config.default_environment
|
32
|
-
raise DeployArgumentError if !env_name && app.environments.size != 1
|
33
|
-
|
34
|
-
default_branch = EY.config.default_branch(env_name)
|
35
|
-
branch ||= (default_branch || repo.current_branch)
|
36
|
-
raise DeployArgumentError unless branch
|
37
|
-
|
38
|
-
invalid_branch = default_branch && (branch != default_branch) && !options[:force]
|
39
|
-
raise BranchMismatch.new(default_branch, branch) if invalid_branch
|
40
|
-
|
41
|
-
if env_name && app.environments
|
42
|
-
env = app.environments.find{|e| e.name == env_name }
|
43
|
-
else
|
44
|
-
env = app.environments.first
|
45
|
-
end
|
46
|
-
|
47
|
-
if !env && account.environment_named(env_name)
|
48
|
-
raise EnvironmentError, "Environment '#{env_name}' doesn't run this application\nYou can add it at #{EY.config.endpoint}"
|
49
|
-
elsif !env
|
50
|
-
raise NoEnvironmentError
|
51
|
-
end
|
52
|
-
|
53
|
-
running = env.app_master && env.app_master.status == "running"
|
54
|
-
raise EnvironmentError, "No running instances for environment #{env.name}\nStart one at #{EY.config.endpoint}" unless running
|
55
|
-
|
56
|
-
hostname = env.app_master.public_hostname
|
57
|
-
username = env.username
|
58
|
-
|
59
|
-
EY.ui.info "Connecting to the server..."
|
60
|
-
ssh_to(hostname, "eysd check '#{EY::VERSION}' '#{EYSD_VERSION}'", username, false)
|
61
|
-
case $?.exitstatus
|
62
|
-
when 255
|
63
|
-
raise EnvironmentError, "SSH connection to #{hostname} failed"
|
64
|
-
when 127
|
65
|
-
EY.ui.warn "Server does not have ey-deploy gem installed"
|
66
|
-
eysd_installed = false
|
67
|
-
when 0
|
68
|
-
eysd_installed = true
|
69
|
-
else
|
70
|
-
raise EnvironmentError, "ey-deploy version not compatible"
|
71
|
-
end
|
72
|
-
|
73
|
-
if !eysd_installed || options[:install_eysd]
|
74
|
-
EY.ui.info "Installing ey-deploy gem..."
|
75
|
-
ssh_to(hostname,
|
76
|
-
"sudo gem install ey-deploy -v '#{EYSD_VERSION}'",
|
77
|
-
username)
|
78
|
-
end
|
79
|
-
|
80
|
-
deploy_cmd = "eysd deploy --app #{app.name} --branch #{branch}"
|
81
|
-
if env.config
|
82
|
-
escaped_config_option = env.config.to_json.gsub(/"/, "\\\"")
|
83
|
-
deploy_cmd << " --config '#{escaped_config_option}'"
|
84
|
-
end
|
85
|
-
|
86
|
-
if options['migrate']
|
87
|
-
deploy_cmd << " --migrate='#{options[:migrate]}'"
|
88
|
-
end
|
89
|
-
|
90
|
-
EY.ui.info "Running deploy on server..."
|
91
|
-
deployed = ssh_to(hostname, deploy_cmd, username)
|
92
|
-
|
93
|
-
if deployed
|
94
|
-
EY.ui.info "Deploy complete"
|
95
|
-
else
|
96
|
-
raise EY::Error, "Deploy failed"
|
97
|
-
end
|
27
|
+
require 'engineyard/action/deploy'
|
28
|
+
EY::Action::Deploy.call(env_name, branch, options)
|
98
29
|
end
|
99
30
|
|
100
31
|
|
101
32
|
desc "environments [--all]", "List cloud environments for this app, or all environments"
|
102
33
|
method_option :all, :type => :boolean, :aliases => %(-a)
|
103
34
|
def environments
|
104
|
-
|
105
|
-
|
106
|
-
EY.ui.say %|Cloud environments for #{app.name}:|
|
107
|
-
EY.ui.print_envs(envs, EY.config.default_environment)
|
108
|
-
elsif envs
|
109
|
-
EY.ui.say %|Cloud environments:|
|
110
|
-
EY.ui.print_envs(envs, EY.config.default_environment)
|
111
|
-
else
|
112
|
-
EY.ui.say %|You do not have any cloud environments.|
|
113
|
-
end
|
35
|
+
require 'engineyard/action/list_environments'
|
36
|
+
EY::Action::ListEnvironments.call(options[:all])
|
114
37
|
end
|
115
38
|
map "envs" => :environments
|
116
39
|
|
40
|
+
desc "rebuild [ENV]", "Rebuild environment (ensure configuration is up-to-date)"
|
41
|
+
def rebuild(name = nil)
|
42
|
+
require 'engineyard/action/rebuild'
|
43
|
+
EY::Action::Rebuild.call(name)
|
44
|
+
end
|
45
|
+
|
117
46
|
desc "ssh ENV", "Open an ssh session to the environment's application server"
|
118
47
|
def ssh(name)
|
119
|
-
|
120
|
-
|
121
|
-
Kernel.exec "ssh", "#{env.username}@#{env.app_master.public_hostname}", *ARGV[2..-1]
|
122
|
-
else
|
123
|
-
EY.ui.warn %|Could not find an environment named "#{name}"|
|
124
|
-
end
|
48
|
+
require 'engineyard/action/ssh'
|
49
|
+
EY::Action::SSH.call(name)
|
125
50
|
end
|
126
51
|
|
127
|
-
desc "logs
|
128
|
-
def logs(
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
raise EnvironmentError, "Environment '#{env_name}' can't be found\n" +
|
133
|
-
"You can create it at #{EY.config.endpoint}"
|
134
|
-
else
|
135
|
-
env.logs.each do |log|
|
136
|
-
EY.ui.info log.instance_name
|
137
|
-
|
138
|
-
if log.main
|
139
|
-
EY.ui.info "Main logs:"
|
140
|
-
EY.ui.say log.main
|
141
|
-
end
|
52
|
+
desc "logs ENV", "Retrieve the latest logs for an enviornment"
|
53
|
+
def logs(name)
|
54
|
+
require 'engineyard/action/show_logs'
|
55
|
+
EY::Action::ShowLogs.call(name)
|
56
|
+
end
|
142
57
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
end # logs_for_environment(env).each
|
148
|
-
end # env.nil?
|
58
|
+
desc "upload_recipes ENV", "Upload custom chef recipes from the current directory to ENV"
|
59
|
+
def upload_recipes(name)
|
60
|
+
require 'engineyard/action/upload_recipes'
|
61
|
+
EY::Action::UploadRecipes.call(name)
|
149
62
|
end
|
150
63
|
|
151
64
|
desc "version", "Print the version of the engineyard gem"
|
@@ -154,44 +67,5 @@ module EY
|
|
154
67
|
end
|
155
68
|
map "-v" => :version
|
156
69
|
|
157
|
-
private
|
158
|
-
|
159
|
-
def app_and_envs(all_envs = false)
|
160
|
-
app = account.app_for_repo(repo)
|
161
|
-
|
162
|
-
if all_envs || !app
|
163
|
-
envs = account.environments
|
164
|
-
EY.ui.warn(NoAppError.new(repo).message) unless app || all_envs
|
165
|
-
[nil, envs]
|
166
|
-
else
|
167
|
-
envs = app.environments
|
168
|
-
if envs.empty?
|
169
|
-
EY.ui.warn %|You have no environments set up for the application "#{app.name}"|
|
170
|
-
EY.ui.warn %|You can make one at #{EY.config.endpoint}|
|
171
|
-
end
|
172
|
-
envs.empty? ? [app, nil] : [app, envs]
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
def account
|
177
|
-
@account ||= EY::Account.new(API.new)
|
178
|
-
end
|
179
|
-
|
180
|
-
def repo
|
181
|
-
@repo ||= EY::Repo.new
|
182
|
-
end
|
183
|
-
|
184
|
-
def ssh_to(hostname, remote_cmd, user, output = true)
|
185
|
-
cmd = %{ssh -o StrictHostKeyChecking=no -q #{user}@#{hostname} "#{remote_cmd}"}
|
186
|
-
cmd << %{ &> /dev/null} unless output
|
187
|
-
EY.ui.debug(cmd)
|
188
|
-
puts cmd if output
|
189
|
-
unless ENV["NO_SSH"]
|
190
|
-
system cmd
|
191
|
-
else
|
192
|
-
true
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
70
|
end # CLI
|
197
71
|
end # EY
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# This particular pile of monkeypatch can be removed once we have a
|
2
|
+
# thor that doesn't consume an argument for --no-migrate.
|
3
|
+
#
|
4
|
+
# A fix has been written and a pull request sent.
|
5
|
+
# The fix is
|
6
|
+
# http://github.com/smerritt/thor/commit/421b2e97684e67ee393a13b3873e1a784bb83f68
|
7
|
+
|
8
|
+
class Thor
|
9
|
+
class Arguments
|
10
|
+
private
|
11
|
+
|
12
|
+
def no_or_skip?(arg)
|
13
|
+
arg =~ /^--(no|skip)-([-\w]+)$/
|
14
|
+
$2
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse_string(name)
|
18
|
+
if no_or_skip?(name)
|
19
|
+
nil
|
20
|
+
else
|
21
|
+
shift
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module EY
|
2
|
+
class Error < RuntimeError; end
|
3
|
+
|
4
|
+
class NoAppError < Error
|
5
|
+
def initialize(repo)
|
6
|
+
@repo = repo
|
7
|
+
end
|
8
|
+
|
9
|
+
def message
|
10
|
+
error = [%|There is no application configured for any of the following remotes:|]
|
11
|
+
@repo.urls.each{|url| error << %|\t#{url}| }
|
12
|
+
error << %|You can add this application at #{EY.config.endpoint}|
|
13
|
+
error.join("\n")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class EnvironmentError < EY::Error
|
18
|
+
end
|
19
|
+
|
20
|
+
class NoEnvironmentError < EY::Error
|
21
|
+
def initialize(env_name=nil)
|
22
|
+
@env_name = env_name
|
23
|
+
end
|
24
|
+
|
25
|
+
def message
|
26
|
+
"No environment named '#{@env_name}'\nYou can create one at #{EY.config.endpoint}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class BranchMismatch < EY::Error
|
31
|
+
def initialize(default_branch, branch)
|
32
|
+
super(nil)
|
33
|
+
@default_branch, @branch = default_branch, branch
|
34
|
+
end
|
35
|
+
|
36
|
+
def message
|
37
|
+
%|Your deploy branch is set to "#{@default_branch}".\n| +
|
38
|
+
%|If you want to deploy branch "#{@branch}", use --force.|
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class DeployArgumentError < EY::Error
|
43
|
+
def message
|
44
|
+
%|"deploy" was called incorrectly. Call as "deploy [ENVIRONMENT] [BRANCH]"\n| +
|
45
|
+
%|You can set default environments and branches in ey.yml|
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/engineyard/repo.rb
CHANGED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EY::Account::ApiStruct do
|
4
|
+
class Foo < EY::Account::ApiStruct.new(:fruit); end
|
5
|
+
class FooWithAccount < EY::Account::ApiStruct.new(:fruit, :account); end
|
6
|
+
|
7
|
+
it "acts like a normal struct" do
|
8
|
+
f = Foo.new("banana")
|
9
|
+
|
10
|
+
f.fruit.should == "banana"
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "from_hash initializer" do
|
14
|
+
it "assigns values from string keys" do
|
15
|
+
f = Foo.from_hash("fruit" => "banana")
|
16
|
+
f.should == Foo.new("banana")
|
17
|
+
end
|
18
|
+
|
19
|
+
it "assigns values from symbol keys" do
|
20
|
+
f = Foo.from_hash(:fruit => "banana")
|
21
|
+
f.should == Foo.new("banana")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "from_array initializer" do
|
26
|
+
it "provides a from_array initializer" do
|
27
|
+
f = Foo.from_array([:fruit => "banana"])
|
28
|
+
f.should == [Foo.new("banana")]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "handles an account as the second argument" do
|
32
|
+
f = FooWithAccount.from_array([:fruit => "banana"], "account")
|
33
|
+
f.should == [FooWithAccount.new("banana", "account")]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|