engineyard-cloud-client 0.1.2

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.
Files changed (58) hide show
  1. data/LICENSE +19 -0
  2. data/README.rdoc +7 -0
  3. data/lib/engineyard-cloud-client.rb +149 -0
  4. data/lib/engineyard-cloud-client/errors.rb +38 -0
  5. data/lib/engineyard-cloud-client/model_registry.rb +21 -0
  6. data/lib/engineyard-cloud-client/models.rb +14 -0
  7. data/lib/engineyard-cloud-client/models/account.rb +38 -0
  8. data/lib/engineyard-cloud-client/models/api_struct.rb +50 -0
  9. data/lib/engineyard-cloud-client/models/app.rb +77 -0
  10. data/lib/engineyard-cloud-client/models/app_environment.rb +85 -0
  11. data/lib/engineyard-cloud-client/models/deployment.rb +105 -0
  12. data/lib/engineyard-cloud-client/models/environment.rb +240 -0
  13. data/lib/engineyard-cloud-client/models/instance.rb +15 -0
  14. data/lib/engineyard-cloud-client/models/keypair.rb +32 -0
  15. data/lib/engineyard-cloud-client/models/log.rb +11 -0
  16. data/lib/engineyard-cloud-client/models/user.rb +11 -0
  17. data/lib/engineyard-cloud-client/resolver_result.rb +19 -0
  18. data/lib/engineyard-cloud-client/rest_client_ext.rb +11 -0
  19. data/lib/engineyard-cloud-client/ruby_ext.rb +9 -0
  20. data/lib/engineyard-cloud-client/test.rb +31 -0
  21. data/lib/engineyard-cloud-client/test/fake_awsm.rb +22 -0
  22. data/lib/engineyard-cloud-client/test/fake_awsm/config.ru +207 -0
  23. data/lib/engineyard-cloud-client/test/fake_awsm/models.rb +9 -0
  24. data/lib/engineyard-cloud-client/test/fake_awsm/models/account.rb +13 -0
  25. data/lib/engineyard-cloud-client/test/fake_awsm/models/app.rb +24 -0
  26. data/lib/engineyard-cloud-client/test/fake_awsm/models/app_environment.rb +19 -0
  27. data/lib/engineyard-cloud-client/test/fake_awsm/models/deployments.rb +15 -0
  28. data/lib/engineyard-cloud-client/test/fake_awsm/models/environment.rb +25 -0
  29. data/lib/engineyard-cloud-client/test/fake_awsm/models/instance.rb +23 -0
  30. data/lib/engineyard-cloud-client/test/fake_awsm/models/user.rb +15 -0
  31. data/lib/engineyard-cloud-client/test/fake_awsm/scenarios.rb +325 -0
  32. data/lib/engineyard-cloud-client/test/fake_awsm/views/accounts.rabl +2 -0
  33. data/lib/engineyard-cloud-client/test/fake_awsm/views/apps.rabl +10 -0
  34. data/lib/engineyard-cloud-client/test/fake_awsm/views/base_app_environment.rabl +13 -0
  35. data/lib/engineyard-cloud-client/test/fake_awsm/views/base_environment.rabl +4 -0
  36. data/lib/engineyard-cloud-client/test/fake_awsm/views/environments.rabl +11 -0
  37. data/lib/engineyard-cloud-client/test/fake_awsm/views/instances.rabl +2 -0
  38. data/lib/engineyard-cloud-client/test/fake_awsm/views/resolve_app_environments.rabl +7 -0
  39. data/lib/engineyard-cloud-client/test/fake_awsm/views/resolve_environments.rabl +7 -0
  40. data/lib/engineyard-cloud-client/test/fake_awsm/views/user.rabl +2 -0
  41. data/lib/engineyard-cloud-client/test/scenario.rb +43 -0
  42. data/lib/engineyard-cloud-client/test/ui.rb +33 -0
  43. data/lib/engineyard-cloud-client/version.rb +7 -0
  44. data/spec/engineyard-cloud-client/api_spec.rb +59 -0
  45. data/spec/engineyard-cloud-client/integration/account_spec.rb +18 -0
  46. data/spec/engineyard-cloud-client/integration/app_environment_spec.rb +38 -0
  47. data/spec/engineyard-cloud-client/integration/app_spec.rb +20 -0
  48. data/spec/engineyard-cloud-client/integration/environment_spec.rb +57 -0
  49. data/spec/engineyard-cloud-client/integration/user_spec.rb +18 -0
  50. data/spec/engineyard-cloud-client/models/api_struct_spec.rb +41 -0
  51. data/spec/engineyard-cloud-client/models/app_spec.rb +64 -0
  52. data/spec/engineyard-cloud-client/models/environment_spec.rb +300 -0
  53. data/spec/engineyard-cloud-client/models/instance_spec.rb +44 -0
  54. data/spec/engineyard-cloud-client/models/keypair_spec.rb +58 -0
  55. data/spec/spec_helper.rb +50 -0
  56. data/spec/support/helpers.rb +16 -0
  57. data/spec/support/matchers.rb +2 -0
  58. metadata +377 -0
