engineyard 1.3.20 → 1.3.21
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/README.rdoc +28 -5
- data/lib/engineyard-api-client.rb +135 -0
- data/lib/engineyard/cli.rb +1 -1
- data/lib/engineyard/collection/abstract.rb +3 -3
- data/lib/engineyard/model/environment.rb +1 -1
- data/lib/engineyard/resolver.rb +7 -7
- data/lib/engineyard/version.rb +1 -1
- data/spec/engineyard/resolver_spec.rb +5 -0
- data/spec/ey/list_environments_spec.rb +39 -27
- metadata +8 -7
data/README.rdoc
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
= ey
|
2
2
|
|
3
|
+
== Login
|
4
|
+
|
5
|
+
The first command you run will notice that you are not logged in and will ask you for your AppCloud email and password.
|
6
|
+
|
7
|
+
== Configuration
|
8
|
+
|
9
|
+
The ey.yml file allows options to be saved for each environment to which an application is deployed. Here's an example ey.yml file in RAILS_ROOT/config/ey.yml:
|
10
|
+
|
11
|
+
$ cat config/ey.yml
|
12
|
+
---
|
13
|
+
environments:
|
14
|
+
env_production:
|
15
|
+
migrate: false
|
16
|
+
migration_command: rake fancy:migrate
|
17
|
+
branch: deploy
|
18
|
+
|
19
|
+
This ey.yml file wile turn off default migrations, set the default command to "rake fancy:migrate" and set the default deploy branch to "deploy".
|
20
|
+
|
21
|
+
== Commands
|
3
22
|
|
4
23
|
Command:
|
5
24
|
ey deploy
|
@@ -17,11 +36,15 @@ Command:
|
|
17
36
|
|
18
37
|
|
19
38
|
Description:
|
20
|
-
This command must be run with the current directory containing the app to be
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
39
|
+
This command must be run with the current directory containing the app to be
|
40
|
+
deployed. If ey.yml specifies a default branch then the ref parameter can be
|
41
|
+
omitted. Furthermore, if a default branch is specified but a different
|
42
|
+
command is supplied the deploy will fail unless --ignore-default-branch
|
43
|
+
is used.
|
44
|
+
|
45
|
+
Migrations are run by default with 'rake db:migrate'. A different command
|
46
|
+
can be specified via --migrate "ruby do_migrations.rb". Migrations can also
|
47
|
+
be skipped entirely by using --no-migrate.
|
25
48
|
|
26
49
|
Command:
|
27
50
|
ey environments
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'engineyard/model'
|
2
|
+
|
3
|
+
module EngineYard
|
4
|
+
class APIClient
|
5
|
+
attr_reader :token
|
6
|
+
|
7
|
+
USER_AGENT_STRING = "EngineYardAPIClient/#{VERSION}"
|
8
|
+
|
9
|
+
def initialize(token = nil)
|
10
|
+
@token ||= token
|
11
|
+
@token ||= self.class.read_token
|
12
|
+
raise ArgumentError, "EY Cloud API token required" unless @token
|
13
|
+
end
|
14
|
+
|
15
|
+
def ==(other)
|
16
|
+
raise ArgumentError unless other.is_a?(self.class)
|
17
|
+
self.token == other.token
|
18
|
+
end
|
19
|
+
|
20
|
+
def request(url, opts={})
|
21
|
+
opts[:headers] ||= {}
|
22
|
+
opts[:headers]["X-EY-Cloud-Token"] = token
|
23
|
+
EY.ui.debug("Token", token)
|
24
|
+
self.class.request(url, opts)
|
25
|
+
end
|
26
|
+
|
27
|
+
def environments
|
28
|
+
@environments ||= Environment.from_array(request('/environments')["environments"], :api => self)
|
29
|
+
end
|
30
|
+
|
31
|
+
def apps
|
32
|
+
@apps ||= App.from_array(request('/apps')["apps"], :api => self)
|
33
|
+
end
|
34
|
+
|
35
|
+
def resolver
|
36
|
+
@resolver ||= Resolver.new(self)
|
37
|
+
end
|
38
|
+
|
39
|
+
def apps_for_repo(repo)
|
40
|
+
repo.fail_on_no_remotes!
|
41
|
+
apps.find_all {|a| repo.has_remote?(a.repository_uri) }
|
42
|
+
end
|
43
|
+
|
44
|
+
class InvalidCredentials < EY::AppCloudClient::Error; end
|
45
|
+
class RequestFailed < EY::AppCloudClient::Error; end
|
46
|
+
|
47
|
+
def self.request(path, opts={})
|
48
|
+
require 'rest_client'
|
49
|
+
require 'json'
|
50
|
+
|
51
|
+
url = EY.config.endpoint + "api/v2#{path}"
|
52
|
+
method = (opts.delete(:method) || 'get').to_s.downcase.to_sym
|
53
|
+
params = opts.delete(:params) || {}
|
54
|
+
headers = opts.delete(:headers) || {}
|
55
|
+
headers["Accept"] ||= "application/json"
|
56
|
+
headers["User-Agent"] = USER_AGENT_STRING
|
57
|
+
|
58
|
+
begin
|
59
|
+
EY.ui.debug("Request", "#{method.to_s.upcase} #{url}")
|
60
|
+
case method
|
61
|
+
when :get, :delete, :head
|
62
|
+
url.query = RestClient::Payload::UrlEncoded.new(params).to_s
|
63
|
+
resp = RestClient.send(method, url.to_s, headers)
|
64
|
+
else
|
65
|
+
resp = RestClient.send(method, url.to_s, params, headers)
|
66
|
+
end
|
67
|
+
rescue RestClient::Unauthorized
|
68
|
+
raise InvalidCredentials
|
69
|
+
rescue Errno::ECONNREFUSED
|
70
|
+
raise RequestFailed, "Could not reach the cloud API"
|
71
|
+
rescue RestClient::ResourceNotFound
|
72
|
+
raise RequestFailed, "The requested resource could not be found"
|
73
|
+
rescue RestClient::BadGateway
|
74
|
+
raise RequestFailed, "AppCloud API is temporarily unavailable. Please try again soon."
|
75
|
+
rescue RestClient::RequestFailed => e
|
76
|
+
raise RequestFailed, "#{e.message} #{e.response}"
|
77
|
+
rescue OpenSSL::SSL::SSLError
|
78
|
+
raise RequestFailed, "SSL is misconfigured on your cloud"
|
79
|
+
end
|
80
|
+
|
81
|
+
if resp.body.empty?
|
82
|
+
data = ''
|
83
|
+
elsif resp.headers[:content_type] =~ /application\/json/
|
84
|
+
begin
|
85
|
+
data = JSON.parse(resp.body)
|
86
|
+
EY.ui.debug("Response", data)
|
87
|
+
rescue JSON::ParserError
|
88
|
+
EY.ui.debug("Raw response", resp.body)
|
89
|
+
raise RequestFailed, "Response was not valid JSON."
|
90
|
+
end
|
91
|
+
else
|
92
|
+
data = resp.body
|
93
|
+
end
|
94
|
+
|
95
|
+
data
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.fetch_token(email, password)
|
99
|
+
api_token = request("/authenticate", :method => "post",
|
100
|
+
:params => { :email => email, :password => password })["api_token"]
|
101
|
+
save_token(api_token)
|
102
|
+
api_token
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.read_token(file = nil)
|
106
|
+
file ||= ENV['EYRC'] || File.expand_path("~/.eyrc")
|
107
|
+
return false unless File.exists?(file)
|
108
|
+
|
109
|
+
require 'yaml'
|
110
|
+
|
111
|
+
data = YAML.load_file(file)
|
112
|
+
if EY.config.default_endpoint?
|
113
|
+
data["api_token"]
|
114
|
+
else
|
115
|
+
(data[EY.config.endpoint.to_s] || {})["api_token"]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.save_token(token, file = nil)
|
120
|
+
file ||= ENV['EYRC'] || File.expand_path("~/.eyrc")
|
121
|
+
require 'yaml'
|
122
|
+
|
123
|
+
data = File.exists?(file) ? YAML.load_file(file) : {}
|
124
|
+
if EY.config.default_endpoint?
|
125
|
+
data.merge!("api_token" => token)
|
126
|
+
else
|
127
|
+
data.merge!(EY.config.endpoint.to_s => {"api_token" => token})
|
128
|
+
end
|
129
|
+
|
130
|
+
File.open(file, "w"){|f| YAML.dump(data, f) }
|
131
|
+
true
|
132
|
+
end
|
133
|
+
|
134
|
+
end # API
|
135
|
+
end # EY
|
data/lib/engineyard/cli.rb
CHANGED
@@ -122,7 +122,7 @@ module EY
|
|
122
122
|
message << "The following environments contain those applications:\n\n"
|
123
123
|
EY.ui.warn(message)
|
124
124
|
elsif apps.empty?
|
125
|
-
EY.ui.warn(NoAppError.new(repo).message)
|
125
|
+
EY.ui.warn(NoAppError.new(repo).message + "\nUse #{self.class.send(:banner_base)} environments --all to see all environments.")
|
126
126
|
end
|
127
127
|
|
128
128
|
EY.ui.print_envs(apps, EY.config.default_environment, options[:simple])
|
@@ -11,9 +11,9 @@ Specify --account ACCOUNT_NAME to resolve this ambiguity.
|
|
11
11
|
def named(name, account_name=nil)
|
12
12
|
candidates = find_all do |x|
|
13
13
|
if account_name
|
14
|
-
x.name == name && x.account.name == account_name
|
14
|
+
x.name.downcase == name.downcase && x.account.name.downcase == account_name.downcase
|
15
15
|
else
|
16
|
-
x.name == name
|
16
|
+
x.name.downcase == name.downcase
|
17
17
|
end
|
18
18
|
end
|
19
19
|
if candidates.size > 1
|
@@ -33,7 +33,7 @@ Specify --account ACCOUNT_NAME to resolve this ambiguity.
|
|
33
33
|
private
|
34
34
|
|
35
35
|
def find_by_unambiguous_substring(name_part)
|
36
|
-
candidates = find_all{|e| e.name[name_part] }
|
36
|
+
candidates = find_all{|e| e.name.downcase[name_part.downcase] }
|
37
37
|
if candidates.size > 1
|
38
38
|
raise ambiguous_error(name_part, candidates.map {|e| e.name})
|
39
39
|
end
|
@@ -83,7 +83,7 @@ module EY
|
|
83
83
|
def upload_recipes_at_path(recipes_path)
|
84
84
|
recipes_path = Pathname.new(recipes_path)
|
85
85
|
if recipes_path.exist?
|
86
|
-
upload_recipes recipes_path.open('
|
86
|
+
upload_recipes recipes_path.open('rb')
|
87
87
|
else
|
88
88
|
raise EY::Error, "Recipes file not found: #{recipes_path}"
|
89
89
|
end
|
data/lib/engineyard/resolver.rb
CHANGED
@@ -64,7 +64,7 @@ module EY
|
|
64
64
|
message = "Multiple app deployments possible, please be more specific:\n\n"
|
65
65
|
candidates.map{|c| [c[:account_name], c[:app_name]]}.uniq.each do |account_name, app_name|
|
66
66
|
message << "#{app_name}\n"
|
67
|
-
candidates.select {|
|
67
|
+
candidates.select {|c| c[:app_name] == app_name && c[:account_name] == account_name}.map{|c| c[:environment_name]}.uniq.each do |env_name|
|
68
68
|
message << "\t#{env_name} # ey <command> --environment='#{env_name}' --app='#{app_name}' --account='#{account_name}'\n"
|
69
69
|
end
|
70
70
|
end
|
@@ -80,10 +80,10 @@ module EY
|
|
80
80
|
@app_deployments ||= api.apps.map do |app|
|
81
81
|
app.environments.map do |environment|
|
82
82
|
{
|
83
|
-
:app_name => app.name,
|
83
|
+
:app_name => app.name.downcase,
|
84
84
|
:repository_uri => app.repository_uri,
|
85
|
-
:environment_name => environment.name,
|
86
|
-
:account_name => app.account.name,
|
85
|
+
:environment_name => environment.name.downcase,
|
86
|
+
:account_name => app.account.name.downcase,
|
87
87
|
}
|
88
88
|
end
|
89
89
|
end.flatten
|
@@ -122,10 +122,10 @@ module EY
|
|
122
122
|
end
|
123
123
|
|
124
124
|
def filter_candidates_by(type, options, candidates)
|
125
|
-
if options[type] && candidates.any?{|c| c[type] == options[type] }
|
126
|
-
candidates.select {|c| c[type] == options[type] }
|
125
|
+
if options[type] && candidates.any?{|c| c[type] == options[type].downcase }
|
126
|
+
candidates.select {|c| c[type] == options[type].downcase }
|
127
127
|
elsif options[type]
|
128
|
-
candidates.select {|c| c[type][options[type]] }
|
128
|
+
candidates.select {|c| c[type][options[type].downcase] }
|
129
129
|
else
|
130
130
|
candidates
|
131
131
|
end
|
data/lib/engineyard/version.rb
CHANGED
@@ -95,6 +95,11 @@ describe EY::Resolver do
|
|
95
95
|
resolver.app_and_environment(:repo => repo("git://github.com/repo/app.git"), :environment_name => "staging").should resolve_to(@staging)
|
96
96
|
end
|
97
97
|
|
98
|
+
it "doesn't care about case" do
|
99
|
+
resolver.app_and_environment(:account_name => "EY", :app_name => "big").should resolve_to(@big)
|
100
|
+
resolver.app_and_environment(:account_name => "ey", :app_name => "BiG").should resolve_to(@big)
|
101
|
+
end
|
102
|
+
|
98
103
|
it "returns the match when an app is specified even when there is a repo" do
|
99
104
|
resolver.app_and_environment(:account_name => "ey", :app_name => "bigapp", :repo => repo("git://github.com/repo/app.git")).should resolve_to(@big)
|
100
105
|
end
|
@@ -4,41 +4,53 @@ describe "ey environments" do
|
|
4
4
|
|
5
5
|
given "integration"
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
context "with no apps" do
|
8
|
+
before do
|
9
|
+
api_scenario "empty"
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
@out.should =~ /bakon/
|
12
|
+
it "suggests that you use environments --all" do
|
13
|
+
ey %w[environments]
|
14
|
+
@out.should =~ /Use ey environments --all to see all environments./
|
15
|
+
end
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
@err.should =~ /fatal: No git remotes found in .*\/tmp/
|
22
|
-
@out.should_not =~ /no application configured/
|
18
|
+
context "with apps" do
|
19
|
+
before(:all) do
|
20
|
+
api_scenario "one app, many environments"
|
23
21
|
end
|
24
|
-
end
|
25
22
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
23
|
+
it "lists the environments your app is in" do
|
24
|
+
ey %w[environments]
|
25
|
+
@out.should include('rails232app (main)')
|
26
|
+
@out.should =~ /giblets/
|
27
|
+
@out.should =~ /bakon/
|
28
|
+
end
|
31
29
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
30
|
+
it "reports failure to find a git repo when not in one" do
|
31
|
+
Dir.chdir("/tmp") do
|
32
|
+
ey %w[environments], :expect_failure => true
|
33
|
+
@err.should =~ /fatal: No git remotes found in .*\/tmp/
|
34
|
+
@out.should_not =~ /no application configured/
|
35
|
+
end
|
36
|
+
end
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
it "lists all environments that have apps with -a" do
|
39
|
+
ey %w[environments -a]
|
40
|
+
@out.should include("bakon")
|
41
|
+
@out.should include("giblets")
|
42
|
+
end
|
41
43
|
|
44
|
+
it "outputs simply with -s" do
|
45
|
+
ey %w[environments -s], :debug => false
|
46
|
+
@out.split(/\n/).sort.should == ["bakon", "giblets"]
|
47
|
+
end
|
48
|
+
|
49
|
+
it "outputs all environments (including ones with no apps) simply with -a and -s" do
|
50
|
+
ey %w[environments -a -s], :debug => false
|
51
|
+
@out.split(/\n/).sort.should == ["bakon", "beef", "giblets"]
|
52
|
+
end
|
53
|
+
end
|
42
54
|
end
|
43
55
|
|
44
56
|
describe "ey environments with an ambiguous git repo" do
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: engineyard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 49
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 3
|
9
|
-
-
|
10
|
-
version: 1.3.
|
9
|
+
- 21
|
10
|
+
version: 1.3.21
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- EY Cloud Team
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-08-03 00:00:00 -07:00
|
19
19
|
default_executable: ey
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -102,12 +102,12 @@ dependencies:
|
|
102
102
|
requirements:
|
103
103
|
- - "="
|
104
104
|
- !ruby/object:Gem::Version
|
105
|
-
hash:
|
105
|
+
hash: 3
|
106
106
|
segments:
|
107
107
|
- 1
|
108
108
|
- 4
|
109
|
-
-
|
110
|
-
version: 1.4.
|
109
|
+
- 2
|
110
|
+
version: 1.4.2
|
111
111
|
name: engineyard-serverside-adapter
|
112
112
|
prerelease: false
|
113
113
|
type: :runtime
|
@@ -297,6 +297,7 @@ files:
|
|
297
297
|
- lib/engineyard/ruby_ext.rb
|
298
298
|
- lib/engineyard/thor.rb
|
299
299
|
- lib/engineyard/version.rb
|
300
|
+
- lib/engineyard-api-client.rb
|
300
301
|
- lib/engineyard.rb
|
301
302
|
- LICENSE
|
302
303
|
- README.rdoc
|