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 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