@@ -0,0 +1,300 @@
1
+ require 'spec_helper'
2
+
3
+ describe EY::CloudClient::Environment do
4
+
5
+ describe ".all" do
6
+ it "hits the index action in the API" do
7
+ response = {
8
+ "environments" => [
9
+ {"apps"=>
10
+ [{"name"=>"myapp",
11
+ "repository_uri"=>"git@github.com:myaccount/myapp.git",
12
+ "account"=>{"name"=>"myaccount", "id"=>1234},
13
+ "id"=>12345}],
14
+ "name"=>"myapp_production",
15
+ "deployment_configurations"=>
16
+ {"myapp"=>
17
+ {"name"=>"myapp",
18
+ "uri"=>nil,
19
+ "migrate"=>{"command"=>"rake db:migrate", "perform"=>true},
20
+ "repository_uri"=>"git@github.com:myaccount/myapp.git",
21
+ "id"=>12345,
22
+ "domain_name"=>"_"}},
23
+ "instances"=>[],
24
+ "app_master"=>nil,
25
+ "framework_env"=>"production",
26
+ "stack_name"=>"nginx_thin",
27
+ "account"=>{"name"=>"myaccount", "id"=>1234},
28
+ "app_server_stack_name"=>"nginx_thin",
29
+ "ssh_username"=>"deploy",
30
+ "load_balancer_ip_address"=>nil,
31
+ "instances_count"=>0,
32
+ "id"=>30573,
33
+ "instance_status"=>"none"
34
+ }
35
+ ]
36
+ }
37
+
38
+ FakeWeb.register_uri(:get, "https://cloud.engineyard.com/api/v2/environments",
39
+ :body => response.to_json, :content_type => "application/json")
40
+
41
+ environments = EY::CloudClient::Environment.all(cloud_client)
42
+
43
+ environments.length.should == 1
44
+ environments.first.name.should == "myapp_production"
45
+ end
46
+ end
47
+
48
+ describe ".create" do
49
+ it "hits the create action in the API without any cluster configuration (0 instances booted)" do
50
+ account = EY::CloudClient::Account.new(cloud_client, {:id => 1234, :name => 'myaccount'})
51
+ app = EY::CloudClient::App.new(cloud_client, {:account => account, :id => 12345, :name => 'myapp',
52
+ :repository_uri => 'git@github.com:myaccount/myapp.git', :app_type_id => 'rails3'})
53
+
54
+ response = {
55
+ "environment"=>
56
+ {"apps"=>
57
+ [{"name"=>"myapp",
58
+ "repository_uri"=>"git@github.com:myaccount/myapp.git",
59
+ "account"=>{"name"=>"myaccount", "id"=>1234},
60
+ "id"=>12345}],
61
+ "name"=>"myapp_production",
62
+ "deployment_configurations"=>
63
+ {"myapp"=>
64
+ {"name"=>"myapp",
65
+ "uri"=>nil,
66
+ "migrate"=>{"command"=>"rake db:migrate", "perform"=>true},
67
+ "repository_uri"=>"git@github.com:myaccount/myapp.git",
68
+ "id"=>12345,
69
+ "domain_name"=>"_"}},
70
+ "instances"=>[],
71
+ "app_master"=>nil,
72
+ "framework_env"=>"production",
73
+ "stack_name"=>"nginx_thin",
74
+ "account"=>{"name"=>"myaccount", "id"=>1234},
75
+ "app_server_stack_name"=>"nginx_thin",
76
+ "ssh_username"=>"deploy",
77
+ "load_balancer_ip_address"=>nil,
78
+ "instances_count"=>0,
79
+ "id"=>30573,
80
+ "instance_status"=>"none"}}
81
+
82
+ FakeWeb.register_uri(:post, "https://cloud.engineyard.com/api/v2/apps/12345/environments",
83
+ :body => response.to_json, :content_type => "application/json")
84
+
85
+ env = EY::CloudClient::Environment.create(cloud_client, {
86
+ "app" => app,
87
+ "name" => 'myapp_production',
88
+ "app_server_stack_name" => 'nginx_thin',
89
+ "region" => 'us-west-1'
90
+ })
91
+ FakeWeb.should have_requested(:post, "https://cloud.engineyard.com/api/v2/apps/12345/environments")
92
+
93
+ env.name.should == "myapp_production"
94
+ env.account.name.should == "myaccount"
95
+ env.apps.to_a.first.name.should == "myapp"
96
+ end
97
+
98
+ it "hits the create action and requests a solo instance booted" do
99
+ account = EY::CloudClient::Account.from_hash(cloud_client, {:id => 1234, :name => 'myaccount'})
100
+ app = EY::CloudClient::App.from_hash(cloud_client, {:account => account, :id => 12345, :name => 'myapp',
101
+ :repository_uri => 'git@github.com:myaccount/myapp.git', :app_type_id => 'rails3'})
102
+
103
+ response = {
104
+ "environment"=>
105
+ {"apps"=>
106
+ [{"name"=>"myapp",
107
+ "repository_uri"=>"git@github.com:myaccount/myapp.git",
108
+ "account"=>{"name"=>"myaccount", "id"=>1234},
109
+ "id"=>12345}],
110
+ "name"=>"myapp_production",
111
+ "deployment_configurations"=>
112
+ {"myapp"=>
113
+ {"name"=>"myapp",
114
+ "uri"=>nil,
115
+ "migrate"=>{"command"=>"rake db:migrate", "perform"=>true},
116
+ "repository_uri"=>"git@github.com:myaccount/myapp.git",
117
+ "id"=>12345,
118
+ "domain_name"=>"_"}},
119
+ "instances"=>
120
+ [{"public_hostname"=>nil,
121
+ "name"=>nil,
122
+ "amazon_id"=>nil,
123
+ "role"=>"solo",
124
+ "bridge"=>true,
125
+ "id"=>135930,
126
+ "status"=>"starting"}],
127
+ "app_master"=>
128
+ {"public_hostname"=>nil,
129
+ "name"=>nil,
130
+ "amazon_id"=>nil,
131
+ "role"=>"solo",
132
+ "bridge"=>true,
133
+ "id"=>135930,
134
+ "status"=>"starting"},
135
+ "framework_env"=>"production",
136
+ "stack_name"=>"nginx_thin",
137
+ "account"=>{"name"=>"myaccount", "id"=>1234},
138
+ "app_server_stack_name"=>"nginx_thin",
139
+ "ssh_username"=>"deploy",
140
+ "load_balancer_ip_address"=>"50.18.248.18",
141
+ "instances_count"=>1,
142
+ "id"=>30573,
143
+ "instance_status"=>"starting"}}
144
+
145
+ FakeWeb.register_uri(:post, "https://cloud.engineyard.com/api/v2/apps/12345/environments",
146
+ :body => response.to_json, :content_type => "application/json")
147
+
148
+ env = EY::CloudClient::Environment.create(cloud_client, {
149
+ "app" => app,
150
+ "name" => "myapp_production",
151
+ "app_server_stack_name" => "nginx_thin",
152
+ "region" => "us-west-1",
153
+ "cluster_configuration" => {
154
+ "configuration" => "solo"
155
+ }
156
+ })
157
+ FakeWeb.should have_requested(:post, "https://cloud.engineyard.com/api/v2/apps/12345/environments")
158
+
159
+ env.name.should == "myapp_production"
160
+ env.instances.count.should == 1
161
+ env.bridge.role.should == "solo"
162
+ end
163
+ end
164
+
165
+ describe "#destroy" do
166
+ it "hits the destroy action in the API" do
167
+ pending
168
+ end
169
+ end
170
+
171
+ describe "#rebuild" do
172
+ it "hits the rebuild action in the API" do
173
+ env = EY::CloudClient::Environment.from_hash(cloud_client, { "id" => 46534 })
174
+
175
+ FakeWeb.register_uri(
176
+ :put,
177
+ "https://cloud.engineyard.com/api/v2/environments/#{env.id}/update_instances",
178
+ :body => ''
179
+ )
180
+
181
+ env.rebuild
182
+
183
+ FakeWeb.should have_requested(:put, "https://cloud.engineyard.com/api/v2/environments/#{env.id}/update_instances")
184
+ end
185
+ end
186
+
187
+ describe "#run_custom_recipes" do
188
+ it "hits the rebuild action in the API" do
189
+ env = EY::CloudClient::Environment.from_hash(cloud_client, { "id" => 46534 })
190
+
191
+ FakeWeb.register_uri(
192
+ :put,
193
+ "https://cloud.engineyard.com/api/v2/environments/#{env.id}/run_custom_recipes",
194
+ :body => '',
195
+ :content_type => 'application/json'
196
+ )
197
+
198
+ env.run_custom_recipes
199
+
200
+ FakeWeb.should have_requested(:put, "https://cloud.engineyard.com/api/v2/environments/#{env.id}/run_custom_recipes")
201
+ end
202
+ end
203
+
204
+ describe "#instances" do
205
+ it "returns instances" do
206
+ instance_data = {
207
+ "id" => "1",
208
+ "role" => "app_master",
209
+ "bridge" => true,
210
+ "amazon_id" => "i-likebeer",
211
+ "public_hostname" => "banana_master"
212
+ }
213
+
214
+ env = EY::CloudClient::Environment.from_hash(cloud_client, {
215
+ "id" => 10291,
216
+ "instances" => [instance_data],
217
+ })
218
+
219
+ FakeWeb.register_uri(:get,
220
+ "https://cloud.engineyard.com/api/v2/environments/#{env.id}/instances",
221
+ :body => {"instances" => [instance_data]}.to_json,
222
+ :content_type => 'application/json'
223
+ )
224
+
225
+ env.should have(1).instances
226
+ env.instances.first.should == EY::CloudClient::Instance.from_hash(cloud_client, instance_data.merge('environment' => env))
227
+ end
228
+ end
229
+
230
+ describe "#bridge!" do
231
+ def make_env_with_bridge(bridge)
232
+ if bridge
233
+ bridge = {
234
+ "id" => 44206,
235
+ "role" => "solo",
236
+ "bridge" => true,
237
+ }.merge(bridge)
238
+ end
239
+
240
+ EY::CloudClient::Environment.from_hash(cloud_client, {
241
+ "id" => 11830,
242
+ "name" => "guinea-pigs-are-delicious",
243
+ "app_master" => bridge,
244
+ "instances" => [bridge].compact,
245
+ })
246
+ end
247
+
248
+
249
+ it "returns the bridge if it's present and running" do
250
+ env = make_env_with_bridge("status" => "running")
251
+ env.bridge!.should_not be_nil
252
+ env.bridge!.id.should == 44206
253
+ end
254
+
255
+ it "raises an error if the bridge is in a non-running state" do
256
+ env = make_env_with_bridge("status" => "error")
257
+ lambda {
258
+ env.bridge!
259
+ }.should raise_error(EY::CloudClient::BadBridgeStatusError)
260
+ end
261
+
262
+ it "returns the bridge if told to ignore the bridge being in a non-running state" do
263
+ env = make_env_with_bridge("status" => "error")
264
+ env.ignore_bad_bridge = true
265
+ env.bridge!.should_not be_nil
266
+ env.bridge!.id.should == 44206
267
+ end
268
+
269
+ it "raises an error if the bridge has fallen down" do
270
+ env = make_env_with_bridge(nil)
271
+ lambda {
272
+ env.bridge!
273
+ }.should raise_error(EY::CloudClient::NoBridgeError)
274
+ end
275
+ end
276
+
277
+ describe "#shorten_name_for(app)" do
278
+ def short(environment_name, app_name)
279
+ env = EY::CloudClient::Environment.from_hash(cloud_client, {'name' => environment_name})
280
+ app = EY::CloudClient::App.from_hash(cloud_client, {'name' => app_name})
281
+ env.shorten_name_for(app)
282
+ end
283
+
284
+ it "turns myapp+myapp_production to production" do
285
+ short('myapp_production', 'myapp').should == 'production'
286
+ end
287
+
288
+ it "turns product+production to production (leaves it alone)" do
289
+ short('production', 'product').should == 'production'
290
+ end
291
+
292
+ it "leaves the environment name alone when the app name appears in the middle" do
293
+ short('hattery', 'ate').should == 'hattery'
294
+ end
295
+
296
+ it "does not produce an empty string when the names are the same" do
297
+ short('dev', 'dev').should == 'dev'
298
+ end
299
+ end
300
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe EY::CloudClient::Instance do
4
+
5
+ describe "#bridge?" do
6
+ it "is true when bridge is true" do
7
+ EY::CloudClient::Instance.from_hash(cloud_client, "bridge" => true, "role" => "app").should be_bridge
8
+ end
9
+
10
+ it "is false when bridge is false" do
11
+ EY::CloudClient::Instance.from_hash(cloud_client, "bridge" => false, "role" => "solo").should_not be_bridge
12
+ end
13
+
14
+ it "is false when not set" do
15
+ EY::CloudClient::Instance.from_hash(cloud_client, "role" => "app_master").should_not be_bridge
16
+ end
17
+ end
18
+
19
+ describe "#has_app_code" do
20
+ it "is true for solos" do
21
+ EY::CloudClient::Instance.from_hash(cloud_client, "role" => "solo").should have_app_code
22
+ end
23
+
24
+ it "is true for app masters" do
25
+ EY::CloudClient::Instance.from_hash(cloud_client, "role" => "app_master").should have_app_code
26
+ end
27
+
28
+ it "is true for app slaves" do
29
+ EY::CloudClient::Instance.from_hash(cloud_client, "role" => "app").should have_app_code
30
+ end
31
+
32
+ it "is true for utilities" do
33
+ EY::CloudClient::Instance.from_hash(cloud_client, "role" => "util").should have_app_code
34
+ end
35
+
36
+ it "is false for DB masters" do
37
+ EY::CloudClient::Instance.from_hash(cloud_client, "role" => "db_master").should_not have_app_code
38
+ end
39
+
40
+ it "is false for DB slaves" do
41
+ EY::CloudClient::Instance.from_hash(cloud_client, "role" => "db_slave").should_not have_app_code
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe EY::CloudClient::Keypair do
4
+ describe ".all" do
5
+ it "hits the index action in the API" do
6
+ response = {
7
+ "keypairs" => [
8
+ {
9
+ "fingerprint" => "11:f4:11:11:47:db:b7:3f:06:a9:XX:XX:XX:XX:XX:XX",
10
+ "id" => 123,
11
+ "name" => "macbook pro",
12
+ "public_key" => "ssh-dss MYKEYPAIR"
13
+ }
14
+ ]
15
+ }
16
+
17
+ FakeWeb.register_uri(:get, "https://cloud.engineyard.com/api/v2/keypairs",
18
+ :body => response.to_json, :content_type => "application/json")
19
+
20
+ keypairs = EY::CloudClient::Keypair.all(cloud_client)
21
+
22
+ keypairs.length.should == 1
23
+ keypairs.first.name.should == "macbook pro"
24
+ end
25
+ end
26
+
27
+ describe ".create" do
28
+ it "hits the create action in the API" do
29
+ response = {
30
+ "keypair" => {
31
+ "fingerprint" => "ce:90:99:b8:62:74:66:2f:ec:bc:XX:XX:XX:XX:XX:XX",
32
+ "id" => 234,
33
+ "name" => "laptop",
34
+ "public_key" => "ssh-rsa OTHERKEYPAIR"
35
+ }
36
+ }
37
+
38
+ FakeWeb.register_uri(:post, "https://cloud.engineyard.com/api/v2/keypairs",
39
+ :body => response.to_json, :content_type => "application/json")
40
+
41
+ keypair = EY::CloudClient::Keypair.create(cloud_client, {
42
+ "name" => 'laptop',
43
+ "public_key" => "ssh-rsa OTHERKEYPAIR"
44
+ })
45
+
46
+ FakeWeb.should have_requested(:post, "https://cloud.engineyard.com/api/v2/keypairs")
47
+
48
+ keypair.name.should == "laptop"
49
+ keypair.public_key.should == "ssh-rsa OTHERKEYPAIR"
50
+ end
51
+ end
52
+
53
+ describe "#destroy" do
54
+ it "hits the destroy action in the API" do
55
+ pending
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,50 @@
1
+ if self.class.const_defined?(:EY_ROOT)
2
+ raise "don't require the spec helper twice!"
3
+ end
4
+
5
+ if ENV['COVERAGE']
6
+ require 'simplecov'
7
+ SimpleCov.start
8
+ end
9
+
10
+ EY_ROOT = File.expand_path("../..", __FILE__)
11
+ require 'rubygems'
12
+ require 'bundler/setup'
13
+
14
+ # Bundled gems
15
+ require 'fakeweb'
16
+ require 'fakeweb_matcher'
17
+
18
+ require 'json'
19
+
20
+ # Engineyard gem
21
+ $LOAD_PATH.unshift(File.join(EY_ROOT, "lib"))
22
+ require 'engineyard-cloud-client'
23
+ require 'engineyard-cloud-client/test'
24
+
25
+ # Spec stuff
26
+ require 'rspec'
27
+ require 'tmpdir'
28
+ require 'yaml'
29
+ require 'pp'
30
+ support = Dir[File.join(EY_ROOT,'/spec/support/*.rb')]
31
+ support.each{|helper| require helper }
32
+
33
+ #support = Dir[File.join(EY_ROOT,'/spec/support/fake_awsm/*.rb')]
34
+ #support.each{|helper| require helper }
35
+
36
+ RSpec.configure do |config|
37
+ config.treat_symbols_as_metadata_keys_with_true_values = true
38
+ config.run_all_when_everything_filtered = true
39
+ config.filter_run :focus
40
+
41
+ config.include SpecHelpers
42
+
43
+ config.before(:all) do
44
+ FakeWeb.allow_net_connect = false
45
+ end
46
+
47
+ config.before(:each) do
48
+ EY::CloudClient.default_endpoint!
49
+ end
50
+ end