engineyard-cloud-client 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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