maitre_d 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +3 -0
- data/README.textile +121 -0
- data/Rakefile +1 -1
- data/addon-manifest.json +10 -0
- data/config/routes.rb +1 -0
- data/lib/maitre_d/heroku/api.rb +42 -0
- data/lib/maitre_d/heroku/api_helpers.rb +36 -0
- data/lib/maitre_d/heroku/listener.rb +17 -0
- data/lib/maitre_d/heroku.rb +41 -0
- data/lib/maitre_d/opperator/api.rb +3 -3
- data/lib/maitre_d/opperator/listener.rb +13 -0
- data/lib/maitre_d/opperator.rb +13 -7
- data/lib/maitre_d/version.rb +1 -1
- data/lib/maitre_d.rb +1 -0
- data/maitre_d.gemspec +3 -2
- data/spec/api/heroku/provisioning_spec.rb +87 -0
- data/spec/api/heroku/single_sign_on_spec.rb +49 -0
- data/spec/api/opperator/provisioning_spec.rb +1 -1
- data/spec/internal/app/listeners/heroku_listener.rb +29 -0
- data/spec/internal/app/listeners/opperator_listener.rb +16 -0
- data/spec/internal/config/initializers/heroku.rb +6 -0
- data/spec/internal/config/initializers/opperator.rb +4 -1
- data/spec/internal/config/routes.rb +9 -0
- metadata +35 -8
- data/spec/spec/internal/log/test.log +0 -0
data/HISTORY
CHANGED
data/README.textile
CHANGED
@@ -2,6 +2,127 @@ h1. Maître d'
|
|
2
2
|
|
3
3
|
Rack APIs powered by Grape for managing Heroku and Opperator add-ons. If used within a Rails application, it'll automatically be mounted as a Rails engine at the appropriate paths.
|
4
4
|
|
5
|
+
Maître d' manages all the authorisation checking for API requests and provides simple hooks for you to write just the code you need to handle provisioning, plan changes, deprovisioning and single-sign-on (SSO) requests.
|
6
|
+
|
7
|
+
h2. Installing
|
8
|
+
|
9
|
+
h3. With Rails
|
10
|
+
|
11
|
+
Because there's a Rails engine as part of Maître d', the API routing is set up automatically. Jump forward to the configuration and listener setup below.
|
12
|
+
|
13
|
+
h3. Without Rack
|
14
|
+
|
15
|
+
This library provides two APIs as mountable Rack applications for you - so you'll want to mount them at the appropriate paths: @MaitreD::Heroku::API@ at @/heroku@, and/or @MaitreD::Opperator::API@ at @/opperator@.
|
16
|
+
|
17
|
+
h2. Configuration
|
18
|
+
|
19
|
+
You'll need to provide Maître d' with the appropriate provider credentials - in a Rails app, this would go in an initializer, but for Rack/Sinatra apps just get the values set before the routing is defined.
|
20
|
+
|
21
|
+
<pre><code>MaitreD::Heroku.configure do |config|
|
22
|
+
config.id = 'addon-id'
|
23
|
+
config.password = 'random'
|
24
|
+
config.sso_salt = 'gibberish'
|
25
|
+
config.listener = HerokuListener
|
26
|
+
end
|
27
|
+
|
28
|
+
MaitreD::Opperator.configure do |config|
|
29
|
+
config.shared_secret = 'something-special'
|
30
|
+
config.listener = OpperatorListener
|
31
|
+
end</code></pre>
|
32
|
+
|
33
|
+
The listeners that are mentioned in the code above are classes, which will handle valid API requests. Read on for more details on how to set them up.
|
34
|
+
|
35
|
+
h2. Heroku Listener
|
36
|
+
|
37
|
+
Your Heroku listener class should be a subclass of @MaitreD::Heroku::Listener@, and handle the following four methods:
|
38
|
+
|
39
|
+
h3. @provision(heroku_id, plan, callback_url, logplex_token, options)@
|
40
|
+
|
41
|
+
This gets called when Heroku's requesting an app be provisioned within your service, and expects a hash to be returned with the following keys:
|
42
|
+
|
43
|
+
<dl>
|
44
|
+
<dt><code>id</code></dt>
|
45
|
+
<dd>Your local resource id, which Heroku will use in related requests (to change plans, deprovision or initialise single-sign-on).</dd>
|
46
|
+
<dt><code>config</code></dt>
|
47
|
+
<dd>A hash of the newly provisioned resource's configuration values (that are provided as environment variables to the app in question).</dd>
|
48
|
+
<dt><code>message</code></dt>
|
49
|
+
<dd>An optional message that will be displayed when your add-on is added via the command-line.</dd>
|
50
|
+
</dl>
|
51
|
+
|
52
|
+
h3. @plan_change(resource_id, heroku_id, plan)@
|
53
|
+
|
54
|
+
This gets called when an app is upgrading or downgrading from their current plan. You need to return a hash with the following keys:
|
55
|
+
|
56
|
+
<dl>
|
57
|
+
<dt><code>config</code></dt>
|
58
|
+
<dd>A hash of the modified resource's configuration values (that are provided as environment variables to the app in question).</dd>
|
59
|
+
<dt><code>message</code></dt>
|
60
|
+
<dd>An optional message that will be displayed when an app using your add-on is upgraded or downgraded via the command-line.</dd>
|
61
|
+
</dl>
|
62
|
+
|
63
|
+
h3. @deprovision(resource_id)@
|
64
|
+
|
65
|
+
This gets called when an app is removing your add-on from itself. You don't have to return anything in particular for this, though Heroku may pass through the @message@ argument like it does for the @provision@ and @plan_change@ calls.
|
66
|
+
|
67
|
+
h3. @single_sign_on(resource_id)@
|
68
|
+
|
69
|
+
Maître d' will check the token and timestamp provided, and sets up the nav-data cookie, but you'll need to decide where the user gets redirected to and what other details you wish to track via their session. To do this, just return a hash with the following keys:
|
70
|
+
|
71
|
+
<dl>
|
72
|
+
<dt><code>uri</code></dt>
|
73
|
+
<dd>The URI to redirect the user to, now that you've signed them in.</dd>
|
74
|
+
<dt><code>session</code></dt>
|
75
|
+
<dd>A hash of any session values you wish to be set.</dd>
|
76
|
+
</dl>
|
77
|
+
|
78
|
+
Here's a very basic example:
|
79
|
+
|
80
|
+
<pre><code>class HerokuListener < MaitreD::Heroku::Listener
|
81
|
+
def provision(heroku_id, plan, callback_url, logplex_token, options)
|
82
|
+
plan = Plan.find_by_name plan
|
83
|
+
widget = Widget.create(
|
84
|
+
:heroku_id => heroku_id,
|
85
|
+
:callback_url => callback_url,
|
86
|
+
:plan => plan
|
87
|
+
)
|
88
|
+
|
89
|
+
{
|
90
|
+
:id => widget.id,
|
91
|
+
:config => {'WIDGET_KEY' => widget.key},
|
92
|
+
:message => 'Add-on provisioned!'
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
def plan_change(resource_id, heroku_id, plan)
|
97
|
+
plan = Plan.find_by_name plan
|
98
|
+
widget = Widget.find resource_id
|
99
|
+
widget.plan = plan
|
100
|
+
widget.save
|
101
|
+
|
102
|
+
{:config => {'WIDGET_KEY' => widget.key}}
|
103
|
+
end
|
104
|
+
|
105
|
+
def deprovision(resource_id)
|
106
|
+
widget = Widget.find resource_id
|
107
|
+
widget.destroy
|
108
|
+
end
|
109
|
+
|
110
|
+
def single_sign_on(resource_id)
|
111
|
+
widget = Widget.find resource_id
|
112
|
+
|
113
|
+
{
|
114
|
+
:uri => '/my/dashboard',
|
115
|
+
:session => {:widget_id => widget.id}
|
116
|
+
}
|
117
|
+
end
|
118
|
+
end</code></pre>
|
119
|
+
|
120
|
+
You can have the listener class wherever you like - as long as it's available within the context of your Rails/Rack site, it'll work as expected.
|
121
|
+
|
122
|
+
h2. Opperator Listener
|
123
|
+
|
124
|
+
This listener is currently in progress as the Opperator API is established.
|
125
|
+
|
5
126
|
h2. Contributing
|
6
127
|
|
7
128
|
Contributions are very much welcome - but keep in mind the following:
|
data/Rakefile
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require
|
1
|
+
require 'bundler/gem_tasks'
|
data/addon-manifest.json
ADDED
data/config/routes.rb
CHANGED
@@ -0,0 +1,42 @@
|
|
1
|
+
class MaitreD::Heroku::API < Grape::API
|
2
|
+
helpers MaitreD::Heroku::APIHelpers
|
3
|
+
|
4
|
+
resources :resources do
|
5
|
+
get ':id' do
|
6
|
+
error!('403 Forbidden', 403) unless valid_token? && valid_timestamp?
|
7
|
+
|
8
|
+
hash = MaitreD::Heroku.listener.new.single_sign_on(params[:id])
|
9
|
+
|
10
|
+
hash[:session] ||= {}
|
11
|
+
hash[:session].each { |key, value| session[key] = value }
|
12
|
+
|
13
|
+
status 302
|
14
|
+
header 'Location', hash[:uri]
|
15
|
+
Rack::Utils.set_cookie_header! header, 'heroku-nav-data',
|
16
|
+
:value => params['nav-data']
|
17
|
+
end
|
18
|
+
|
19
|
+
post do
|
20
|
+
authenticate!
|
21
|
+
|
22
|
+
MaitreD::Heroku.listener.new.provision(
|
23
|
+
params[:heroku_id], params[:plan], params[:callback_url],
|
24
|
+
params[:logplex_token], params[:options]
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
put ':id' do
|
29
|
+
authenticate!
|
30
|
+
|
31
|
+
MaitreD::Heroku.listener.new.plan_change(
|
32
|
+
params[:id], params[:heroku_id], params[:plan]
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
delete ':id' do
|
37
|
+
authenticate!
|
38
|
+
|
39
|
+
MaitreD::Heroku.listener.new.deprovision params[:id]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module MaitreD::Heroku::APIHelpers
|
2
|
+
def authenticate!
|
3
|
+
error!('401 Unauthorized', 401) unless valid_authorization?
|
4
|
+
end
|
5
|
+
|
6
|
+
def session
|
7
|
+
env['rack.session']
|
8
|
+
end
|
9
|
+
|
10
|
+
def valid_timestamp?
|
11
|
+
params[:timestamp].to_i >= (Time.now - 5*60).to_i
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid_token?
|
15
|
+
expected_token == params[:token]
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def expected_token
|
21
|
+
@expected_token ||= Digest::SHA1.hexdigest(
|
22
|
+
"#{params[:id]}:#{MaitreD::Heroku.sso_salt}:#{params[:timestamp]}"
|
23
|
+
).to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
def valid_authorization?
|
27
|
+
valid_authorization.strip == env['HTTP_AUTHORIZATION'].strip
|
28
|
+
end
|
29
|
+
|
30
|
+
def valid_authorization
|
31
|
+
encoded_authorization = Base64.encode64(
|
32
|
+
"#{MaitreD::Heroku.id}:#{MaitreD::Heroku.password}"
|
33
|
+
)
|
34
|
+
"Basic #{encoded_authorization}"
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class MaitreD::Heroku::Listener
|
2
|
+
def provision(heroku_id, plan, callback_url, logplex_token, options)
|
3
|
+
#
|
4
|
+
end
|
5
|
+
|
6
|
+
def plan_change(resource_id, heroku_id, plan)
|
7
|
+
#
|
8
|
+
end
|
9
|
+
|
10
|
+
def deprovision(resource_id)
|
11
|
+
#
|
12
|
+
end
|
13
|
+
|
14
|
+
def single_sign_on(resource_id)
|
15
|
+
#
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module MaitreD::Heroku
|
2
|
+
def self.configure
|
3
|
+
yield self
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.listener
|
7
|
+
@listener
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.listener=(listener)
|
11
|
+
@listener = listener
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.id
|
15
|
+
@id
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.id=(id)
|
19
|
+
@id = id
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.password
|
23
|
+
@password
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.password=(password)
|
27
|
+
@password = password
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.sso_salt
|
31
|
+
@sso_salt
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.sso_salt=(salt)
|
35
|
+
@sso_salt = salt
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
require 'maitre_d/heroku/api_helpers'
|
40
|
+
require 'maitre_d/heroku/api'
|
41
|
+
require 'maitre_d/heroku/listener'
|
@@ -5,15 +5,15 @@ class MaitreD::Opperator::API < Grape::API
|
|
5
5
|
|
6
6
|
resources :instances do
|
7
7
|
post do
|
8
|
-
MaitreD::Opperator.
|
8
|
+
MaitreD::Opperator.listener.new.provision params[:features]
|
9
9
|
end
|
10
10
|
|
11
11
|
delete ':id' do
|
12
|
-
|
12
|
+
MaitreD::Opperator.listener.new.deprovision params[:id]
|
13
13
|
end
|
14
14
|
|
15
15
|
put ':id' do
|
16
|
-
|
16
|
+
MaitreD::Opperator.listener.new.change_plan params[:id], params[:features]
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
data/lib/maitre_d/opperator.rb
CHANGED
@@ -1,4 +1,16 @@
|
|
1
1
|
module MaitreD::Opperator
|
2
|
+
def self.configure
|
3
|
+
yield self
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.listener
|
7
|
+
@listener
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.listener=(listener)
|
11
|
+
@listener = listener
|
12
|
+
end
|
13
|
+
|
2
14
|
def self.shared_secret
|
3
15
|
@shared_secret
|
4
16
|
end
|
@@ -6,14 +18,8 @@ module MaitreD::Opperator
|
|
6
18
|
def self.shared_secret=(secret)
|
7
19
|
@shared_secret = secret
|
8
20
|
end
|
9
|
-
|
10
|
-
def self.subscribe(features = {})
|
11
|
-
{
|
12
|
-
'id' => '321',
|
13
|
-
'config' => {}
|
14
|
-
}
|
15
|
-
end
|
16
21
|
end
|
17
22
|
|
18
23
|
require 'maitre_d/opperator/api_helpers'
|
19
24
|
require 'maitre_d/opperator/api'
|
25
|
+
require 'maitre_d/opperator/listener'
|
data/lib/maitre_d/version.rb
CHANGED
data/lib/maitre_d.rb
CHANGED
data/maitre_d.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
$:.push File.expand_path(
|
3
|
-
require
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'maitre_d/version'
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = 'maitre_d'
|
@@ -18,6 +18,7 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
19
|
s.require_paths = ['lib']
|
20
20
|
|
21
|
+
s.add_development_dependency 'kensa', '1.3.0'
|
21
22
|
s.add_development_dependency 'rails', '3.1.3'
|
22
23
|
s.add_development_dependency 'rspec-rails', '2.7.0'
|
23
24
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Heroku Provisioning API' do
|
4
|
+
let(:authorisation) { "Basic #{Base64.encode64('foo:bar')}" }
|
5
|
+
let(:json_response) { JSON.parse response.body }
|
6
|
+
|
7
|
+
describe 'Provisioning' do
|
8
|
+
let(:params) {
|
9
|
+
{
|
10
|
+
:plan => 'basic',
|
11
|
+
:callback_url => 'https://domain/vendor/apps/app123%40heroku.com',
|
12
|
+
:heroku_id => 'app123@heroku.com'
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
it "returns a 401 if the HTTP authorisation does not match" do
|
17
|
+
post '/heroku/resources', params,
|
18
|
+
{'HTTP_AUTHORIZATION' => 'Basic foobarbaz'}
|
19
|
+
|
20
|
+
response.status.should == 401
|
21
|
+
end
|
22
|
+
|
23
|
+
it "returns the resource id" do
|
24
|
+
post '/heroku/resources', params, {'HTTP_AUTHORIZATION' => authorisation}
|
25
|
+
|
26
|
+
json_response['id'].should == '123'
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns the resource configuration" do
|
30
|
+
post '/heroku/resources', params, {'HTTP_AUTHORIZATION' => authorisation}
|
31
|
+
|
32
|
+
json_response['config'].should == {'FOO_PROVISIONED' => "true"}
|
33
|
+
end
|
34
|
+
|
35
|
+
it "returns a custom message" do
|
36
|
+
post '/heroku/resources', params, {'HTTP_AUTHORIZATION' => authorisation}
|
37
|
+
|
38
|
+
json_response['message'].should == 'Add-on provisioned!'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'Changing Plans' do
|
43
|
+
let(:params) {
|
44
|
+
{:heroku_id => 'app123@heroku.com', :plan => 'premium'}
|
45
|
+
}
|
46
|
+
|
47
|
+
it "returns a 401 if the HTTP authorisation does not match" do
|
48
|
+
put '/heroku/resources/7', params,
|
49
|
+
{'HTTP_AUTHORIZATION' => 'Basic foobarbaz'}
|
50
|
+
|
51
|
+
response.status.should == 401
|
52
|
+
end
|
53
|
+
|
54
|
+
it "returns the new resource configuration" do
|
55
|
+
put '/heroku/resources/7', params, {'HTTP_AUTHORIZATION' => authorisation}
|
56
|
+
|
57
|
+
json_response['config'].should == {'FOO_PROVISIONED' => "false"}
|
58
|
+
end
|
59
|
+
|
60
|
+
it "returns a custom message" do
|
61
|
+
put '/heroku/resources/7', params, {'HTTP_AUTHORIZATION' => authorisation}
|
62
|
+
|
63
|
+
json_response['message'].should == 'Add-on upgraded or downgraded.'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe 'Deprovisioning' do
|
68
|
+
it "returns a 401 if the HTTP authorisation does not match" do
|
69
|
+
delete '/heroku/resources/28', {},
|
70
|
+
{'HTTP_AUTHORIZATION' => 'Basic foobarbaz'}
|
71
|
+
|
72
|
+
response.status.should == 401
|
73
|
+
end
|
74
|
+
|
75
|
+
it "returns with a status of 200" do
|
76
|
+
delete '/heroku/resources/28', {}, {'HTTP_AUTHORIZATION' => authorisation}
|
77
|
+
|
78
|
+
response.status.should == 200
|
79
|
+
end
|
80
|
+
|
81
|
+
it "returns a custom message" do
|
82
|
+
delete '/heroku/resources/28', {}, {'HTTP_AUTHORIZATION' => authorisation}
|
83
|
+
|
84
|
+
json_response['message'].should == 'Add-on removed.'
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Heroku SSO API' do
|
4
|
+
let(:timestamp) { Time.now.to_i }
|
5
|
+
let(:nav_data) { 'heroku-nav-data-goes-here' }
|
6
|
+
let(:token) {
|
7
|
+
pre_token = "789:sea salt:#{timestamp.to_s}"
|
8
|
+
Digest::SHA1.hexdigest(pre_token).to_s
|
9
|
+
}
|
10
|
+
|
11
|
+
it "renders a 403 if the token is incorrect" do
|
12
|
+
get '/heroku/resources/789', :token => 'foo',
|
13
|
+
:timestamp => timestamp, 'nav-data' => nav_data
|
14
|
+
|
15
|
+
response.status.should == 403
|
16
|
+
end
|
17
|
+
|
18
|
+
it "renders a 403 if the timestamp is older than 5 minutes" do
|
19
|
+
timestamp = 5.minutes.ago.to_i - 1
|
20
|
+
pre_token = "789:sea salt:#{timestamp.to_s}"
|
21
|
+
token = Digest::SHA1.hexdigest(pre_token).to_s
|
22
|
+
|
23
|
+
get '/heroku/resources/789', :token => token,
|
24
|
+
:timestamp => timestamp, 'nav-data' => nav_data
|
25
|
+
|
26
|
+
response.status.should == 403
|
27
|
+
end
|
28
|
+
|
29
|
+
it "sets the heroku nav data cookie" do
|
30
|
+
get '/heroku/resources/789', :token => token,
|
31
|
+
:timestamp => timestamp, 'nav-data' => nav_data
|
32
|
+
|
33
|
+
cookies['heroku-nav-data'].should == nav_data
|
34
|
+
end
|
35
|
+
|
36
|
+
it "redirects to the appropriate URL" do
|
37
|
+
get '/heroku/resources/789', :token => token,
|
38
|
+
:timestamp => timestamp, 'nav-data' => nav_data
|
39
|
+
|
40
|
+
response.should redirect_to('/my/dashboard')
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should set the provided session variables" do
|
44
|
+
get '/heroku/resources/789', :token => token,
|
45
|
+
:timestamp => timestamp, 'nav-data' => nav_data
|
46
|
+
|
47
|
+
session[:app_id].should == '789'
|
48
|
+
end
|
49
|
+
end
|
@@ -22,7 +22,7 @@ describe 'Opperator Provisioning API' do
|
|
22
22
|
post '/opperator/instances', {'features' => {}},
|
23
23
|
{'X-Opperator-Shared-Secret' => 'something-special'}
|
24
24
|
|
25
|
-
json_response['config'].should
|
25
|
+
json_response['config'].should == {'provisioned_for' => 'opperator'}
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class HerokuListener < MaitreD::Heroku::Listener
|
2
|
+
def provision(heroku_id, plan, callback_url, logplex_token, options)
|
3
|
+
{
|
4
|
+
:id => '123',
|
5
|
+
:config => {'FOO_PROVISIONED' => 'true'},
|
6
|
+
:message => 'Add-on provisioned!'
|
7
|
+
}
|
8
|
+
end
|
9
|
+
|
10
|
+
def plan_change(resource_id, heroku_id, plan)
|
11
|
+
{
|
12
|
+
:config => {'FOO_PROVISIONED' => 'false'},
|
13
|
+
:message => 'Add-on upgraded or downgraded.'
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def deprovision(resource_id)
|
18
|
+
{
|
19
|
+
:message => 'Add-on removed.'
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def single_sign_on(resource_id)
|
24
|
+
{
|
25
|
+
:uri => '/my/dashboard',
|
26
|
+
:session => {:app_id => resource_id}
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class OpperatorListener < MaitreD::Opperator::Listener
|
2
|
+
def provision(features)
|
3
|
+
{
|
4
|
+
'id' => '321',
|
5
|
+
'config' => {'provisioned_for' => 'opperator'}
|
6
|
+
}
|
7
|
+
end
|
8
|
+
|
9
|
+
def deprovision(resource_id)
|
10
|
+
#
|
11
|
+
end
|
12
|
+
|
13
|
+
def change_plan(resource_id, features)
|
14
|
+
#
|
15
|
+
end
|
16
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: maitre_d
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,22 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2012-01-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: kensa
|
16
|
+
requirement: &70319103578580 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - =
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.3.0
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70319103578580
|
14
25
|
- !ruby/object:Gem::Dependency
|
15
26
|
name: rails
|
16
|
-
requirement: &
|
27
|
+
requirement: &70319103578080 !ruby/object:Gem::Requirement
|
17
28
|
none: false
|
18
29
|
requirements:
|
19
30
|
- - =
|
@@ -21,10 +32,10 @@ dependencies:
|
|
21
32
|
version: 3.1.3
|
22
33
|
type: :development
|
23
34
|
prerelease: false
|
24
|
-
version_requirements: *
|
35
|
+
version_requirements: *70319103578080
|
25
36
|
- !ruby/object:Gem::Dependency
|
26
37
|
name: rspec-rails
|
27
|
-
requirement: &
|
38
|
+
requirement: &70319103577620 !ruby/object:Gem::Requirement
|
28
39
|
none: false
|
29
40
|
requirements:
|
30
41
|
- - =
|
@@ -32,7 +43,7 @@ dependencies:
|
|
32
43
|
version: 2.7.0
|
33
44
|
type: :development
|
34
45
|
prerelease: false
|
35
|
-
version_requirements: *
|
46
|
+
version_requirements: *70319103577620
|
36
47
|
description: A Rack API (through Grape) for Heroku and Opperator add-on providers
|
37
48
|
- which can also be attached as a Rails Engine.
|
38
49
|
email:
|
@@ -47,20 +58,31 @@ files:
|
|
47
58
|
- LICENCE
|
48
59
|
- README.textile
|
49
60
|
- Rakefile
|
61
|
+
- addon-manifest.json
|
50
62
|
- config.ru
|
51
63
|
- config/routes.rb
|
52
64
|
- lib/maitre_d.rb
|
53
65
|
- lib/maitre_d/engine.rb
|
66
|
+
- lib/maitre_d/heroku.rb
|
67
|
+
- lib/maitre_d/heroku/api.rb
|
68
|
+
- lib/maitre_d/heroku/api_helpers.rb
|
69
|
+
- lib/maitre_d/heroku/listener.rb
|
54
70
|
- lib/maitre_d/opperator.rb
|
55
71
|
- lib/maitre_d/opperator/api.rb
|
56
72
|
- lib/maitre_d/opperator/api_helpers.rb
|
73
|
+
- lib/maitre_d/opperator/listener.rb
|
57
74
|
- lib/maitre_d/version.rb
|
58
75
|
- maitre_d.gemspec
|
76
|
+
- spec/api/heroku/provisioning_spec.rb
|
77
|
+
- spec/api/heroku/single_sign_on_spec.rb
|
59
78
|
- spec/api/opperator/provisioning_spec.rb
|
79
|
+
- spec/internal/app/listeners/heroku_listener.rb
|
80
|
+
- spec/internal/app/listeners/opperator_listener.rb
|
81
|
+
- spec/internal/config/initializers/heroku.rb
|
60
82
|
- spec/internal/config/initializers/opperator.rb
|
83
|
+
- spec/internal/config/routes.rb
|
61
84
|
- spec/internal/log/.gitignore
|
62
85
|
- spec/internal/public/favicon.ico
|
63
|
-
- spec/spec/internal/log/test.log
|
64
86
|
- spec/spec_helper.rb
|
65
87
|
homepage: http://github.com/flying-sphinx/maitre_d
|
66
88
|
licenses: []
|
@@ -87,10 +109,15 @@ signing_key:
|
|
87
109
|
specification_version: 3
|
88
110
|
summary: Rack API and Rails Engine for Heroku and Opperator add-ons
|
89
111
|
test_files:
|
112
|
+
- spec/api/heroku/provisioning_spec.rb
|
113
|
+
- spec/api/heroku/single_sign_on_spec.rb
|
90
114
|
- spec/api/opperator/provisioning_spec.rb
|
115
|
+
- spec/internal/app/listeners/heroku_listener.rb
|
116
|
+
- spec/internal/app/listeners/opperator_listener.rb
|
117
|
+
- spec/internal/config/initializers/heroku.rb
|
91
118
|
- spec/internal/config/initializers/opperator.rb
|
119
|
+
- spec/internal/config/routes.rb
|
92
120
|
- spec/internal/log/.gitignore
|
93
121
|
- spec/internal/public/favicon.ico
|
94
|
-
- spec/spec/internal/log/test.log
|
95
122
|
- spec/spec_helper.rb
|
96
123
|
has_rdoc:
|
File without changes
|