addons-client 0.0.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.
- data/.gitignore +18 -0
- data/.travis.yml +8 -0
- data/Gemfile +11 -0
- data/LICENSE +22 -0
- data/README.md +133 -0
- data/Rakefile +11 -0
- data/addons-client.gemspec +22 -0
- data/bin/addons-client +18 -0
- data/lib/addons-client/cli.rb +56 -0
- data/lib/addons-client/client.rb +89 -0
- data/lib/addons-client/mock.rb +44 -0
- data/lib/addons-client/response.rb +29 -0
- data/lib/addons-client/settings.rb +0 -0
- data/lib/addons-client/version.rb +5 -0
- data/lib/addons-client.rb +17 -0
- data/test/client_test.rb +33 -0
- data/test/deprovision_test.rb +27 -0
- data/test/plan_change_test.rb +53 -0
- data/test/provision_test.rb +69 -0
- data/test/response_test.rb +55 -0
- data/test/test_helper.rb +32 -0
- metadata +132 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Chris Continanza
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
# Addons::Client
|
2
|
+
|
3
|
+
[](http://travis-ci.org/heroku/addons-client)
|
4
|
+
|
5
|
+
## codename: Kaikei
|
6
|
+
|
7
|
+
The addons client is a Ruby library that creates the RESTful requests that are used to interact with the Add-on Platform API.
|
8
|
+
|
9
|
+
The Platform API provides 3 main functions for Add-ons: provisioning, deprovisioning, and plan change.
|
10
|
+
Historically, the heroku module "core" was responsible for sending the appropriate messages to
|
11
|
+
add-on providers and reacting correctly according to those responses. Core will still have to react
|
12
|
+
to api errors, but will no longer send messages to providers.
|
13
|
+
|
14
|
+
The Addons Client represents the first attempt at having a well-defined interface between a Platform and these
|
15
|
+
add-on related API interactions. It is an implementation of the following API: https://gist.github.com/079c98529d399bb08c7a
|
16
|
+
|
17
|
+
Also, we have provided a command line client (the real first consumer of the API) so we could issue API requests
|
18
|
+
to the add-ons app without having to fire up a console session.
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
### make a test directory
|
23
|
+
|
24
|
+
mkdir client-test
|
25
|
+
cd client-test
|
26
|
+
|
27
|
+
### make a Gemfile with the gem on github
|
28
|
+
|
29
|
+
echo "source :rubygems" > Gemfile
|
30
|
+
echo "gem 'addons-client', :git => 'git@github.com:heroku/addons-client.git'" >> Gemfile
|
31
|
+
|
32
|
+
### use bundler to install the Gem from github
|
33
|
+
|
34
|
+
bundle install
|
35
|
+
|
36
|
+
### set up ENV
|
37
|
+
|
38
|
+
export ADDONS_API_URL=https://heroku:password@localhost:3000
|
39
|
+
|
40
|
+
### it works
|
41
|
+
|
42
|
+
bundle exec addons-client
|
43
|
+
Command must be one of: provision, deprovision, planchange
|
44
|
+
|
45
|
+
Remember to use bundle exec to run the command line commands!
|
46
|
+
|
47
|
+
## Ruby Usage
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
client = Addons::Client.new
|
51
|
+
# Addons::UserError: ADDONS_API_URL must be set
|
52
|
+
|
53
|
+
ENV['ADDONS_API_URL']='http://heroku:password@localhost:3000'
|
54
|
+
|
55
|
+
client = Addons::Client.new
|
56
|
+
```
|
57
|
+
|
58
|
+
### API Methods
|
59
|
+
```ruby
|
60
|
+
client.provision! 'memcache:5mb'
|
61
|
+
|
62
|
+
client.provision! 'foo:bar', :consumer_id => 'app123@heroku.com',
|
63
|
+
:options => { :foo => 'bar', 'baz' => 'test' }
|
64
|
+
|
65
|
+
# => {:resource_id=>"DEADBEEF",
|
66
|
+
# :config=>{"FOO_URL"=>"http://foo.com"},
|
67
|
+
# :message=>"great success",
|
68
|
+
# :provider_id=>"ABC123"}
|
69
|
+
|
70
|
+
client.plan_change! 'ABC123', 'new_plan'
|
71
|
+
|
72
|
+
client.deprovision! 'ABC123'
|
73
|
+
```
|
74
|
+
|
75
|
+
## Command Line Usage
|
76
|
+
export ADDONS_API_URL=http://heroku:password@localhost:3000
|
77
|
+
|
78
|
+
addons-client provision memcache:5mb --consumer-id=app123@heroku.com --options.foo=bar --options.baz=true
|
79
|
+
|
80
|
+
#### Provisioning:
|
81
|
+
|
82
|
+
addons-client provision glenntest:test
|
83
|
+
{"resource_id":"3bdb228d-a94e-4135-b19f-7a17a9f4f481","config":null,"message":null,"provider_id":null}
|
84
|
+
|
85
|
+
### use the resource id to interact with the add-on
|
86
|
+
|
87
|
+
addons-client plan_change 0dedb8f4-2921-42b8-81b9-a7df4c551140 test
|
88
|
+
|
89
|
+
addons-client deprovision 0dedb8f4-2921-42b8-81b9-a7df4c551140
|
90
|
+
Deprovisioned 0dedb8f4-2921-42b8-81b9-a7df4c551140
|
91
|
+
|
92
|
+
### fun with options
|
93
|
+
|
94
|
+
addons-client provision foo-bar:test --consumer_id=resource123@heroku.com --options.message='Good job'
|
95
|
+
|
96
|
+
|
97
|
+
## enabling api requests
|
98
|
+
Notice the provider_id is null.
|
99
|
+
This is because we won't send live requests until we've enabled the switch.
|
100
|
+
And also because in a later story we will want to have this toggleable on a per-request basis.
|
101
|
+
|
102
|
+
heroku config:add PROVIDER_API_ENABLED=true --app addons-staging
|
103
|
+
|
104
|
+
### it should now create a resource with a provider_id
|
105
|
+
|
106
|
+
addons-client provision foo-bar:test
|
107
|
+
Provisioned foo-bar:test
|
108
|
+
{"resource_id":"0dedb8f4-2921-42b8-81b9-a7df4c551140","config":{"MYADDON_URL":"http://user.yourapp.com"},"message":null,"provider_id":2}
|
109
|
+
|
110
|
+
## Test Usage
|
111
|
+
|
112
|
+
The client supports a mocked mode that sends no requests and returns canned responses.
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
Addons::Client.mock!
|
116
|
+
Addons::Client.new.provision! 'foo:bar'
|
117
|
+
# => {:resource_id=>"DEADBEEF",
|
118
|
+
# :config=>{"FOO_URL"=>"http://foo.com"},
|
119
|
+
# :message=>"great success",
|
120
|
+
# :provider_id=>"ABC123"}
|
121
|
+
|
122
|
+
Addons::Client.unmock!
|
123
|
+
Addons::Client.new.provision! 'foo:bar'
|
124
|
+
# Addons::UserError: ADDONS_API_URL must be set
|
125
|
+
```
|
126
|
+
|
127
|
+
## Contributing
|
128
|
+
|
129
|
+
1. Fork it
|
130
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
131
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
132
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
133
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$LOAD_PATH.unshift File.dirname(File.expand_path('.', __FILE__)) + '/lib'
|
3
|
+
require "addons-client/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.authors = ["Chris Continanza"]
|
7
|
+
gem.email = ["csquared@gmail.com"]
|
8
|
+
gem.description = %q{Addons Platform API client}
|
9
|
+
gem.summary = %q{Allows platfomrs to provision, deprovision, and change plans for add-on resources.}
|
10
|
+
gem.homepage = ""
|
11
|
+
|
12
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
13
|
+
gem.files = `git ls-files`.split("\n")
|
14
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
gem.name = "addons-client"
|
16
|
+
gem.require_paths = ["lib"]
|
17
|
+
gem.version = Addons::Client::VERSION
|
18
|
+
|
19
|
+
gem.add_dependency 'rest-client'
|
20
|
+
gem.add_dependency 'configliere'
|
21
|
+
gem.add_dependency 'json'
|
22
|
+
end
|
data/bin/addons-client
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require_relative '../lib/addons-client'
|
5
|
+
|
6
|
+
begin
|
7
|
+
Addons::CLI.run!
|
8
|
+
rescue Addons::UserError => e
|
9
|
+
STDERR.puts e.message and exit 1
|
10
|
+
rescue RestClient::ServiceUnavailable => e
|
11
|
+
if ! e.response.body.strip.empty?
|
12
|
+
STDERR.puts JSON.parse(e.response.body)['message']
|
13
|
+
else
|
14
|
+
STDERR.puts "Add-on is not available"
|
15
|
+
end
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Addons::CLI
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def run!
|
5
|
+
load_settings!
|
6
|
+
run_command!
|
7
|
+
end
|
8
|
+
|
9
|
+
def run_command!
|
10
|
+
command = Settings.rest.first
|
11
|
+
case command
|
12
|
+
when /deprovision/i
|
13
|
+
resource_id = Settings.rest[1]
|
14
|
+
raise Addons::UserError, "Must supply resource id" unless resource_id
|
15
|
+
response = client.deprovision!(resource_id)
|
16
|
+
puts "Deprovisioned #{resource_id}"
|
17
|
+
puts response
|
18
|
+
when /provision/i
|
19
|
+
slug = Settings.rest[1]
|
20
|
+
raise Addons::UserError, "Must supply add-on:plan" unless slug
|
21
|
+
response = client.provision!(slug, :options => Settings[:options],
|
22
|
+
:consumer_id => Settings[:consumer_id])
|
23
|
+
puts "Provisioned #{slug}"
|
24
|
+
puts response
|
25
|
+
when /plan-change/i
|
26
|
+
resource_id = Settings.rest[1]
|
27
|
+
raise Addons::UserError, "Must supply resource id" unless resource_id
|
28
|
+
plan = Settings.rest[2]
|
29
|
+
raise Addons::UserError, "Must supply plan after resource id" unless plan
|
30
|
+
response = client.plan_change!(resource_id, plan)
|
31
|
+
puts "Plan Changed to #{plan}"
|
32
|
+
puts response
|
33
|
+
else
|
34
|
+
if command
|
35
|
+
puts "#{command} is not a valid command"
|
36
|
+
else
|
37
|
+
puts "Command must be one of: provision, deprovision, plan-change"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def client
|
43
|
+
@client ||= Addons::Client
|
44
|
+
end
|
45
|
+
|
46
|
+
def puts(string)
|
47
|
+
STDOUT.puts(string)
|
48
|
+
end
|
49
|
+
|
50
|
+
def load_settings!
|
51
|
+
Settings.use :commandline
|
52
|
+
Settings.resolve!
|
53
|
+
rescue RuntimeError => e
|
54
|
+
raise Addons::UserError.new(e)
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Addons
|
2
|
+
class Client
|
3
|
+
extend Mock::Methods
|
4
|
+
extend Mock::Responses
|
5
|
+
DEFAULT_CONSUMER_ID = "api-client@localhost"
|
6
|
+
|
7
|
+
def self.api_url
|
8
|
+
@api_url = validate_api_url!
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.provision!(slug, opts = {})
|
12
|
+
wrap_request do
|
13
|
+
addon_name, plan = slug.split(':')
|
14
|
+
raise UserError, "No add-on name given" unless addon_name
|
15
|
+
raise UserError, "No plan name given" unless plan
|
16
|
+
|
17
|
+
if mocked?
|
18
|
+
mocked_provision(addon_name)
|
19
|
+
else
|
20
|
+
payload = {
|
21
|
+
:addon => addon_name,
|
22
|
+
:plan => plan,
|
23
|
+
:consumer_id => opts[:consumer_id] || DEFAULT_CONSUMER_ID
|
24
|
+
}
|
25
|
+
payload.merge! :options => opts[:options] if opts[:options]
|
26
|
+
payload.merge! :rate => opts[:rate] if opts[:rate]
|
27
|
+
payload.merge! :addons_id => opts[:addons_id] if opts[:addons_id]
|
28
|
+
if start_at = opts[:start_at]
|
29
|
+
start_at.utc
|
30
|
+
payload.merge! :start_at => start_at.to_s
|
31
|
+
end
|
32
|
+
if end_at = opts[:end_at]
|
33
|
+
end_at.utc
|
34
|
+
payload.merge! :end_at => end_at.to_s
|
35
|
+
end
|
36
|
+
resource.post payload, :accept => :json
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.deprovision!(resource_id)
|
42
|
+
wrap_request do
|
43
|
+
if mocked?
|
44
|
+
mocked_deprovision(resource_id)
|
45
|
+
else
|
46
|
+
resource["/#{resource_id}"].delete :accept => :json
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.plan_change!(resource_id, plan)
|
52
|
+
wrap_request do
|
53
|
+
if mocked?
|
54
|
+
mocked_plan_change(resource_id, plan)
|
55
|
+
else
|
56
|
+
payload = {
|
57
|
+
:plan => plan,
|
58
|
+
}
|
59
|
+
resource["/#{resource_id}"].put payload, :accept => :json
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.resource
|
65
|
+
RestClient::Resource.new(api_url.to_s)
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
def self.wrap_request
|
70
|
+
response = yield
|
71
|
+
Addons::Client::Response.new(response)
|
72
|
+
rescue RestClient::ResourceNotFound
|
73
|
+
raise UserError, "Add-on not found: check addon spelling and plan name"
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.validate_api_url!
|
77
|
+
api_url = nil
|
78
|
+
raise UserError, "ADDONS_API_URL must be set" unless ENV['ADDONS_API_URL']
|
79
|
+
begin
|
80
|
+
api_url = URI.join(ENV['ADDONS_API_URL'], '/api/1/resources')
|
81
|
+
rescue URI::InvalidURIError
|
82
|
+
raise UserError, "ADDONS_API_URL is an invalid url"
|
83
|
+
end
|
84
|
+
raise UserError, "No username given" unless api_url.user
|
85
|
+
raise UserError, "No password given" unless api_url.password
|
86
|
+
api_url
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Addons
|
2
|
+
class Client
|
3
|
+
module Mock
|
4
|
+
module Methods
|
5
|
+
def mock!
|
6
|
+
@mock = true
|
7
|
+
end
|
8
|
+
|
9
|
+
def unmock!
|
10
|
+
@mock = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def mocked?
|
14
|
+
@mock
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Responses
|
19
|
+
private
|
20
|
+
def mocked_provision(name)
|
21
|
+
{
|
22
|
+
'resource_id' => "DEADBEEF",
|
23
|
+
'config' => {"#{name.upcase}_URL" => 'http://foo.com'},
|
24
|
+
'message' => "great success",
|
25
|
+
'provider_id' => 'ABC123'
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def mocked_deprovision(resource_id)
|
30
|
+
{}
|
31
|
+
end
|
32
|
+
|
33
|
+
def mocked_plan_change(resource_id, plan)
|
34
|
+
{
|
35
|
+
'resource_id' => resource_id,
|
36
|
+
'config' => {"#{plan.upcase}_URL" => 'http://foo.com'},
|
37
|
+
'message' => "great success",
|
38
|
+
'provider_id' => 'ABC123'
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Addons
|
2
|
+
class Client
|
3
|
+
class Response
|
4
|
+
def initialize(response)
|
5
|
+
@data = case response
|
6
|
+
when String
|
7
|
+
response.strip.empty? ? {} : JSON.parse(response)
|
8
|
+
when Hash
|
9
|
+
response
|
10
|
+
end
|
11
|
+
@data
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
@data.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def method_missing(name, *args, &blk)
|
19
|
+
if @data.keys.include? name
|
20
|
+
@data[name]
|
21
|
+
elsif @data.keys.include? name.to_s
|
22
|
+
@data[name.to_s]
|
23
|
+
else
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
File without changes
|
@@ -0,0 +1,17 @@
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(File.expand_path('..', __FILE__)) + '/lib'
|
2
|
+
|
3
|
+
require "addons-client/version"
|
4
|
+
require 'rest-client'
|
5
|
+
require 'digest'
|
6
|
+
require 'configliere'
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
module Addons
|
10
|
+
Exception = Class.new(Exception)
|
11
|
+
UserError = Class.new(RuntimeError)
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'addons-client/mock'
|
15
|
+
require 'addons-client/client'
|
16
|
+
require 'addons-client/cli'
|
17
|
+
require 'addons-client/response'
|
data/test/client_test.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/test_helper"
|
2
|
+
|
3
|
+
class SettingsTest < Addons::Client::TestCase
|
4
|
+
def setup
|
5
|
+
stub_request(:any, /api\/1\/resources/)
|
6
|
+
ENV['ADDONS_API_URL'] = 'https://test:password@localhost:3333'
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_client_uses_env_var
|
10
|
+
Addons::Client.provision! 'foo:bar'
|
11
|
+
assert_requested(:post, URI.join(ENV['ADDONS_API_URL'], '/api/1/resources').to_s)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_client_raises_error_on_bad_url
|
15
|
+
[
|
16
|
+
nil,
|
17
|
+
'https://localhost:3333',
|
18
|
+
'https://foo@localhost:3333',
|
19
|
+
].each do |url|
|
20
|
+
ENV['ADDONS_API_URL'] = url
|
21
|
+
assert_raises Addons::UserError do
|
22
|
+
Addons::Client.provision! 'foo:bar'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_client_handles_404s_gracefully
|
28
|
+
stub_request(:any, /api\/1\/resources/).to_return(:status => 404)
|
29
|
+
assert_raises Addons::UserError do
|
30
|
+
Addons::Client.provision! 'foo:bar'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/test_helper"
|
2
|
+
|
3
|
+
class DeprovisionTest < Addons::Client::TestCase
|
4
|
+
def setup
|
5
|
+
ENV["ADDONS_API_URL"] = 'https://foo:bar@heroku.com'
|
6
|
+
stub_request(:any, target_url).to_return :body => " "
|
7
|
+
stub(Addons::CLI).puts
|
8
|
+
end
|
9
|
+
|
10
|
+
alias :target_url :resource_url
|
11
|
+
|
12
|
+
def test_cmd_line_requires_resource_id
|
13
|
+
assert_raises Addons::UserError, "Must supply resource id" do
|
14
|
+
addons_client! "deprovision"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_deprovisions_from_cmd_line
|
19
|
+
addons_client! "deprovision addons-uuid"
|
20
|
+
assert_requested(:delete, target_url)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_deprovisions_from_ruby
|
24
|
+
Addons::Client.deprovision! "addons-uuid"
|
25
|
+
assert_requested(:delete, target_url)
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/test_helper"
|
2
|
+
|
3
|
+
class PlanChangeTest < Addons::Client::TestCase
|
4
|
+
def setup
|
5
|
+
ENV["ADDONS_API_URL"] = 'https://foo:bar@heroku.com/api/1/resources'
|
6
|
+
stub_request(:any, target_url)
|
7
|
+
stub(Addons::CLI).puts
|
8
|
+
end
|
9
|
+
|
10
|
+
alias :target_url :resource_url
|
11
|
+
|
12
|
+
def test_plan_change_from_cmd_line
|
13
|
+
addons_client! "plan-change addons-uuid 5mb"
|
14
|
+
assert_requested(:put, target_url,
|
15
|
+
:body => { :plan => '5mb' })
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_plan_change_from_ruby
|
19
|
+
Addons::Client.plan_change! 'addons-uuid', 'plizzan'
|
20
|
+
assert_requested(:put, target_url,
|
21
|
+
:body => { :plan => 'plizzan' })
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_client_sets_plan_change_options
|
25
|
+
Addons::Client.plan_change! 'addons-uuid', 'bar'
|
26
|
+
|
27
|
+
assert_requested(:put, target_url,
|
28
|
+
:body => { :plan => 'bar' })
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_cmd_line_sets_plan_change_options
|
32
|
+
addons_client! "plan-change addons-uuid bar"
|
33
|
+
|
34
|
+
assert_requested(:put, target_url,
|
35
|
+
:body => { :plan => 'bar' })
|
36
|
+
assert_received(Addons::CLI) do |cli|
|
37
|
+
cli.puts "Plan Changed to bar"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_cmd_line_requires_resource_id
|
42
|
+
assert_raises Addons::UserError, "Must supply resource id" do
|
43
|
+
addons_client! "provision"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_cmd_line_requires_plan_name
|
48
|
+
assert_raises Addons::UserError, "Must supply plan after resource id" do
|
49
|
+
addons_client! "provision ABC-123"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/test_helper"
|
2
|
+
|
3
|
+
class ProvisionTest < Addons::Client::TestCase
|
4
|
+
def setup
|
5
|
+
ENV["ADDONS_API_URL"] = 'https://foo:bar@heroku.com'
|
6
|
+
stub_request(:any, target_url)
|
7
|
+
stub(Addons::CLI).puts
|
8
|
+
end
|
9
|
+
|
10
|
+
def target_url
|
11
|
+
URI.join(ENV["ADDONS_API_URL"], '/api/1/resources').to_s
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_cmd_line_requires_addon_slug
|
15
|
+
assert_raises Addons::UserError, "Must supply addon:plan" do
|
16
|
+
addons_client! "provision"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_cmd_line_requires_plan
|
21
|
+
assert_raises Addons::UserError, "No plan name given" do
|
22
|
+
addons_client! "provision foo"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_provisions_from_cmd_line
|
27
|
+
addons_client! "provision memcache:5mb"
|
28
|
+
assert_requested(:post, target_url,
|
29
|
+
:body => { :addon => 'memcache', :plan => '5mb',
|
30
|
+
:consumer_id => 'api-client@localhost'})
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_provisions_from_ruby
|
34
|
+
Addons::Client.provision! 'foo:plizzan'
|
35
|
+
assert_requested(:post, target_url,
|
36
|
+
:body => { :addon => 'foo', :plan => 'plizzan',
|
37
|
+
:consumer_id => 'api-client@localhost'})
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_sets_consumer_id
|
41
|
+
Addons::Client.provision! 'foo:bar', :consumer_id => 'app123@heroku.com'
|
42
|
+
assert_requested(:post, target_url,
|
43
|
+
:body => { :addon => 'foo', :plan => 'bar',
|
44
|
+
:consumer_id => 'app123@heroku.com'})
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_client_sets_provision_options
|
48
|
+
Addons::Client.provision! 'foo:bar',
|
49
|
+
:consumer_id => 'app123@heroku.com',
|
50
|
+
:options => { :foo => 'bar', 'baz' => 'test' }
|
51
|
+
|
52
|
+
assert_requested(:post, target_url,
|
53
|
+
:body => { :addon => 'foo', :plan => 'bar',
|
54
|
+
:consumer_id => 'app123@heroku.com',
|
55
|
+
:options => { :foo => 'bar', :baz => 'test'}})
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_cmd_line_sets_provision_options
|
59
|
+
addons_client! "provision foo:bar --options.foo=bar --options.baz=test --consumer_id=app123@heroku.com"
|
60
|
+
|
61
|
+
assert_requested(:post, target_url,
|
62
|
+
:body => { :addon => 'foo', :plan => 'bar',
|
63
|
+
:consumer_id => 'app123@heroku.com',
|
64
|
+
:options => { :foo => 'bar', :baz => 'test'}})
|
65
|
+
assert_received(Addons::CLI) do |cli|
|
66
|
+
cli.puts "Provisioned foo:bar"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/test_helper"
|
2
|
+
|
3
|
+
# testing the canned responses with the mocks
|
4
|
+
# means we are both testing the mock mode and
|
5
|
+
# keeping the mocked responses in one place
|
6
|
+
class ResponseTest < Addons::Client::TestCase
|
7
|
+
def setup
|
8
|
+
super
|
9
|
+
Addons::Client.mock!
|
10
|
+
end
|
11
|
+
|
12
|
+
def teardown
|
13
|
+
super
|
14
|
+
Addons::Client.unmock!
|
15
|
+
end
|
16
|
+
|
17
|
+
def target_url
|
18
|
+
URI.join(ENV["ADDONS_API_URL"], '/api/1/resources').to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_response_parses_json
|
22
|
+
Addons::Client.unmock!
|
23
|
+
stub_request(:any, target_url).to_return(:body => {
|
24
|
+
'resource_id' => '123-ABC',
|
25
|
+
'config' => {'ADDON_URL' => 'foo'},
|
26
|
+
'message' => 'great success',
|
27
|
+
'provider_id' => '42'
|
28
|
+
}.to_json)
|
29
|
+
|
30
|
+
response = Addons::Client.provision! 'memcache:5mb'
|
31
|
+
|
32
|
+
assert_equal '123-ABC', response.resource_id
|
33
|
+
assert_equal 'foo', response.config['ADDON_URL']
|
34
|
+
assert_equal 'great success', response.message
|
35
|
+
assert_equal '42', response.provider_id
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_response_object_wraps_provision_data
|
39
|
+
response = Addons::Client.provision! 'memcache:5mb'
|
40
|
+
|
41
|
+
assert_equal 'DEADBEEF', response.resource_id
|
42
|
+
assert_equal 'http://foo.com', response.config['MEMCACHE_URL']
|
43
|
+
assert_equal 'great success', response.message
|
44
|
+
assert_equal 'ABC123', response.provider_id
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_response_object_wraps_deprovision
|
48
|
+
response = Addons::Client.deprovision! 'UUID'
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_response_object_wraps_plan_change
|
52
|
+
response = Addons::Client.plan_change! 'UUID', 'plan'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
Bundler.require :test
|
3
|
+
require 'webmock/test_unit'
|
4
|
+
require "#{File.dirname(__FILE__)}/../lib/addons-client"
|
5
|
+
|
6
|
+
class Addons::Client::TestCase < Test::Unit::TestCase
|
7
|
+
include RR::Adapters::TestUnit
|
8
|
+
|
9
|
+
def setup
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def resource_url
|
14
|
+
URI.join(ENV["ADDONS_API_URL"], '/api/1/resources/', 'addons-uuid').to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
def teardown
|
18
|
+
super
|
19
|
+
WebMock.reset!
|
20
|
+
::ARGV.replace []
|
21
|
+
Settings.replace Hash.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def addons_client! cmd
|
25
|
+
::ARGV.replace cmd.split
|
26
|
+
Addons::CLI.run!
|
27
|
+
end
|
28
|
+
|
29
|
+
# for ruby 1.8.7 Test::Unit compatibility
|
30
|
+
def default_test
|
31
|
+
end
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: addons-client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Chris Continanza
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-04-29 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
prerelease: false
|
22
|
+
type: :runtime
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
name: rest-client
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
prerelease: false
|
36
|
+
type: :runtime
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
name: configliere
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
prerelease: false
|
50
|
+
type: :runtime
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
hash: 3
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
name: json
|
61
|
+
version_requirements: *id003
|
62
|
+
description: Addons Platform API client
|
63
|
+
email:
|
64
|
+
- csquared@gmail.com
|
65
|
+
executables:
|
66
|
+
- addons-client
|
67
|
+
extensions: []
|
68
|
+
|
69
|
+
extra_rdoc_files: []
|
70
|
+
|
71
|
+
files:
|
72
|
+
- .gitignore
|
73
|
+
- .travis.yml
|
74
|
+
- Gemfile
|
75
|
+
- LICENSE
|
76
|
+
- README.md
|
77
|
+
- Rakefile
|
78
|
+
- addons-client.gemspec
|
79
|
+
- bin/addons-client
|
80
|
+
- lib/addons-client.rb
|
81
|
+
- lib/addons-client/cli.rb
|
82
|
+
- lib/addons-client/client.rb
|
83
|
+
- lib/addons-client/mock.rb
|
84
|
+
- lib/addons-client/response.rb
|
85
|
+
- lib/addons-client/settings.rb
|
86
|
+
- lib/addons-client/version.rb
|
87
|
+
- test/client_test.rb
|
88
|
+
- test/deprovision_test.rb
|
89
|
+
- test/plan_change_test.rb
|
90
|
+
- test/provision_test.rb
|
91
|
+
- test/response_test.rb
|
92
|
+
- test/test_helper.rb
|
93
|
+
homepage: ""
|
94
|
+
licenses: []
|
95
|
+
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options: []
|
98
|
+
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
hash: 3
|
107
|
+
segments:
|
108
|
+
- 0
|
109
|
+
version: "0"
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
none: false
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
hash: 3
|
116
|
+
segments:
|
117
|
+
- 0
|
118
|
+
version: "0"
|
119
|
+
requirements: []
|
120
|
+
|
121
|
+
rubyforge_project:
|
122
|
+
rubygems_version: 1.8.15
|
123
|
+
signing_key:
|
124
|
+
specification_version: 3
|
125
|
+
summary: Allows platfomrs to provision, deprovision, and change plans for add-on resources.
|
126
|
+
test_files:
|
127
|
+
- test/client_test.rb
|
128
|
+
- test/deprovision_test.rb
|
129
|
+
- test/plan_change_test.rb
|
130
|
+
- test/provision_test.rb
|
131
|
+
- test/response_test.rb
|
132
|
+
- test/test_helper.rb
|