engineyard-cloud-client 1.0.7 → 1.0.8

Sign up to get free protection for your applications and to get access to all the features.
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
+
@@ -1,6 +1,29 @@
1
- = Engine Yard Cloud Client
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
- == Debugging:
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
- instances.select { |inst| inst.has_app_code? }
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.status != "running"
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 'json'
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
- new_scenario = SCENARIOS.detect { |scen| scen.user.name == params[:scenario] }
59
- unless new_scenario
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?"}.to_json
67
+ return json({"ok" => "false", "message" => "wtf is the #{params[:scenario]} scenario?"})
62
68
  end
63
- user = new_scenario.user
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 do |scen|
75
- user = scen.user
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
@@ -0,0 +1,2 @@
1
+ object @scenario => :scenario
2
+ attributes :name, :email, :password, :api_token
@@ -0,0 +1,2 @@
1
+ collection @scenarios, :root => :scenarios, :object_root => false
2
+ attributes :name, :email, :password, :api_token
@@ -1,7 +1,7 @@
1
1
  # This file is maintained by a herd of rabid monkeys with Rakes.
2
2
  module EY
3
3
  class CloudClient
4
- VERSION = '1.0.7'
4
+ VERSION = '1.0.8'
5
5
  end
6
6
  end
7
7
  # Please be aware that the monkeys like tho throw poo sometimes.
@@ -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
@@ -15,7 +15,7 @@ require 'bundler/setup'
15
15
  require 'fakeweb'
16
16
  require 'fakeweb_matcher'
17
17
 
18
- require 'json'
18
+ require 'multi_json'
19
19
 
20
20
  # Engineyard gem
21
21
  $LOAD_PATH.unshift(File.join(EY_ROOT, "lib"))
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.7
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: 2012-10-25 00:00:00.000000000 Z
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.3.0
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.3.0
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.2.2
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.2.2
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.rdoc
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: -3497272709251376043
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: -3497272709251376043
350
+ hash: -1071401773858451620
364
351
  requirements: []
365
352
  rubyforge_project:
366
353
  rubygems_version: 1.8.24