engineyard-cloud-client 1.0.7 → 1.0.8
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/ChangeLog.md +46 -0
- data/{README.rdoc → README.md} +26 -3
- data/lib/engineyard-cloud-client/models/app_environment.rb +16 -0
- data/lib/engineyard-cloud-client/models/deployment.rb +24 -0
- data/lib/engineyard-cloud-client/models/environment.rb +6 -2
- data/lib/engineyard-cloud-client/models/instance.rb +7 -0
- data/lib/engineyard-cloud-client/test/fake_awsm.rb +1 -1
- data/lib/engineyard-cloud-client/test/fake_awsm/config.ru +22 -22
- data/lib/engineyard-cloud-client/test/fake_awsm/models/deployment.rb +19 -0
- data/lib/engineyard-cloud-client/test/fake_awsm/views/scenario.rabl +2 -0
- data/lib/engineyard-cloud-client/test/fake_awsm/views/scenarios.rabl +2 -0
- data/lib/engineyard-cloud-client/version.rb +1 -1
- data/spec/engineyard-cloud-client/integration/deployment_spec.rb +29 -0
- data/spec/spec_helper.rb +1 -1
- metadata +12 -25
data/ChangeLog.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# ChangeLog
|
2
|
+
|
3
|
+
## NEXT
|
4
|
+
|
5
|
+
*
|
6
|
+
|
7
|
+
## v1.0.8 (2013-02-14)
|
8
|
+
|
9
|
+
* Loosen the multi\_json gem version requirement to allow 1.0 compatible security fixes.
|
10
|
+
|
11
|
+
## v1.0.7 (2012-10-25)
|
12
|
+
|
13
|
+
* Send serverside\_version to the deployment API when starting a deploy.
|
14
|
+
|
15
|
+
## v1.0.6 (2012-08-20)
|
16
|
+
|
17
|
+
*
|
18
|
+
|
19
|
+
## v1.0.5 (2012-08-14)
|
20
|
+
|
21
|
+
*
|
22
|
+
|
23
|
+
## v1.0.4 (2012-08-14)
|
24
|
+
|
25
|
+
* Send input\_ref to deployments in the extra config.
|
26
|
+
* Use Connection object to take over for all api communication, simplifying the CloudClient class.
|
27
|
+
* Interface for creating a CloudClient has changed to support new Connection class.
|
28
|
+
|
29
|
+
## v1.0.3 (2012-06-13)
|
30
|
+
|
31
|
+
*
|
32
|
+
|
33
|
+
## v1.0.2 (2012-05-29)
|
34
|
+
|
35
|
+
*
|
36
|
+
|
37
|
+
## v1.0.1 (2012-05-22)
|
38
|
+
|
39
|
+
* Includes fixes for deployment test harness used by this gem and engineyard gem
|
40
|
+
|
41
|
+
## v1.0.0 (2012-05-22)
|
42
|
+
|
43
|
+
* First attempt at a real release.
|
44
|
+
* Provides all the functionality that is needed for engineyard gem to operate.
|
45
|
+
* Like Torchwood, The Colbert Report, and The Cleveland Show, start CloudClient's new life as a spin-off.
|
46
|
+
|
data/{README.rdoc → README.md}
RENAMED
@@ -1,6 +1,29 @@
|
|
1
|
-
|
1
|
+
# Engine Yard Cloud Client
|
2
2
|
|
3
|
-
engineyard-cloud-client contains a Ruby api library to the Engine Yard Cloud API. Extracted from the engineyard gem.
|
3
|
+
engineyard-cloud-client contains a Ruby api library to the Engine Yard Cloud API. Extracted from the [engineyard gem](https://github.com/engineyard/engineyard).
|
4
|
+
|
5
|
+
## Use at your own risk
|
6
|
+
|
7
|
+
At this time, cloud-client is not documented for public use. It was created to
|
8
|
+
be used by the engineyard gem with the hopes of eventually providing a public
|
9
|
+
api client, but it's still in its infancy.
|
10
|
+
|
11
|
+
If you would like to use this gem directly instead of interfacing with the
|
12
|
+
engineyard gem, you will likely be on your own. We may be available to answer
|
13
|
+
questions about the gem, but the gem itself is not currently a supported public
|
14
|
+
interface to Engine Yard Cloud.
|
15
|
+
|
16
|
+
This gem accesses API actions that can alter your dashboard and settings. If
|
17
|
+
used incorrectly, it could lead to problems. As we work towards making this a
|
18
|
+
reliable public client, the interface may change.
|
19
|
+
|
20
|
+
If you intend to use this gem, please lock to an exact version and pay close
|
21
|
+
attention to newly released versions and changes to the interface.
|
22
|
+
|
23
|
+
Please [open github issues](https://github.com/engineyard/engineyard-cloud-client/issues)
|
24
|
+
for any problems you encounter.
|
25
|
+
|
26
|
+
## Usage
|
4
27
|
|
5
28
|
Setup:
|
6
29
|
|
@@ -112,7 +135,7 @@ Instances:
|
|
112
135
|
instance.hostname # => "ec2-1-2-3-4.compute-1.amazonaws.com"
|
113
136
|
instance.public_hostname # => "ec2-1-2-3-4.compute-1.amazonaws.com" # alias of hostname
|
114
137
|
|
115
|
-
|
138
|
+
## Debugging:
|
116
139
|
|
117
140
|
When $DEBUG is set, display debug information to the ui object using the #debug method. The API commands will print internal request information:
|
118
141
|
|
@@ -62,10 +62,26 @@ module EY
|
|
62
62
|
Deployment.last(api, self)
|
63
63
|
end
|
64
64
|
|
65
|
+
# Create a new, unsaved, Deployment record.
|
66
|
+
#
|
67
|
+
# Call start on the return object to indicate to EY Cloud that you
|
68
|
+
# will be starting a deployment using your own connection to your
|
69
|
+
# servers. This is the way that the engineyard gem does deployments.
|
65
70
|
def new_deployment(attrs)
|
66
71
|
Deployment.from_hash(api, attrs.merge(:app_environment => self))
|
67
72
|
end
|
68
73
|
|
74
|
+
# Trigger a deployment on the api side.
|
75
|
+
#
|
76
|
+
# This is like hitting the deploy button on the web interface.
|
77
|
+
#
|
78
|
+
# Returns a started deployment that will run from EY Cloud automatically.
|
79
|
+
# Load the deployment again to see when it finishes. This action returns
|
80
|
+
# immediately before the deployment is complete.
|
81
|
+
def deploy(attrs)
|
82
|
+
Deployment.deploy(api, self, attrs)
|
83
|
+
end
|
84
|
+
|
69
85
|
protected
|
70
86
|
|
71
87
|
def set_app(app_or_hash)
|
@@ -27,6 +27,10 @@ module EY
|
|
27
27
|
dep
|
28
28
|
end
|
29
29
|
|
30
|
+
def self.deploy(api, app_environment, attrs)
|
31
|
+
Deployment.from_hash(api, attrs.merge(:app_environment => app_environment)).deploy
|
32
|
+
end
|
33
|
+
|
30
34
|
def app
|
31
35
|
app_environment.app
|
32
36
|
end
|
@@ -60,6 +64,10 @@ module EY
|
|
60
64
|
@config ||= {'input_ref' => ref, 'deployed_by' => deployed_by}.merge(extra_config)
|
61
65
|
end
|
62
66
|
|
67
|
+
# Tell EY Cloud that you will be starting a deploy yourself.
|
68
|
+
#
|
69
|
+
# The name for this method isn't great. It's a relic of how the deploy
|
70
|
+
# ran before it ever told EY Cloud that is was running a deploy at all.
|
63
71
|
def start
|
64
72
|
params = {
|
65
73
|
:migrate => migrate,
|
@@ -70,6 +78,21 @@ module EY
|
|
70
78
|
post_to_api(params)
|
71
79
|
end
|
72
80
|
|
81
|
+
# Tell EY Cloud to deploy on our behalf.
|
82
|
+
#
|
83
|
+
# Deploy is different from start in that it triggers the deploy remotely.
|
84
|
+
# This is almost exactly equivalent to pressing the deploy button on the
|
85
|
+
# dashboard. No output will be returned.
|
86
|
+
def deploy
|
87
|
+
params = {
|
88
|
+
:migrate => migrate,
|
89
|
+
:ref => ref,
|
90
|
+
}
|
91
|
+
params[:serverside_version] = serverside_version if serverside_version
|
92
|
+
params[:migrate_command] = migrate_command if migrate
|
93
|
+
update_with_response api.post(collection_uri + "/deploy", 'deployment' => params)
|
94
|
+
end
|
95
|
+
|
73
96
|
def output
|
74
97
|
@output ||= StringIO.new
|
75
98
|
end
|
@@ -95,6 +118,7 @@ module EY
|
|
95
118
|
response['deployment'].each do |key,val|
|
96
119
|
send("#{key}=", val) if respond_to?("#{key}=")
|
97
120
|
end
|
121
|
+
self
|
98
122
|
end
|
99
123
|
|
100
124
|
private
|
@@ -114,8 +114,12 @@ module EY
|
|
114
114
|
Log.from_array(api, api.get("/environments/#{id}/logs")["logs"])
|
115
115
|
end
|
116
116
|
|
117
|
+
def provisioned_instances
|
118
|
+
instances.select { |inst| inst.provisioned? }
|
119
|
+
end
|
120
|
+
|
117
121
|
def deploy_to_instances
|
118
|
-
|
122
|
+
provisioned_instances.select { |inst| inst.has_app_code? }
|
119
123
|
end
|
120
124
|
|
121
125
|
def bridge
|
@@ -125,7 +129,7 @@ module EY
|
|
125
129
|
def bridge!(ignore_bad_bridge = false)
|
126
130
|
if bridge.nil?
|
127
131
|
raise NoBridgeError.new(name)
|
128
|
-
elsif !ignore_bad_bridge && bridge.
|
132
|
+
elsif !ignore_bad_bridge && !bridge.running?
|
129
133
|
raise BadBridgeStatusError.new(bridge.status, api.endpoint)
|
130
134
|
end
|
131
135
|
bridge
|
@@ -10,6 +10,13 @@ module EY
|
|
10
10
|
!["db_master", "db_slave"].include?(role.to_s)
|
11
11
|
end
|
12
12
|
|
13
|
+
def running?
|
14
|
+
status == "running"
|
15
|
+
end
|
16
|
+
|
17
|
+
def provisioned?
|
18
|
+
hostname && role && status != "starting" # not foolproof, but help throw out bad instances
|
19
|
+
end
|
13
20
|
end
|
14
21
|
end
|
15
22
|
end
|
@@ -15,7 +15,7 @@ module EY::CloudClient::Test
|
|
15
15
|
unless system("ruby -c '#{config_ru}' > /dev/null")
|
16
16
|
raise SyntaxError, "There is a syntax error in fake_awsm/config.ru! FIX IT!"
|
17
17
|
end
|
18
|
-
@server = RealWeb.start_server_in_fork(config_ru)
|
18
|
+
@server = RealWeb.start_server_in_fork(config_ru, :timeout => 5)
|
19
19
|
@server.base_uri.to_s
|
20
20
|
end
|
21
21
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'sinatra/base'
|
3
|
-
require '
|
3
|
+
require 'multi_json'
|
4
4
|
require 'rabl'
|
5
5
|
require 'gitable'
|
6
6
|
require 'ey_resolver'
|
@@ -33,6 +33,12 @@ class FakeAwsm < Sinatra::Base
|
|
33
33
|
@user = Scenario::Base.new.user
|
34
34
|
end
|
35
35
|
|
36
|
+
helpers do
|
37
|
+
def json(data)
|
38
|
+
MultiJson.dump(data)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
36
42
|
before do
|
37
43
|
if env['PATH_INFO'] =~ %r#/api/v2#
|
38
44
|
user_agent = env['HTTP_USER_AGENT']
|
@@ -55,32 +61,18 @@ class FakeAwsm < Sinatra::Base
|
|
55
61
|
end
|
56
62
|
|
57
63
|
get "/scenario" do
|
58
|
-
|
59
|
-
unless
|
64
|
+
found_scenario = SCENARIOS.detect { |scen| scen.user.name == params[:scenario] }
|
65
|
+
unless found_scenario
|
60
66
|
status(404)
|
61
|
-
return {"ok" => "false", "message" => "wtf is the #{params[:scenario]} scenario?"}
|
67
|
+
return json({"ok" => "false", "message" => "wtf is the #{params[:scenario]} scenario?"})
|
62
68
|
end
|
63
|
-
|
64
|
-
|
65
|
-
"scenario" => {
|
66
|
-
"email" => user.email,
|
67
|
-
"password" => user.password,
|
68
|
-
"api_token" => user.api_token,
|
69
|
-
}
|
70
|
-
}.to_json
|
69
|
+
@scenario = found_scenario.user
|
70
|
+
render :rabl, :scenario, :format => "json"
|
71
71
|
end
|
72
72
|
|
73
73
|
get "/scenarios" do
|
74
|
-
scenarios = SCENARIOS.map
|
75
|
-
|
76
|
-
{
|
77
|
-
:name => user.name,
|
78
|
-
:email => user.email,
|
79
|
-
:password => user.password,
|
80
|
-
:api_token => user.api_token,
|
81
|
-
}
|
82
|
-
end
|
83
|
-
{'scenarios' => scenarios}.to_json
|
74
|
+
@scenarios = SCENARIOS.map { |scen| scen.user }
|
75
|
+
render :rabl, :scenarios, :format => "json"
|
84
76
|
end
|
85
77
|
|
86
78
|
get "/api/v2/current_user" do
|
@@ -203,6 +195,14 @@ class FakeAwsm < Sinatra::Base
|
|
203
195
|
render :rabl, :deployment, :format => "json"
|
204
196
|
end
|
205
197
|
|
198
|
+
post "/api/v2/apps/:app_id/environments/:environment_id/deployments/deploy" do
|
199
|
+
app_env = @user.accounts.apps.get(params[:app_id]).app_environments.first(:environment_id => params[:environment_id])
|
200
|
+
@deployment = app_env.deployments.create(params[:deployment])
|
201
|
+
@deployment.deploy
|
202
|
+
response['Location'] = "/api/v2/apps/#{params[:app_id]}/environments/#{params[:environment_id]}/deployments/#{@deployment.id}"
|
203
|
+
render :rabl, :deployment, :format => "json"
|
204
|
+
end
|
205
|
+
|
206
206
|
get "/api/v2/apps/:app_id/environments/:environment_id/deployments/last" do
|
207
207
|
app_env = @user.accounts.apps.get(params[:app_id]).app_environments.first(:environment_id => params[:environment_id])
|
208
208
|
@deployment = app_env.deployments.last
|
@@ -3,6 +3,8 @@ require 'dm-core'
|
|
3
3
|
class Deployment
|
4
4
|
include DataMapper::Resource
|
5
5
|
|
6
|
+
AWSM_SERVERSIDE_VERSION = '2.0.0.awsm'
|
7
|
+
|
6
8
|
property :id, Serial
|
7
9
|
property :created_at, DateTime
|
8
10
|
property :finished_at, DateTime
|
@@ -29,6 +31,23 @@ class Deployment
|
|
29
31
|
"resolved-#{ref}"
|
30
32
|
end
|
31
33
|
|
34
|
+
# pretend to trigger a deploy
|
35
|
+
#
|
36
|
+
# this deploy will be instant, unlike real deploys
|
37
|
+
#
|
38
|
+
def deploy
|
39
|
+
unless serverside_version
|
40
|
+
# only set serverside version if it's not set, to imitate the api
|
41
|
+
# behavior of choosing its own serverside version if one is not
|
42
|
+
# sent
|
43
|
+
update :serverside_version => AWSM_SERVERSIDE_VERSION
|
44
|
+
end
|
45
|
+
finished!(
|
46
|
+
:successful => true,
|
47
|
+
:output => 'Deployment triggered by the API'
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
32
51
|
def finished?
|
33
52
|
finished_at != nil
|
34
53
|
end
|
@@ -56,6 +56,35 @@ describe EY::CloudClient::AppEnvironment do
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
+
describe "triggering an api deploy" do
|
60
|
+
before do
|
61
|
+
@api = scenario_cloud_client "Multiple Ambiguous Accounts"
|
62
|
+
result = EY::CloudClient::AppEnvironment.resolve(@api, 'app_name' => 'rails232app', 'environment_name' => 'giblets', 'account_name' => 'main')
|
63
|
+
result.should be_one_match
|
64
|
+
@app_env = result.matches.first
|
65
|
+
end
|
66
|
+
|
67
|
+
it "triggers a deployment (assumes that deploys happen instantly, which they don't)" do
|
68
|
+
deployment = @app_env.deploy({
|
69
|
+
:ref => 'master',
|
70
|
+
:migrate => true,
|
71
|
+
:migrate_command => 'rake migrate',
|
72
|
+
:extra_config => {'extra' => 'config'},
|
73
|
+
})
|
74
|
+
deployment.config.should == {'input_ref' => 'master', 'deployed_by' => 'Multiple Ambiguous Accounts', 'extra' => 'config'}
|
75
|
+
deployment.commit.should =~ /[0-9a-f]{40}/
|
76
|
+
deployment.resolved_ref.should_not be_nil
|
77
|
+
deployment.created_at.should_not be_nil
|
78
|
+
deployment.finished_at.should_not be_nil
|
79
|
+
deployment.should be_finished
|
80
|
+
|
81
|
+
found_dep = @app_env.last_deployment
|
82
|
+
found_dep.id.should == deployment.id
|
83
|
+
found_dep.should be_finished
|
84
|
+
found_dep.serverside_version.should == '2.0.0.awsm' # uses the awsm version if one is not sent.
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
59
88
|
describe "last deployment" do
|
60
89
|
before do
|
61
90
|
@api = scenario_cloud_client "Linked App"
|
data/spec/spec_helper.rb
CHANGED
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.8
|
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:
|
12
|
+
date: 2013-02-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rest-client
|
@@ -34,7 +34,7 @@ dependencies:
|
|
34
34
|
requirements:
|
35
35
|
- - ~>
|
36
36
|
- !ruby/object:Gem::Version
|
37
|
-
version: 1.
|
37
|
+
version: '1.6'
|
38
38
|
type: :runtime
|
39
39
|
prerelease: false
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -42,7 +42,7 @@ dependencies:
|
|
42
42
|
requirements:
|
43
43
|
- - ~>
|
44
44
|
- !ruby/object:Gem::Version
|
45
|
-
version: 1.
|
45
|
+
version: '1.6'
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
47
|
name: rspec
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -75,22 +75,6 @@ dependencies:
|
|
75
75
|
- - ! '>='
|
76
76
|
- !ruby/object:Gem::Version
|
77
77
|
version: '0'
|
78
|
-
- !ruby/object:Gem::Dependency
|
79
|
-
name: rdoc
|
80
|
-
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
|
-
requirements:
|
83
|
-
- - ! '>='
|
84
|
-
- !ruby/object:Gem::Version
|
85
|
-
version: '0'
|
86
|
-
type: :development
|
87
|
-
prerelease: false
|
88
|
-
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
|
-
requirements:
|
91
|
-
- - ! '>='
|
92
|
-
- !ruby/object:Gem::Version
|
93
|
-
version: '0'
|
94
78
|
- !ruby/object:Gem::Dependency
|
95
79
|
name: fakeweb
|
96
80
|
requirement: !ruby/object:Gem::Requirement
|
@@ -146,7 +130,7 @@ dependencies:
|
|
146
130
|
requirements:
|
147
131
|
- - ~>
|
148
132
|
- !ruby/object:Gem::Version
|
149
|
-
version: 0.
|
133
|
+
version: 1.0.1
|
150
134
|
type: :development
|
151
135
|
prerelease: false
|
152
136
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -154,7 +138,7 @@ dependencies:
|
|
154
138
|
requirements:
|
155
139
|
- - ~>
|
156
140
|
- !ruby/object:Gem::Version
|
157
|
-
version: 0.
|
141
|
+
version: 1.0.1
|
158
142
|
- !ruby/object:Gem::Dependency
|
159
143
|
name: dm-core
|
160
144
|
requirement: !ruby/object:Gem::Requirement
|
@@ -313,6 +297,8 @@ files:
|
|
313
297
|
- lib/engineyard-cloud-client/test/fake_awsm/views/keypairs.rabl
|
314
298
|
- lib/engineyard-cloud-client/test/fake_awsm/views/resolve_app_environments.rabl
|
315
299
|
- lib/engineyard-cloud-client/test/fake_awsm/views/resolve_environments.rabl
|
300
|
+
- lib/engineyard-cloud-client/test/fake_awsm/views/scenario.rabl
|
301
|
+
- lib/engineyard-cloud-client/test/fake_awsm/views/scenarios.rabl
|
316
302
|
- lib/engineyard-cloud-client/test/fake_awsm/views/user.rabl
|
317
303
|
- lib/engineyard-cloud-client/test/fake_awsm.rb
|
318
304
|
- lib/engineyard-cloud-client/test/scenario.rb
|
@@ -320,7 +306,8 @@ files:
|
|
320
306
|
- lib/engineyard-cloud-client/version.rb
|
321
307
|
- lib/engineyard-cloud-client.rb
|
322
308
|
- LICENSE
|
323
|
-
- README.
|
309
|
+
- README.md
|
310
|
+
- ChangeLog.md
|
324
311
|
- spec/engineyard-cloud-client/api_spec.rb
|
325
312
|
- spec/engineyard-cloud-client/integration/account_spec.rb
|
326
313
|
- spec/engineyard-cloud-client/integration/app_environment_spec.rb
|
@@ -351,7 +338,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
351
338
|
version: '0'
|
352
339
|
segments:
|
353
340
|
- 0
|
354
|
-
hash: -
|
341
|
+
hash: -1071401773858451620
|
355
342
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
356
343
|
none: false
|
357
344
|
requirements:
|
@@ -360,7 +347,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
360
347
|
version: '0'
|
361
348
|
segments:
|
362
349
|
- 0
|
363
|
-
hash: -
|
350
|
+
hash: -1071401773858451620
|
364
351
|
requirements: []
|
365
352
|
rubyforge_project:
|
366
353
|
rubygems_version: 1.8.24
|