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,2 @@
1
+ collection @accounts, :root => :accounts, :object_root => false
2
+ attributes :id, :name
@@ -0,0 +1,10 @@
1
+ collection @apps, :root => :apps, :object_root => false
2
+ attributes :id, :name, :repository_uri, :app_type_id
3
+ child :account do
4
+ attributes :id, :name
5
+ end
6
+ node :environments do |m|
7
+ m.environments.map do |env|
8
+ partial('base_environment', :object => env, :root => nil)
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ attributes :id, :domain_name, :uri
2
+ child :environment do
3
+ attributes :id, :ssh_username, :name, :instances_count, :app_server_stack_name, :load_balancer_ip_address, :framework_env
4
+ child :account do
5
+ attributes :id, :name
6
+ end
7
+ end
8
+ child :app do
9
+ attributes :id, :name, :repository_uri, :app_type_id
10
+ child :account do
11
+ attributes :id, :name
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ attributes :id, :ssh_username, :name, :instances_count, :app_server_stack_name, :load_balancer_ip_address, :framework_env
2
+ child :account do
3
+ attributes :id, :name
4
+ end
@@ -0,0 +1,11 @@
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
3
+ child :account do
4
+ attributes :id, :name
5
+ end
6
+ child :apps do
7
+ attributes :id, :name, :repository_uri, :app_type_id
8
+ child :account do
9
+ attributes :id, :name
10
+ end
11
+ end
@@ -0,0 +1,2 @@
1
+ collection @instances, :root => :instances, :object_root => false
2
+ attributes :id, :status, :amazon_id, :role, :bridge, :name, :public_hostname
@@ -0,0 +1,7 @@
1
+ object @resolver => :resolver
2
+ node :matches do
3
+ @resolver.matches.map do |match|
4
+ partial('base_app_environment', :object => match, :root => nil)
5
+ end
6
+ end
7
+ attributes :errors, :suggestions
@@ -0,0 +1,7 @@
1
+ object @resolver => :resolver
2
+ node :matches do |resolver|
3
+ resolver.matches.map do |match|
4
+ partial('base_environment', :object => match, :root => nil)
5
+ end
6
+ end
7
+ attributes :errors, :suggestions
@@ -0,0 +1,2 @@
1
+ object @user => :user
2
+ attributes :id, :name, :email
@@ -0,0 +1,43 @@
1
+ require 'multi_json'
2
+ require 'engineyard-cloud-client/rest_client_ext'
3
+ require 'engineyard-cloud-client/test'
4
+ require 'engineyard-cloud-client/test/fake_awsm'
5
+ require 'engineyard-cloud-client/test/ui'
6
+
7
+ module EY::CloudClient::Test
8
+ class Scenario
9
+ def self.[](name)
10
+ scenarios[name] or raise "Scenario #{name.inspect} not found in:\n\t#{scenarios.keys.join("\n\t")}"
11
+ end
12
+
13
+ def self.scenarios
14
+ @scenarios ||= load_scenarios
15
+ end
16
+
17
+ def self.load_scenarios
18
+ response = ::RestClient.get(EY::CloudClient::Test::FakeAwsm.uri.sub(/\/?$/,'/scenarios'))
19
+ data = MultiJson.decode(response)
20
+ data['scenarios'].inject({}) do |hsh, scenario|
21
+ hsh[scenario['name']] = new(scenario)
22
+ hsh
23
+ end
24
+ end
25
+
26
+ attr_reader :email, :password, :api_token
27
+
28
+ def initialize(options)
29
+ @name = options['name']
30
+ @email = options['email']
31
+ @password = options['password']
32
+ @api_token = options['api_token']
33
+ end
34
+
35
+ def cloud_client(ui = EY::CloudClient::Test::UI.new)
36
+ EY::CloudClient.new(@api_token, ui)
37
+ end
38
+
39
+ def inspect
40
+ "#<Test::Scenario name:#@name>"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,33 @@
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
@@ -0,0 +1,7 @@
1
+ # This file is maintained by a herd of rabid monkeys with Rakes.
2
+ module EY
3
+ class CloudClient
4
+ VERSION = '0.1.2'
5
+ end
6
+ end
7
+ # Please be aware that the monkeys like tho throw poo sometimes.
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ describe EY::CloudClient do
4
+ it "holds an api token" do
5
+ EY::CloudClient.new('asdf', test_ui).token.should == "asdf"
6
+ end
7
+
8
+ it "holds a UI" do
9
+ EY::CloudClient.new('asdf', test_ui).ui.should == test_ui
10
+ end
11
+
12
+ describe ".endpoint" do
13
+ after do
14
+ EY::CloudClient.default_endpoint!
15
+ end
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
36
+ end
37
+
38
+ it "authenticates with valid credentials and returns the api token" do
39
+ FakeWeb.register_uri(:post, "https://cloud.engineyard.com/api/v2/authenticate", :body => %|{"api_token": "asdf"}|, :content_type => 'application/json')
40
+
41
+ EY::CloudClient.authenticate("a@b.com", "foo", test_ui).should == "asdf"
42
+ end
43
+
44
+ it "raises InvalidCredentials when the credentials are invalid" do
45
+ FakeWeb.register_uri(:post, "https://cloud.engineyard.com/api/v2/authenticate", :status => 401, :content_type => 'application/json')
46
+
47
+ lambda {
48
+ EY::CloudClient.authenticate("a@b.com", "foo", test_ui)
49
+ }.should raise_error(EY::CloudClient::InvalidCredentials)
50
+ end
51
+
52
+ it "raises RequestFailed with a friendly error when cloud is under maintenance" do
53
+ FakeWeb.register_uri(:post, "https://cloud.engineyard.com/api/v2/authenticate", :status => 502, :content_type => 'text/html')
54
+
55
+ lambda {
56
+ EY::CloudClient.authenticate("a@b.com", "foo", test_ui)
57
+ }.should raise_error(EY::CloudClient::RequestFailed, /API is temporarily unavailable/)
58
+ end
59
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe EY::CloudClient::Account do
4
+ before do
5
+ FakeWeb.allow_net_connect = true
6
+ EY::CloudClient.endpoint = EY::CloudClient::Test::FakeAwsm.uri
7
+ end
8
+
9
+ describe ".all" do
10
+ it "returns all accounts" do
11
+ api = scenario_cloud_client "Multiple Ambiguous Accounts"
12
+ accounts = EY::CloudClient::Account.all(api)
13
+ accounts.should have(2).account
14
+ accounts.find { |account| account.name == 'main' }.should be
15
+ accounts.find { |account| account.name == 'account_2' }.should be
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe EY::CloudClient::AppEnvironment do
4
+ before(:each) do
5
+ FakeWeb.allow_net_connect = true
6
+ EY::CloudClient.endpoint = EY::CloudClient::Test::FakeAwsm.uri
7
+ end
8
+
9
+ describe ".resolve" do
10
+ it "finds an environment" do
11
+ api = scenario_cloud_client "Multiple Ambiguous Accounts"
12
+ result = EY::CloudClient::AppEnvironment.resolve(api, 'app_name' => 'rails232app', 'environment_name' => 'giblets', 'account_name' => 'main')
13
+ result.should be_one_match
14
+ end
15
+
16
+ it "returns multiple matches with ambiguous query" do
17
+ api = scenario_cloud_client "Multiple Ambiguous Accounts"
18
+ result = EY::CloudClient::AppEnvironment.resolve(api, 'app_name' => 'rails232app', 'environment_name' => 'giblets')
19
+ result.should be_many_matches
20
+ end
21
+
22
+ it "parses errors when there are no matches" do
23
+ api = scenario_cloud_client "Multiple Ambiguous Accounts"
24
+ result = EY::CloudClient::AppEnvironment.resolve(api, 'app_name' => 'rails232app', 'environment_name' => 'notfound')
25
+ result.should be_no_matches
26
+ result.errors.should_not be_empty
27
+ end
28
+
29
+ it "parses errors and suggestions when there are ambiguous matches" do
30
+ api = scenario_cloud_client "Unlinked App"
31
+ result = EY::CloudClient::AppEnvironment.resolve(api, 'app_name' => 'rails232app', 'environment_name' => 'giblets')
32
+ result.matches.should be_empty
33
+ result.errors.should_not be_empty
34
+ result.suggestions.should_not be_empty
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe EY::CloudClient::App do
4
+ before(:each) do
5
+ FakeWeb.allow_net_connect = true
6
+ EY::CloudClient.endpoint = EY::CloudClient::Test::FakeAwsm.uri
7
+ end
8
+
9
+ describe ".all" do
10
+ it "finds all the apps" do
11
+ api = scenario_cloud_client "One App Many Envs"
12
+ apps = EY::CloudClient::App.all(api)
13
+ apps.size.should == 1
14
+ app = apps.first
15
+ app.environments.size.should == 2
16
+ app.environments.map(&:name).should =~ %w[giblets bakon]
17
+ end
18
+ end
19
+
20
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe EY::CloudClient::Environment do
4
+ before(:each) do
5
+ FakeWeb.allow_net_connect = true
6
+ EY::CloudClient.endpoint = EY::CloudClient::Test::FakeAwsm.uri
7
+ end
8
+
9
+ describe ".all" do
10
+ it "finds all the environments" do
11
+ api = scenario_cloud_client "One App Many Envs"
12
+ envs = EY::CloudClient::Environment.all(api)
13
+ envs.size.should == 3
14
+ envs.map(&:name).should =~ %w[giblets bakon beef]
15
+ end
16
+ end
17
+
18
+ describe ".resolve" do
19
+ it "finds an environment" do
20
+ api = scenario_cloud_client "Multiple Ambiguous Accounts"
21
+ result = EY::CloudClient::Environment.resolve(api, 'environment_name' => 'giblets', 'account_name' => 'main' )
22
+ result.should be_one_match
23
+ end
24
+
25
+ it "returns multiple matches with ambiguous query" do
26
+ api = scenario_cloud_client "Multiple Ambiguous Accounts"
27
+ result = EY::CloudClient::Environment.resolve(api, 'environment_name' => 'giblets' )
28
+ result.should be_many_matches
29
+ end
30
+
31
+ it "parses errors when there are no matches" do
32
+ api = scenario_cloud_client "Multiple Ambiguous Accounts"
33
+ result = EY::CloudClient::Environment.resolve(api, 'environment_name' => 'notfound' )
34
+ result.should be_no_matches
35
+ result.errors.should_not be_empty
36
+ end
37
+
38
+ it "parses errors and suggestions when there are ambiguous matches" do
39
+ api = scenario_cloud_client "Unlinked App"
40
+ result = EY::CloudClient::Environment.resolve(api, 'app_name' => 'rails232app', 'environment_name' => 'giblets' )
41
+ result.should be_no_matches
42
+ result.errors.should_not be_empty
43
+ result.suggestions.should_not be_empty
44
+ end
45
+ end
46
+
47
+ describe "loading instances" do
48
+ it "requests instances" do
49
+ api = scenario_cloud_client "Linked App"
50
+ result = EY::CloudClient::Environment.resolve(api, 'account_name' => 'main', 'app_name' => 'rails232app', 'environment_name' => 'giblets')
51
+ env = result.matches.first
52
+ env.bridge.role.should == 'app_master'
53
+ env.instances.size.should == env.instances_count
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe EY::CloudClient::User do
4
+ before do
5
+ FakeWeb.allow_net_connect = true
6
+ EY::CloudClient.endpoint = EY::CloudClient::Test::FakeAwsm.uri
7
+ end
8
+
9
+ describe ".all" do
10
+ it "returns all accounts" do
11
+ api = scenario_cloud_client "User Name"
12
+ user = api.current_user
13
+ user.name.should == 'User Name'
14
+ user.accounts.size.should == 1
15
+ user.accounts.first.name.should == 'main'
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe EY::CloudClient::ApiStruct do
4
+ class Foo < EY::CloudClient::ApiStruct.new(:fruit, :veggie); end
5
+
6
+ it "acts like a normal struct" do
7
+ f = Foo.new(cloud_client, "fruit" => "banana")
8
+
9
+ f.fruit.should == "banana"
10
+ end
11
+
12
+ describe "from_hash initializer" do
13
+ it "assigns values from string keys" do
14
+ f = Foo.from_hash(cloud_client, "fruit" => "banana")
15
+ f.should == Foo.new(cloud_client, "fruit" => "banana")
16
+ end
17
+
18
+ it "assigns values from symbol keys" do
19
+ f = Foo.from_hash(cloud_client, :fruit => "banana")
20
+ f.should == Foo.new(cloud_client, "fruit" => "banana")
21
+ end
22
+ end
23
+
24
+ describe "from_array initializer" do
25
+ it "provides a from_array initializer" do
26
+ f = Foo.from_array(cloud_client, [:fruit => "banana"])
27
+ f.should == [Foo.new(cloud_client, "fruit" => "banana")]
28
+ end
29
+
30
+ it "handles a common-arguments hash as the second argument" do
31
+ foos = Foo.from_array(cloud_client,
32
+ [{:fruit => "banana"}, {:fruit => 'apple'}],
33
+ :veggie => 'kale')
34
+ foos.should == [
35
+ Foo.new(cloud_client, "fruit" => "banana", "veggie" => "kale"),
36
+ Foo.new(cloud_client, "fruit" => "apple", "veggie" => "kale"),
37
+ ]
38
+ end
39
+ end
40
+
41
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ describe EY::CloudClient::App do
4
+ describe ".all" do
5
+ it "hits the index action in the API" do
6
+ response = {
7
+ "apps" => [
8
+ {
9
+ "environments"=>[],
10
+ "name"=>"myapp",
11
+ "repository_uri"=>"git@github.com:myaccount/myapp.git",
12
+ "account"=>{"name"=>"myaccount", "id"=>1234},
13
+ "id"=>12345
14
+ }
15
+ ]
16
+ }
17
+
18
+ FakeWeb.register_uri(:get, "https://cloud.engineyard.com/api/v2/apps",
19
+ :body => response.to_json, :content_type => "application/json")
20
+
21
+ apps = EY::CloudClient::App.all(cloud_client)
22
+
23
+ apps.length.should == 1
24
+ apps.first.name.should == "myapp"
25
+ end
26
+ end
27
+
28
+ describe ".create" do
29
+ it "hits the create app action in the API" do
30
+ account = EY::CloudClient::Account.new(cloud_client, {:id => 1234, :name => 'myaccount'})
31
+
32
+ response = {
33
+ "app"=>{
34
+ "environments"=>[],
35
+ "name"=>"myapp",
36
+ "repository_uri"=>"git@github.com:myaccount/myapp.git",
37
+ "account"=>{"name"=>"myaccount", "id"=>1234},
38
+ "id"=>12345
39
+ }
40
+ }
41
+
42
+ FakeWeb.register_uri(:post, "https://cloud.engineyard.com/api/v2/accounts/1234/apps",
43
+ :body => response.to_json, :content_type => "application/json")
44
+
45
+ app = EY::CloudClient::App.create(cloud_client, {
46
+ "account" => account,
47
+ "name" => 'myapp',
48
+ "repository_uri" => 'git@github.com:myaccount/myapp.git',
49
+ "app_type_id" => 'rails3'
50
+ })
51
+
52
+ FakeWeb.should have_requested(:post, "https://cloud.engineyard.com/api/v2/accounts/1234/apps")
53
+
54
+ app.name.should == "myapp"
55
+ app.account.name.should == "myaccount"
56
+ end
57
+ end
58
+
59
+ describe "#destroy" do
60
+ it "hits the destroy action in the API" do
61
+ pending
62
+ end
63
+ end
64
+ end