engineyard-cloud-client 1.0.3 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/engineyard-cloud-client.rb +14 -93
- data/lib/engineyard-cloud-client/connection.rb +142 -0
- data/lib/engineyard-cloud-client/models/account.rb +1 -1
- data/lib/engineyard-cloud-client/models/app.rb +2 -2
- data/lib/engineyard-cloud-client/models/app_environment.rb +4 -4
- data/lib/engineyard-cloud-client/models/deployment.rb +6 -6
- data/lib/engineyard-cloud-client/models/environment.rb +13 -11
- data/lib/engineyard-cloud-client/models/keypair.rb +3 -3
- data/lib/engineyard-cloud-client/models/recipes.rb +3 -6
- data/lib/engineyard-cloud-client/test.rb +3 -3
- data/lib/engineyard-cloud-client/test/fake_awsm/config.ru +4 -3
- data/lib/engineyard-cloud-client/test/fake_awsm/views/base_app_environment.rabl +1 -1
- data/lib/engineyard-cloud-client/test/fake_awsm/views/base_environment.rabl +1 -1
- data/lib/engineyard-cloud-client/test/fake_awsm/views/environments.rabl +1 -1
- data/lib/engineyard-cloud-client/test/scenario.rb +2 -3
- data/lib/engineyard-cloud-client/version.rb +1 -1
- data/spec/engineyard-cloud-client/api_spec.rb +15 -33
- data/spec/engineyard-cloud-client/integration/account_spec.rb +0 -1
- data/spec/engineyard-cloud-client/integration/app_environment_spec.rb +0 -1
- data/spec/engineyard-cloud-client/integration/app_spec.rb +0 -1
- data/spec/engineyard-cloud-client/integration/deployment_spec.rb +1 -2
- data/spec/engineyard-cloud-client/integration/environment_spec.rb +0 -1
- data/spec/engineyard-cloud-client/integration/user_spec.rb +6 -9
- data/spec/spec_helper.rb +0 -3
- data/spec/support/helpers.rb +2 -6
- metadata +9 -9
- data/lib/engineyard-cloud-client/test/ui.rb +0 -33
@@ -3,6 +3,7 @@ module EY
|
|
3
3
|
end
|
4
4
|
end
|
5
5
|
|
6
|
+
require 'engineyard-cloud-client/connection'
|
6
7
|
require 'engineyard-cloud-client/model_registry'
|
7
8
|
require 'engineyard-cloud-client/models'
|
8
9
|
require 'engineyard-cloud-client/rest_client_ext'
|
@@ -11,58 +12,33 @@ require 'engineyard-cloud-client/version'
|
|
11
12
|
require 'engineyard-cloud-client/errors'
|
12
13
|
require 'multi_json'
|
13
14
|
require 'pp'
|
15
|
+
require 'forwardable'
|
14
16
|
|
15
17
|
module EY
|
16
18
|
class CloudClient
|
17
|
-
|
18
|
-
attr_accessor :ui
|
19
|
+
extend Forwardable
|
19
20
|
|
20
|
-
|
21
|
+
def_delegators :connection, :head, :get, :post, :put, :delete, :request, :endpoint, :token, :token=, :authenticate!, :authenticated?
|
21
22
|
|
22
|
-
|
23
|
-
@endpoint
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.endpoint=(endpoint)
|
27
|
-
@endpoint = URI.parse(endpoint)
|
28
|
-
unless @endpoint.absolute?
|
29
|
-
raise BadEndpointError.new(endpoint)
|
30
|
-
end
|
31
|
-
@endpoint
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.default_endpoint!
|
35
|
-
self.endpoint = "https://cloud.engineyard.com/"
|
36
|
-
end
|
37
|
-
default_endpoint!
|
23
|
+
attr_reader :connection
|
38
24
|
|
39
|
-
|
40
|
-
|
41
|
-
|
25
|
+
# Initialize a new EY::CloudClient.
|
26
|
+
#
|
27
|
+
# Creates and stores a new Connection for communicating with EY Cloud.
|
28
|
+
#
|
29
|
+
# See EY::CloudClient::Connection for options.
|
30
|
+
def initialize(options={})
|
31
|
+
@connection = Connection.new(options)
|
42
32
|
end
|
43
33
|
|
44
34
|
def ==(other)
|
45
|
-
other.is_a?(self.class) && other.
|
35
|
+
other.is_a?(self.class) && other.connection == connection
|
46
36
|
end
|
47
37
|
|
48
38
|
def registry
|
49
39
|
@registry ||= ModelRegistry.new
|
50
40
|
end
|
51
41
|
|
52
|
-
def token=(new_token)
|
53
|
-
unless new_token
|
54
|
-
raise ArgumentError, "EY Cloud API token required"
|
55
|
-
end
|
56
|
-
@token = new_token
|
57
|
-
end
|
58
|
-
|
59
|
-
def request(url, opts={})
|
60
|
-
opts[:headers] ||= {}
|
61
|
-
opts[:headers]["X-EY-Cloud-Token"] = token
|
62
|
-
ui.debug("Token", token)
|
63
|
-
self.class.request(url, ui, opts)
|
64
|
-
end
|
65
|
-
|
66
42
|
def resolve_environments(constraints)
|
67
43
|
EY::CloudClient::Environment.resolve(self, constraints)
|
68
44
|
end
|
@@ -86,62 +62,7 @@ module EY
|
|
86
62
|
end
|
87
63
|
|
88
64
|
def current_user
|
89
|
-
EY::CloudClient::User.from_hash(self,
|
90
|
-
end
|
91
|
-
|
92
|
-
def self.request(path, ui, opts={})
|
93
|
-
url = self.endpoint + "api/v2#{path}"
|
94
|
-
method = (opts.delete(:method) || 'get').to_s.downcase.to_sym
|
95
|
-
params = opts.delete(:params) || {}
|
96
|
-
headers = opts.delete(:headers) || {}
|
97
|
-
headers["Accept"] ||= "application/json"
|
98
|
-
headers["User-Agent"] = USER_AGENT_STRING
|
99
|
-
|
100
|
-
begin
|
101
|
-
ui.debug("Request", "#{method.to_s.upcase} #{url}")
|
102
|
-
ui.debug("Params", params.inspect)
|
103
|
-
case method
|
104
|
-
when :get, :delete, :head
|
105
|
-
unless params.empty?
|
106
|
-
url.query = RestClient::Payload::UrlEncoded.new(params).to_s
|
107
|
-
end
|
108
|
-
resp = RestClient.send(method, url.to_s, headers)
|
109
|
-
else
|
110
|
-
resp = RestClient.send(method, url.to_s, params, headers)
|
111
|
-
end
|
112
|
-
rescue RestClient::Unauthorized
|
113
|
-
raise InvalidCredentials
|
114
|
-
rescue Errno::ECONNREFUSED
|
115
|
-
raise RequestFailed, "Could not reach the cloud API"
|
116
|
-
rescue RestClient::ResourceNotFound
|
117
|
-
raise ResourceNotFound, "The requested resource could not be found"
|
118
|
-
rescue RestClient::BadGateway
|
119
|
-
raise RequestFailed, "EY Cloud API is temporarily unavailable. Please try again soon."
|
120
|
-
rescue RestClient::RequestFailed => e
|
121
|
-
raise RequestFailed, "#{e.message} #{e.response}"
|
122
|
-
rescue OpenSSL::SSL::SSLError
|
123
|
-
raise RequestFailed, "SSL is misconfigured on your cloud"
|
124
|
-
end
|
125
|
-
|
126
|
-
if resp.body.empty?
|
127
|
-
data = ''
|
128
|
-
elsif resp.headers[:content_type] =~ /application\/json/
|
129
|
-
begin
|
130
|
-
data = MultiJson.load(resp.body)
|
131
|
-
ui.debug("Response", "\n" + data.pretty_inspect)
|
132
|
-
rescue MultiJson::DecodeError
|
133
|
-
ui.debug("Raw response", resp.body)
|
134
|
-
raise RequestFailed, "Response was not valid JSON."
|
135
|
-
end
|
136
|
-
else
|
137
|
-
data = resp.body
|
138
|
-
end
|
139
|
-
|
140
|
-
data
|
141
|
-
end
|
142
|
-
|
143
|
-
def self.authenticate(email, password, ui)
|
144
|
-
request("/authenticate", ui, { :method => "post", :params => { :email => email, :password => password }})["api_token"]
|
65
|
+
EY::CloudClient::User.from_hash(self, get('/current_user')['user'])
|
145
66
|
end
|
146
67
|
|
147
68
|
end # API
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'engineyard-cloud-client/rest_client_ext'
|
2
|
+
require 'multi_json'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module EY
|
6
|
+
class CloudClient
|
7
|
+
class Connection
|
8
|
+
attr_reader :token, :output, :user_agent, :endpoint
|
9
|
+
|
10
|
+
BASE_USER_AGENT = "EngineYardCloudClient/#{EY::CloudClient::VERSION}".freeze
|
11
|
+
DEFAULT_ENDPOINT = "https://cloud.engineyard.com/".freeze
|
12
|
+
|
13
|
+
# Initialize a new EY::CloudClient::Connection with a hash including:
|
14
|
+
#
|
15
|
+
# :token: (optional) Perform authenticated requests with this token
|
16
|
+
# :user_agent: (optional) A user agent name/version pair to add to the User-Agent header. (e.g. EngineYardCLI/2.0.0)
|
17
|
+
# :output: (optional) Send output to a stream other than $stdout
|
18
|
+
# :endpoint: (optional) An alternate Engine Yard Cloud endpoint URI
|
19
|
+
def initialize(options={})
|
20
|
+
@output = options[:output] || $stdout
|
21
|
+
@user_agent = [options[:user_agent], BASE_USER_AGENT].compact.join(' ').strip
|
22
|
+
@endpoint = URI.parse(options[:endpoint] || DEFAULT_ENDPOINT)
|
23
|
+
self.token = options[:token]
|
24
|
+
|
25
|
+
unless @endpoint.absolute?
|
26
|
+
raise BadEndpointError.new(@endpoint)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def token=(new_token)
|
31
|
+
@token = new_token
|
32
|
+
end
|
33
|
+
|
34
|
+
def debug(name, value)
|
35
|
+
return unless ENV['DEBUG']
|
36
|
+
|
37
|
+
indent = 12 # 12 because that's what Thor used.
|
38
|
+
unless String === value
|
39
|
+
value = value.pretty_inspect.rstrip # remove trailing whitespace
|
40
|
+
if value.index("\n") # if the inspect is multi-line
|
41
|
+
value.gsub!(/[\r\n]./, "\n" + ' ' * (indent + 2)) # indent it
|
42
|
+
end
|
43
|
+
end
|
44
|
+
@output << "#{name.to_s.rjust(indent)} #{value.rstrip}\n" # just one newline
|
45
|
+
end
|
46
|
+
|
47
|
+
def ==(other)
|
48
|
+
other.is_a?(self.class) && [other.token, other.user_agent, other.endpoint] == [token, user_agent, endpoint]
|
49
|
+
end
|
50
|
+
|
51
|
+
%w[ get head post put delete ].each do |meth|
|
52
|
+
eval <<-RUBY, binding, __FILE__, __LINE__ + 1
|
53
|
+
def #{meth}(path, params=nil, headers=nil, &block)
|
54
|
+
request("#{meth}", path, params, headers, &block)
|
55
|
+
end
|
56
|
+
RUBY
|
57
|
+
end
|
58
|
+
|
59
|
+
def request(meth, path, params=nil, extra_headers=nil)
|
60
|
+
url = endpoint + "api/v2#{path}"
|
61
|
+
meth ||= 'get'
|
62
|
+
meth = meth.to_s.downcase.to_sym
|
63
|
+
params ||= {}
|
64
|
+
|
65
|
+
headers = {
|
66
|
+
"User-Agent" => user_agent,
|
67
|
+
"Accept" => "application/json",
|
68
|
+
}
|
69
|
+
|
70
|
+
if token
|
71
|
+
headers["X-EY-Cloud-Token"] = token
|
72
|
+
end
|
73
|
+
|
74
|
+
if extra_headers
|
75
|
+
headers.merge!(extra_headers)
|
76
|
+
end
|
77
|
+
|
78
|
+
debug(meth.to_s.upcase, url.to_s)
|
79
|
+
debug("Params", params) if params
|
80
|
+
debug("Headers", headers)
|
81
|
+
|
82
|
+
resp = do_request(meth, url, params, headers)
|
83
|
+
data = parse_response(resp)
|
84
|
+
|
85
|
+
data
|
86
|
+
end
|
87
|
+
|
88
|
+
def authenticate!(email, password)
|
89
|
+
response = post("/authenticate", :email => email, :password => password)
|
90
|
+
self.token = response["api_token"]
|
91
|
+
token
|
92
|
+
end
|
93
|
+
|
94
|
+
def authenticated?
|
95
|
+
!token.nil? && !token.empty?
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def do_request(meth, url, params, headers)
|
101
|
+
case meth
|
102
|
+
when :get, :delete, :head
|
103
|
+
if params
|
104
|
+
url.query = RestClient::Payload::UrlEncoded.new(params).to_s
|
105
|
+
end
|
106
|
+
RestClient.send(meth, url.to_s, headers)
|
107
|
+
else
|
108
|
+
RestClient.send(meth, url.to_s, params, headers)
|
109
|
+
end
|
110
|
+
rescue RestClient::Unauthorized
|
111
|
+
raise InvalidCredentials
|
112
|
+
rescue Errno::ECONNREFUSED
|
113
|
+
raise RequestFailed, "Could not reach the cloud API"
|
114
|
+
rescue RestClient::ResourceNotFound
|
115
|
+
raise ResourceNotFound, "The requested resource could not be found"
|
116
|
+
rescue RestClient::BadGateway
|
117
|
+
raise RequestFailed, "EY Cloud API is temporarily unavailable. Please try again soon."
|
118
|
+
rescue RestClient::RequestFailed => e
|
119
|
+
raise RequestFailed, "#{e.message} #{e.response}"
|
120
|
+
rescue OpenSSL::SSL::SSLError
|
121
|
+
raise RequestFailed, "SSL is misconfigured on your cloud"
|
122
|
+
end
|
123
|
+
|
124
|
+
def parse_response(resp)
|
125
|
+
if resp.body.empty?
|
126
|
+
''
|
127
|
+
elsif resp.headers[:content_type] =~ /application\/json/
|
128
|
+
begin
|
129
|
+
data = MultiJson.load(resp.body)
|
130
|
+
debug("Response", data)
|
131
|
+
data
|
132
|
+
rescue MultiJson::DecodeError
|
133
|
+
debug("Response", resp.body)
|
134
|
+
raise RequestFailed, "Response was not valid JSON."
|
135
|
+
end
|
136
|
+
else
|
137
|
+
resp.body
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -9,7 +9,7 @@ module EY
|
|
9
9
|
|
10
10
|
# Return list of all Apps linked to all current user's accounts
|
11
11
|
def self.all(api)
|
12
|
-
self.from_array(api, api.
|
12
|
+
self.from_array(api, api.get("/apps", 'no_instances' => 'true')["apps"])
|
13
13
|
end
|
14
14
|
|
15
15
|
# An everything-you-need helper to create an App
|
@@ -32,7 +32,7 @@ module EY
|
|
32
32
|
raise EY::CloudClient::AttributeRequiredError.new("name") unless params["name"]
|
33
33
|
raise EY::CloudClient::AttributeRequiredError.new("repository_uri") unless params["repository_uri"]
|
34
34
|
raise EY::CloudClient::AttributeRequiredError.new("app_type_id") unless params["app_type_id"]
|
35
|
-
response = api.
|
35
|
+
response = api.post("/accounts/#{account.id}/apps", "app" => params)
|
36
36
|
from_hash(api, response['app'])
|
37
37
|
end
|
38
38
|
|
@@ -7,15 +7,15 @@ module EY
|
|
7
7
|
|
8
8
|
# Return a constrained list of app_environments given a set of constraints like:
|
9
9
|
#
|
10
|
-
# * app_name
|
11
|
-
# * account_name
|
12
|
-
# * environment_name
|
10
|
+
# * app_name: app name full or partial match string
|
11
|
+
# * account_name: account name full or partial match string
|
12
|
+
# * environment_name: environment name full or partial match string
|
13
13
|
# * remotes: An array of git remote URIs
|
14
14
|
#
|
15
15
|
def self.resolve(api, constraints)
|
16
16
|
clean_constraints = constraints.reject { |k,v| v.nil? }
|
17
17
|
params = {'constraints' => clean_constraints}
|
18
|
-
response = api.
|
18
|
+
response = api.get("/app_environments/resolve", params)['resolver']
|
19
19
|
matches = from_array(api, response['matches'])
|
20
20
|
ResolverResult.new(api, matches, response['errors'], response['suggestions'])
|
21
21
|
end
|
@@ -14,7 +14,7 @@ module EY
|
|
14
14
|
|
15
15
|
def self.get(api, app_environment, id)
|
16
16
|
uri = api_root(app_environment.app.id, app_environment.environment.id) + "/#{id}"
|
17
|
-
response = api.
|
17
|
+
response = api.get(uri)
|
18
18
|
load_from_response api, app_environment, response
|
19
19
|
rescue EY::CloudClient::ResourceNotFound
|
20
20
|
nil
|
@@ -47,16 +47,16 @@ module EY
|
|
47
47
|
alias deployed_by= user_name=
|
48
48
|
|
49
49
|
def created_at
|
50
|
-
|
50
|
+
@created_at ||= super && Time.parse(super)
|
51
51
|
end
|
52
52
|
|
53
53
|
def finished_at
|
54
|
-
|
54
|
+
@finished_at ||= super && Time.parse(super)
|
55
55
|
end
|
56
56
|
|
57
57
|
def config
|
58
58
|
return {} unless deployed_by # not started yet so not all info is here
|
59
|
-
@config ||= {'deployed_by' => deployed_by}.merge(extra_config)
|
59
|
+
@config ||= {'input_ref' => ref, 'deployed_by' => deployed_by}.merge(extra_config)
|
60
60
|
end
|
61
61
|
|
62
62
|
def start
|
@@ -95,11 +95,11 @@ module EY
|
|
95
95
|
private
|
96
96
|
|
97
97
|
def post_to_api(params)
|
98
|
-
update_with_response api.
|
98
|
+
update_with_response api.post(collection_uri, 'deployment' => params)
|
99
99
|
end
|
100
100
|
|
101
101
|
def put_to_api(params)
|
102
|
-
update_with_response api.
|
102
|
+
update_with_response api.put(member_uri("/finished"), 'deployment' => params)
|
103
103
|
end
|
104
104
|
|
105
105
|
def collection_uri
|
@@ -4,27 +4,29 @@ require 'engineyard-cloud-client/errors'
|
|
4
4
|
|
5
5
|
module EY
|
6
6
|
class CloudClient
|
7
|
-
class Environment < ApiStruct.new(:id, :name, :framework_env,
|
7
|
+
class Environment < ApiStruct.new(:id, :name, :framework_env,
|
8
|
+
:instances_count,
|
9
|
+
:instance_status,
|
8
10
|
:username, :app_server_stack_name,
|
9
11
|
:load_balancer_ip_address
|
10
12
|
)
|
11
13
|
|
12
14
|
# Return list of all Environments linked to all current user's accounts
|
13
15
|
def self.all(api)
|
14
|
-
self.from_array(api, api.
|
16
|
+
self.from_array(api, api.get("/environments", "no_instances" => "true")["environments"])
|
15
17
|
end
|
16
18
|
|
17
19
|
# Return a constrained list of environments given a set of constraints like:
|
18
20
|
#
|
19
|
-
# * app_name
|
20
|
-
# * account_name
|
21
|
-
# * environment_name
|
21
|
+
# * app_name: app name full or partial match string
|
22
|
+
# * account_name: account name full or partial match string
|
23
|
+
# * environment_name: environment name full or partial match string
|
22
24
|
# * remotes: An array of git remote URIs
|
23
25
|
#
|
24
26
|
def self.resolve(api, constraints)
|
25
27
|
clean_constraints = constraints.reject { |k,v| v.nil? }
|
26
28
|
params = {'constraints' => clean_constraints}
|
27
|
-
response = api.
|
29
|
+
response = api.get("/environments/resolve", params)['resolver']
|
28
30
|
matches = from_array(api, response['matches'])
|
29
31
|
ResolverResult.new(api, matches, response['errors'], response['suggestions'])
|
30
32
|
end
|
@@ -52,7 +54,7 @@ module EY
|
|
52
54
|
|
53
55
|
params = {"environment" => attrs.dup}
|
54
56
|
unpack_cluster_configuration(params, cluster_configuration)
|
55
|
-
response = api.
|
57
|
+
response = api.post("/apps/#{app.id}/environments", params)
|
56
58
|
self.from_hash(api, response['environment'])
|
57
59
|
end
|
58
60
|
attr_accessor :apps, :account
|
@@ -99,7 +101,7 @@ module EY
|
|
99
101
|
end
|
100
102
|
|
101
103
|
def logs
|
102
|
-
Log.from_array(api, api.
|
104
|
+
Log.from_array(api, api.get("/environments/#{id}/logs")["logs"])
|
103
105
|
end
|
104
106
|
|
105
107
|
def deploy_to_instances
|
@@ -114,13 +116,13 @@ module EY
|
|
114
116
|
if bridge.nil?
|
115
117
|
raise NoBridgeError.new(name)
|
116
118
|
elsif !ignore_bad_bridge && bridge.status != "running"
|
117
|
-
raise BadBridgeStatusError.new(bridge.status,
|
119
|
+
raise BadBridgeStatusError.new(bridge.status, api.endpoint)
|
118
120
|
end
|
119
121
|
bridge
|
120
122
|
end
|
121
123
|
|
122
124
|
def update
|
123
|
-
api.
|
125
|
+
api.put("/environments/#{id}/update_instances")
|
124
126
|
true # raises on failure
|
125
127
|
end
|
126
128
|
alias rebuild update
|
@@ -178,7 +180,7 @@ module EY
|
|
178
180
|
if instances_count.zero?
|
179
181
|
[]
|
180
182
|
else
|
181
|
-
instances_attrs = api.
|
183
|
+
instances_attrs = api.get("/environments/#{id}/instances")["instances"]
|
182
184
|
load_instances(instances_attrs)
|
183
185
|
end
|
184
186
|
end
|
@@ -5,7 +5,7 @@ module EY
|
|
5
5
|
class Keypair < ApiStruct.new(:id, :name, :public_key)
|
6
6
|
|
7
7
|
def self.all(api)
|
8
|
-
self.from_array(api, api.
|
8
|
+
self.from_array(api, api.get("/keypairs")["keypairs"])
|
9
9
|
end
|
10
10
|
|
11
11
|
# Create a Keypair with your SSH public key so that you can access your Instances
|
@@ -24,8 +24,8 @@ module EY
|
|
24
24
|
params = attrs.dup # no default fields
|
25
25
|
raise EY::CloudClient::AttributeRequiredError.new("name") unless params["name"]
|
26
26
|
raise EY::CloudClient::AttributeRequiredError.new("public_key") unless params["public_key"]
|
27
|
-
response = api.
|
28
|
-
self.from_hash(api, response
|
27
|
+
response = api.post("/keypairs", "keypair" => params)['keypair']
|
28
|
+
self.from_hash(api, response)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -12,13 +12,13 @@ module EY
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def run
|
15
|
-
api.
|
15
|
+
api.put("/environments/#{environment.id}/run_custom_recipes")
|
16
16
|
true
|
17
17
|
end
|
18
18
|
|
19
19
|
def download
|
20
20
|
tmp = Tempfile.new("recipes")
|
21
|
-
data = api.
|
21
|
+
data = api.get("/environments/#{environment.id}/recipes")
|
22
22
|
tmp.write(data)
|
23
23
|
tmp.flush
|
24
24
|
tmp.close
|
@@ -37,10 +37,7 @@ module EY
|
|
37
37
|
# Expects a File object opened for binary reading.
|
38
38
|
# i.e. upload(File.open(recipes_path, 'rb'))
|
39
39
|
def upload(file_to_upload)
|
40
|
-
api.
|
41
|
-
:method => :post,
|
42
|
-
:params => {:file => file_to_upload}
|
43
|
-
})
|
40
|
+
api.post("/environments/#{environment.id}/recipes", :file => file_to_upload)
|
44
41
|
true
|
45
42
|
end
|
46
43
|
|
@@ -15,11 +15,12 @@ rescue LoadError
|
|
15
15
|
engineyard-cloud-client needs the following gems to run in test mode:
|
16
16
|
|
17
17
|
group 'engineyard-cloud-client-test' do
|
18
|
-
gem 'dm-core'
|
18
|
+
gem 'dm-core', '~>1.2.0'
|
19
19
|
gem 'dm-migrations'
|
20
20
|
gem 'dm-aggregates'
|
21
|
+
gem 'dm-timestamps'
|
21
22
|
gem 'dm-sqlite-adapter'
|
22
|
-
gem 'ey_resolver',
|
23
|
+
gem 'ey_resolver', '~>0.2.1'
|
23
24
|
gem 'rabl'
|
24
25
|
end
|
25
26
|
|
@@ -27,5 +28,4 @@ Please add the above to your Gemfile.
|
|
27
28
|
ERROR
|
28
29
|
end
|
29
30
|
|
30
|
-
require 'engineyard-cloud-client/test/ui'
|
31
31
|
require 'engineyard-cloud-client/test/scenario'
|
@@ -36,9 +36,10 @@ class FakeAwsm < Sinatra::Base
|
|
36
36
|
before do
|
37
37
|
if env['PATH_INFO'] =~ %r#/api/v2#
|
38
38
|
user_agent = env['HTTP_USER_AGENT']
|
39
|
-
unless user_agent =~ %r
|
40
|
-
|
41
|
-
|
39
|
+
unless user_agent =~ %r#EngineYardCloudClient/\d#
|
40
|
+
msg = "No user agent header, expected EngineYardCloudClient/ got #{user_agent.inspect}"
|
41
|
+
$stderr.puts msg
|
42
|
+
halt 400, msg
|
42
43
|
end
|
43
44
|
end
|
44
45
|
content_type "application/json"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
attributes :id, :domain_name, :uri
|
2
2
|
child :environment do
|
3
|
-
attributes :id, :ssh_username, :name, :instances_count, :app_server_stack_name, :load_balancer_ip_address, :framework_env
|
3
|
+
attributes :id, :ssh_username, :name, :instances_count, :instance_status, :app_server_stack_name, :load_balancer_ip_address, :framework_env
|
4
4
|
child :account do
|
5
5
|
attributes :id, :name
|
6
6
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
attributes :id, :ssh_username, :name, :instances_count, :app_server_stack_name, :load_balancer_ip_address, :framework_env
|
1
|
+
attributes :id, :ssh_username, :name, :instances_count, :instance_status, :app_server_stack_name, :load_balancer_ip_address, :framework_env
|
2
2
|
child :account do
|
3
3
|
attributes :id, :name
|
4
4
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
collection @environments, :root => :environments, :object_root => false
|
2
|
-
attributes :id, :ssh_username, :name, :instances_count, :app_server_stack_name, :load_balancer_ip_address, :framework_env
|
2
|
+
attributes :id, :ssh_username, :name, :instances_count, :instance_status, :app_server_stack_name, :load_balancer_ip_address, :framework_env
|
3
3
|
child :account do
|
4
4
|
attributes :id, :name
|
5
5
|
end
|
@@ -2,7 +2,6 @@ require 'multi_json'
|
|
2
2
|
require 'engineyard-cloud-client/rest_client_ext'
|
3
3
|
require 'engineyard-cloud-client/test'
|
4
4
|
require 'engineyard-cloud-client/test/fake_awsm'
|
5
|
-
require 'engineyard-cloud-client/test/ui'
|
6
5
|
|
7
6
|
module EY::CloudClient::Test
|
8
7
|
class Scenario
|
@@ -32,8 +31,8 @@ module EY::CloudClient::Test
|
|
32
31
|
@api_token = options['api_token']
|
33
32
|
end
|
34
33
|
|
35
|
-
def cloud_client
|
36
|
-
EY::CloudClient.new(@api_token
|
34
|
+
def cloud_client
|
35
|
+
EY::CloudClient.new(:endpoint => EY::CloudClient::Test::FakeAwsm.uri, :token => @api_token)
|
37
36
|
end
|
38
37
|
|
39
38
|
def inspect
|
@@ -2,50 +2,32 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe EY::CloudClient do
|
4
4
|
it "holds an api token" do
|
5
|
-
EY::CloudClient.new('asdf'
|
5
|
+
EY::CloudClient.new(:token => 'asdf').connection.token.should == "asdf"
|
6
6
|
end
|
7
7
|
|
8
|
-
it "
|
9
|
-
|
8
|
+
it "uses production EY Cloud by default" do
|
9
|
+
FakeWeb.register_uri(:post, "https://cloud.engineyard.com/api/v2/authenticate", :body => %|{"api_token": "cloudtoken"}|, :content_type => 'application/json')
|
10
|
+
client = EY::CloudClient.new
|
11
|
+
client.authenticate!("a@b.com", "foo")
|
12
|
+
client.connection.token.should == "cloudtoken"
|
10
13
|
end
|
11
14
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
it "defaults to production EY Cloud" do
|
18
|
-
EY::CloudClient.endpoint.should == URI.parse('https://cloud.engineyard.com')
|
19
|
-
end
|
20
|
-
|
21
|
-
it "accepts a valid endpoint" do
|
22
|
-
EY::CloudClient.endpoint = "http://fake.local/"
|
23
|
-
EY::CloudClient.endpoint.should == URI.parse('http://fake.local')
|
24
|
-
end
|
25
|
-
|
26
|
-
it "uses the endpoint to make requests" do
|
27
|
-
FakeWeb.register_uri(:post, "http://fake.local/api/v2/authenticate", :body => %|{"api_token": "fake.localtoken"}|, :content_type => 'application/json')
|
28
|
-
|
29
|
-
EY::CloudClient.endpoint = "http://fake.local/"
|
30
|
-
EY::CloudClient.authenticate("a@b.com", "foo", test_ui).should == "fake.localtoken"
|
31
|
-
end
|
32
|
-
|
33
|
-
it "raises on an invalid endpoint" do
|
34
|
-
lambda { EY::CloudClient.endpoint = "non/absolute" }.should raise_error(EY::CloudClient::BadEndpointError)
|
35
|
-
end
|
15
|
+
it "uses a custom endpoint to make requests" do
|
16
|
+
FakeWeb.register_uri(:post, "http://fake.local/api/v2/authenticate", :body => %|{"api_token": "fake.localtoken"}|, :content_type => 'application/json')
|
17
|
+
client = EY::CloudClient.new(:endpoint => "http://fake.local/")
|
18
|
+
client.authenticate!("a@b.com", "foo")
|
19
|
+
client.connection.token.should == "fake.localtoken"
|
36
20
|
end
|
37
21
|
|
38
|
-
it "
|
39
|
-
|
40
|
-
|
41
|
-
EY::CloudClient.authenticate("a@b.com", "foo", test_ui).should == "asdf"
|
22
|
+
it "raises on an invalid endpoint" do
|
23
|
+
lambda { EY::CloudClient.new(:endpoint => "non/absolute") }.should raise_error(EY::CloudClient::BadEndpointError)
|
42
24
|
end
|
43
25
|
|
44
26
|
it "raises InvalidCredentials when the credentials are invalid" do
|
45
27
|
FakeWeb.register_uri(:post, "https://cloud.engineyard.com/api/v2/authenticate", :status => 401, :content_type => 'application/json')
|
46
28
|
|
47
29
|
lambda {
|
48
|
-
EY::CloudClient.authenticate("a@b.com", "foo"
|
30
|
+
EY::CloudClient.new.authenticate!("a@b.com", "foo")
|
49
31
|
}.should raise_error(EY::CloudClient::InvalidCredentials)
|
50
32
|
end
|
51
33
|
|
@@ -53,7 +35,7 @@ describe EY::CloudClient do
|
|
53
35
|
FakeWeb.register_uri(:post, "https://cloud.engineyard.com/api/v2/authenticate", :status => 502, :content_type => 'text/html')
|
54
36
|
|
55
37
|
lambda {
|
56
|
-
EY::CloudClient.authenticate("a@b.com", "foo"
|
38
|
+
EY::CloudClient.new.authenticate!("a@b.com", "foo")
|
57
39
|
}.should raise_error(EY::CloudClient::RequestFailed, /API is temporarily unavailable/)
|
58
40
|
end
|
59
41
|
end
|
@@ -3,7 +3,6 @@ require 'spec_helper'
|
|
3
3
|
describe EY::CloudClient::AppEnvironment do
|
4
4
|
before(:each) do
|
5
5
|
FakeWeb.allow_net_connect = true
|
6
|
-
EY::CloudClient.endpoint = EY::CloudClient::Test::FakeAwsm.uri
|
7
6
|
end
|
8
7
|
|
9
8
|
describe "deploying" do
|
@@ -31,7 +30,7 @@ describe EY::CloudClient::AppEnvironment do
|
|
31
30
|
|
32
31
|
deployment.created_at.should_not be_nil
|
33
32
|
deployment.finished_at.should be_nil
|
34
|
-
deployment.config.should == {'deployed_by' => 'Multiple Ambiguous Accounts', 'extra' => 'config'}
|
33
|
+
deployment.config.should == {'input_ref' => 'master', 'deployed_by' => 'Multiple Ambiguous Accounts', 'extra' => 'config'}
|
35
34
|
deployment.commit.should =~ /[0-9a-f]{40}/
|
36
35
|
deployment.resolved_ref.should_not be_nil
|
37
36
|
deployment.out << "Test output"
|
@@ -3,16 +3,13 @@ require 'spec_helper'
|
|
3
3
|
describe EY::CloudClient::User do
|
4
4
|
before do
|
5
5
|
FakeWeb.allow_net_connect = true
|
6
|
-
EY::CloudClient.endpoint = EY::CloudClient::Test::FakeAwsm.uri
|
7
6
|
end
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
user.accounts.first.name.should == 'main'
|
16
|
-
end
|
8
|
+
it "loads current user and returns all accounts" do
|
9
|
+
api = scenario_cloud_client "User Name"
|
10
|
+
user = api.current_user
|
11
|
+
user.name.should == 'User Name'
|
12
|
+
user.accounts.size.should == 1
|
13
|
+
user.accounts.first.name.should == 'main'
|
17
14
|
end
|
18
15
|
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/helpers.rb
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
require 'rest_client'
|
2
2
|
|
3
3
|
module SpecHelpers
|
4
|
-
def
|
5
|
-
@
|
6
|
-
end
|
7
|
-
|
8
|
-
def cloud_client(token = 'asdf', ui = test_ui)
|
9
|
-
@cloud_client ||= EY::CloudClient.new(token, ui)
|
4
|
+
def cloud_client(token = 'asdf')
|
5
|
+
@cloud_client ||= EY::CloudClient.new(:token => token)
|
10
6
|
end
|
11
7
|
|
12
8
|
def scenario_cloud_client(scenario)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: engineyard-cloud-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-08-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rest-client
|
@@ -160,17 +160,17 @@ dependencies:
|
|
160
160
|
requirement: !ruby/object:Gem::Requirement
|
161
161
|
none: false
|
162
162
|
requirements:
|
163
|
-
- -
|
163
|
+
- - ~>
|
164
164
|
- !ruby/object:Gem::Version
|
165
|
-
version:
|
165
|
+
version: 1.2.0
|
166
166
|
type: :development
|
167
167
|
prerelease: false
|
168
168
|
version_requirements: !ruby/object:Gem::Requirement
|
169
169
|
none: false
|
170
170
|
requirements:
|
171
|
-
- -
|
171
|
+
- - ~>
|
172
172
|
- !ruby/object:Gem::Version
|
173
|
-
version:
|
173
|
+
version: 1.2.0
|
174
174
|
- !ruby/object:Gem::Dependency
|
175
175
|
name: dm-migrations
|
176
176
|
requirement: !ruby/object:Gem::Requirement
|
@@ -273,6 +273,7 @@ executables: []
|
|
273
273
|
extensions: []
|
274
274
|
extra_rdoc_files: []
|
275
275
|
files:
|
276
|
+
- lib/engineyard-cloud-client/connection.rb
|
276
277
|
- lib/engineyard-cloud-client/errors.rb
|
277
278
|
- lib/engineyard-cloud-client/model_registry.rb
|
278
279
|
- lib/engineyard-cloud-client/models/account.rb
|
@@ -312,7 +313,6 @@ files:
|
|
312
313
|
- lib/engineyard-cloud-client/test/fake_awsm/views/user.rabl
|
313
314
|
- lib/engineyard-cloud-client/test/fake_awsm.rb
|
314
315
|
- lib/engineyard-cloud-client/test/scenario.rb
|
315
|
-
- lib/engineyard-cloud-client/test/ui.rb
|
316
316
|
- lib/engineyard-cloud-client/test.rb
|
317
317
|
- lib/engineyard-cloud-client/version.rb
|
318
318
|
- lib/engineyard-cloud-client.rb
|
@@ -348,7 +348,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
348
348
|
version: '0'
|
349
349
|
segments:
|
350
350
|
- 0
|
351
|
-
hash:
|
351
|
+
hash: 2753056383473924638
|
352
352
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
353
353
|
none: false
|
354
354
|
requirements:
|
@@ -357,7 +357,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
357
357
|
version: '0'
|
358
358
|
segments:
|
359
359
|
- 0
|
360
|
-
hash:
|
360
|
+
hash: 2753056383473924638
|
361
361
|
requirements: []
|
362
362
|
rubyforge_project:
|
363
363
|
rubygems_version: 1.8.24
|
@@ -1,33 +0,0 @@
|
|
1
|
-
require 'engineyard-cloud-client/test'
|
2
|
-
|
3
|
-
module EY::CloudClient::Test
|
4
|
-
class QuietUI
|
5
|
-
def info(*)
|
6
|
-
end
|
7
|
-
|
8
|
-
def debug(*)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
class VerboseUI
|
13
|
-
def info(name, message = nil)
|
14
|
-
say name, message
|
15
|
-
end
|
16
|
-
|
17
|
-
def debug(name, message = nil)
|
18
|
-
name = name.inspect unless name.nil? or name.is_a?(String)
|
19
|
-
message = message.inspect unless message.nil? or message.is_a?(String)
|
20
|
-
say name, message
|
21
|
-
end
|
22
|
-
|
23
|
-
def say(status, message = nil)
|
24
|
-
if message
|
25
|
-
$stdout.puts "#{status.to_s.rjust(12)} #{message}"
|
26
|
-
else
|
27
|
-
$stdout.puts status
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
UI = ENV['DEBUG'] ? VerboseUI : QuietUI
|
33
|
-
end
|