azuki-api 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.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.travis.yml +16 -0
  4. data/Gemfile +4 -0
  5. data/README.md +128 -0
  6. data/Rakefile +41 -0
  7. data/azuki-api.gemspec +23 -0
  8. data/changelog.txt +204 -0
  9. data/lib/azuki-api.rb +1 -0
  10. data/lib/azuki/api.rb +146 -0
  11. data/lib/azuki/api/addons.rb +48 -0
  12. data/lib/azuki/api/apps.rb +71 -0
  13. data/lib/azuki/api/attachments.rb +14 -0
  14. data/lib/azuki/api/collaborators.rb +33 -0
  15. data/lib/azuki/api/config_vars.rb +33 -0
  16. data/lib/azuki/api/domains.rb +42 -0
  17. data/lib/azuki/api/errors.rb +26 -0
  18. data/lib/azuki/api/features.rb +45 -0
  19. data/lib/azuki/api/keys.rb +42 -0
  20. data/lib/azuki/api/login.rb +14 -0
  21. data/lib/azuki/api/logs.rb +18 -0
  22. data/lib/azuki/api/mock.rb +179 -0
  23. data/lib/azuki/api/mock/addons.rb +153 -0
  24. data/lib/azuki/api/mock/apps.rb +205 -0
  25. data/lib/azuki/api/mock/attachments.rb +19 -0
  26. data/lib/azuki/api/mock/cache/get_addons.json +1 -0
  27. data/lib/azuki/api/mock/cache/get_features.json +1 -0
  28. data/lib/azuki/api/mock/cache/get_user.json +1 -0
  29. data/lib/azuki/api/mock/collaborators.rb +55 -0
  30. data/lib/azuki/api/mock/config_vars.rb +49 -0
  31. data/lib/azuki/api/mock/domains.rb +80 -0
  32. data/lib/azuki/api/mock/features.rb +120 -0
  33. data/lib/azuki/api/mock/keys.rb +46 -0
  34. data/lib/azuki/api/mock/login.rb +22 -0
  35. data/lib/azuki/api/mock/logs.rb +20 -0
  36. data/lib/azuki/api/mock/processes.rb +198 -0
  37. data/lib/azuki/api/mock/releases.rb +69 -0
  38. data/lib/azuki/api/mock/stacks.rb +83 -0
  39. data/lib/azuki/api/mock/user.rb +16 -0
  40. data/lib/azuki/api/processes.rb +77 -0
  41. data/lib/azuki/api/releases.rb +33 -0
  42. data/lib/azuki/api/ssl_endpoints.rb +62 -0
  43. data/lib/azuki/api/stacks.rb +22 -0
  44. data/lib/azuki/api/user.rb +14 -0
  45. data/lib/azuki/api/vendor/okjson.rb +600 -0
  46. data/lib/azuki/api/version.rb +5 -0
  47. data/test/data/site.crt +19 -0
  48. data/test/data/site.key +27 -0
  49. data/test/test_addons.rb +193 -0
  50. data/test/test_apps.rb +147 -0
  51. data/test/test_attachments.rb +23 -0
  52. data/test/test_collaborators.rb +73 -0
  53. data/test/test_config_vars.rb +54 -0
  54. data/test/test_domains.rb +65 -0
  55. data/test/test_error_conditions.rb +11 -0
  56. data/test/test_features.rb +87 -0
  57. data/test/test_helper.rb +48 -0
  58. data/test/test_keys.rb +39 -0
  59. data/test/test_login.rb +20 -0
  60. data/test/test_logs.rb +29 -0
  61. data/test/test_processes.rb +245 -0
  62. data/test/test_releases.rb +73 -0
  63. data/test/test_ssl_endpoints.rb +132 -0
  64. data/test/test_stacks.rb +49 -0
  65. data/test/test_user.rb +13 -0
  66. metadata +168 -0
@@ -0,0 +1,80 @@
1
+ module Azuki
2
+ class API
3
+ module Mock
4
+
5
+ # stub DELETE /apps/:app/domains/:domain
6
+ Excon.stub(:expects => 200, :method => :delete, :path => %r{^/apps/([^/]+)/domains/([^/]+)$} ) do |params|
7
+ request_params, mock_data = parse_stub_params(params)
8
+ app, domain, _ = request_params[:captures][:path]
9
+ domain = unescape(domain)
10
+ with_mock_app(mock_data, app) do |app_data|
11
+ if domain = get_mock_app_domain(mock_data, app, domain)
12
+ mock_data[:domains][app].delete(domain)
13
+ {
14
+ :body => {},
15
+ :status => 200
16
+ }
17
+ else
18
+ {
19
+ :body => 'Domain not found.',
20
+ :status => 404
21
+ }
22
+ end
23
+ end
24
+ end
25
+
26
+ # stub DELETE /apps/:app/domains/:domain
27
+ Excon.stub(:expects => 200, :method => :delete, :path => %r{^/apps/([^/]+)/domains$} ) do |params|
28
+ request_params, mock_data = parse_stub_params(params)
29
+ app, _ = request_params[:captures][:path]
30
+ with_mock_app(mock_data, app) do |app_data|
31
+ mock_data[:domains][app] = []
32
+ {
33
+ :body => "",
34
+ :status => 200
35
+ }
36
+ end
37
+ end
38
+
39
+ # stub DELETE /apps//domains/:domain
40
+ Excon.stub({:method => :delete, :path => %r{^/apps//domains/([^/]+)$} }, {:status => 404})
41
+
42
+ # stub GET /apps/:app/domains
43
+ Excon.stub(:expects => 200, :method => :get, :path => %r{^/apps/([^/]+)/domains$} ) do |params|
44
+ request_params, mock_data = parse_stub_params(params)
45
+ app, _ = request_params[:captures][:path]
46
+ with_mock_app(mock_data, app) do |app_data|
47
+ {
48
+ :body => Azuki::API::OkJson.encode(mock_data[:domains][app]),
49
+ :status => 200
50
+ }
51
+ end
52
+ end
53
+
54
+ # stub POST /apps/:app/domains
55
+ Excon.stub(:expects => 201, :method => :post, :path => %r{^/apps/([^/]+)/domains$} ) do |params|
56
+ request_params, mock_data = parse_stub_params(params)
57
+ app, _ = request_params[:captures][:path]
58
+ domain = request_params[:query]['domain_name[domain]']
59
+ with_mock_app(mock_data, app) do |app_data|
60
+ unless get_mock_app_domain(mock_data, app, domain)
61
+ mock_data[:domains][app] << {
62
+ 'app_id' => app_data['id'],
63
+ 'base_domain' => domain.split('.')[-2..-1].join('.'),
64
+ 'created_at' => timestamp,
65
+ 'domain' => domain,
66
+ 'default' => nil,
67
+ 'id' => rand(999999),
68
+ 'updated_at' => timestamp
69
+ }
70
+ end
71
+ {
72
+ :body => Azuki::API::OkJson.encode('domain' => domain),
73
+ :status => 201
74
+ }
75
+ end
76
+ end
77
+
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,120 @@
1
+ module Azuki
2
+ class API
3
+ module Mock
4
+
5
+ # stub DELETE /features/:feature
6
+ Excon.stub(:expects => 200, :method => :delete, :path => %r{^/features/([^/]+)$}) do |params|
7
+ request_params, mock_data = parse_stub_params(params)
8
+ app = request_params[:query].has_key?('app') && request_params[:query]['app']
9
+ feature, _ = request_params[:captures][:path]
10
+ if !app || get_mock_app(mock_data, app)
11
+ # app found
12
+ if feature_data = get_mock_feature(mock_data, feature)
13
+ feature_data = feature_data.merge('enabled' => true)
14
+ # feature exists
15
+ case feature_data['kind']
16
+ when 'app'
17
+ mock_data[:features][:app][app].delete(feature_data)
18
+ {
19
+ :body => Azuki::API::OkJson.encode(feature_data.merge('enabled' => false)),
20
+ :status => 200
21
+ }
22
+ when 'user'
23
+ mock_data[:features][:user].delete(feature_data.merge('enabled' => false))
24
+ {
25
+ :body => Azuki::API::OkJson.encode(feature_data),
26
+ :status => 200
27
+ }
28
+ end
29
+ else
30
+ # feature does not exist
31
+ {
32
+ :body => Azuki::API::OkJson.encode({'error' => "Feature not found."}),
33
+ :status => 404
34
+ }
35
+ end
36
+ else
37
+ {
38
+ :body => 'Feature not enabled.',
39
+ :status => 422
40
+ }
41
+ end
42
+ end
43
+
44
+ # stub GET /features
45
+ Excon.stub(:expects => 200, :method => :get, :path => %r|^/features$|) do |params|
46
+ request_params, mock_data = parse_stub_params(params)
47
+ {
48
+ :body => File.read("#{File.dirname(__FILE__)}/cache/get_features.json"),
49
+ :status => 200
50
+ }
51
+ end
52
+
53
+ # stub GET /features/:feature
54
+ Excon.stub(:expects => 200, :method => :get, :path => %r{^/features/([^/]+)}) do |params|
55
+ request_params, mock_data = parse_stub_params(params)
56
+ feature, _ = request_params[:captures][:path]
57
+ if feature_data = get_mock_feature(mock_data, feature)
58
+ {
59
+ :body => Azuki::API::OkJson.encode(feature_data),
60
+ :status => 200
61
+ }
62
+ else
63
+ # feature does not exist
64
+ {
65
+ :body => Azuki::API::OkJson.encode({'error' => "Feature not found."}),
66
+ :status => 404
67
+ }
68
+ end
69
+ end
70
+
71
+ # stub POST /features/:feature
72
+ Excon.stub(:expects => [200, 201], :method => :post, :path => %r{^/features/([^/]+)$}) do |params|
73
+ request_params, mock_data = parse_stub_params(params)
74
+ app = request_params[:query].has_key?('app') && request_params[:query]['app']
75
+ feature, _ = request_params[:captures][:path]
76
+ if !app || get_mock_app(mock_data, app)
77
+ # app found
78
+ if feature_data = get_mock_feature(mock_data, feature)
79
+ feature_data = feature_data.merge('enabled' => true)
80
+ # feature exists
81
+ case feature_data['kind']
82
+ when 'app'
83
+ status = if mock_data[:features][:app][app].include?(feature_data)
84
+ 200
85
+ else
86
+ mock_data[:features][:app][app] << feature_data
87
+ 201
88
+ end
89
+ {
90
+ :body => Azuki::API::OkJson.encode(feature_data),
91
+ :status => status
92
+ }
93
+ when 'user'
94
+ status = if mock_data[:features][:user].include(feature_data)
95
+ 200
96
+ else
97
+ mock_data[:features][:user] << feature_data
98
+ 201
99
+ end
100
+ {
101
+ :body => '',
102
+ :status => status
103
+ }
104
+ end
105
+ else
106
+ # feature does not exist
107
+ {
108
+ :body => Azuki::API::OkJson.encode({'error' => "Feature not found."}),
109
+ :status => 404
110
+ }
111
+ end
112
+ else
113
+ # app not found
114
+ Azuki::API::Mock::APP_NOT_FOUND
115
+ end
116
+ end
117
+
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,46 @@
1
+ module Azuki
2
+ class API
3
+ module Mock
4
+
5
+ # stub DELETE /user/keys/:key
6
+ Excon.stub(:expects => 200, :method => :delete, :path => %r{^/user/keys/([^/]+)}) do |params|
7
+ request_params, mock_data = parse_stub_params(params)
8
+ key, _ = request_params[:captures][:path]
9
+ key = unescape(key)
10
+ if key_data = get_mock_key(mock_data, key)
11
+ mock_data[:keys].delete(key_data)
12
+ { :status => 200 }
13
+ else
14
+ { :body => "Key not found.", :status => 404 }
15
+ end
16
+ end
17
+
18
+ # stub DELETE /user/keys
19
+ Excon.stub(:expects => 200, :method => :delete, :path => %r{^/user/keys$}) do |params|
20
+ request_params, mock_data = parse_stub_params(params)
21
+ mock_data[:keys] = []
22
+ { :status => 200 }
23
+ end
24
+
25
+ # stub GET /user/keys
26
+ Excon.stub(:expects => 200, :method => :get, :path => %r{^/user/keys}) do |params|
27
+ request_params, mock_data = parse_stub_params(params)
28
+ {
29
+ :body => Azuki::API::OkJson.encode(mock_data[:keys]),
30
+ :status => 200
31
+ }
32
+ end
33
+
34
+ # stub POST /user/keys
35
+ Excon.stub(:expects => 200, :method => :post, :path => %r{^/user/keys}) do |params|
36
+ request_params, mock_data = parse_stub_params(params)
37
+ mock_data[:keys] |= [{
38
+ 'email' => 'email@example.com',
39
+ 'contents' => request_params[:body]
40
+ }]
41
+ { :status => 200 }
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,22 @@
1
+ module Azuki
2
+ class API
3
+ module Mock
4
+
5
+ # stub POST /login
6
+ Excon.stub(:expects => 200, :method => :post, :path => '/login') do |params|
7
+ request_params, mock_data = parse_stub_params(params)
8
+ {
9
+ :body => {
10
+ 'api_key' => SecureRandom.hex(20),
11
+ 'email' => 'email@example.com',
12
+ 'id' => '123456@users.azukiapp.com',
13
+ 'verified' => true,
14
+ 'verified_at' => timestamp
15
+ },
16
+ :status => 200
17
+ }
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ module Azuki
2
+ class API
3
+ module Mock
4
+
5
+ # stub GET /apps/:app/logs
6
+ Excon.stub(:expects => 200, :method => :get, :path => %r{^/apps/([^/]+)/logs}) do |params|
7
+ request_params, mock_data = parse_stub_params(params)
8
+ app, _ = request_params[:captures][:path]
9
+ with_mock_app(mock_data, app) do
10
+ uuid = [SecureRandom.hex(4), SecureRandom.hex(2), SecureRandom.hex(2), SecureRandom.hex(2), SecureRandom.hex(6)].join('-')
11
+ {
12
+ :body => "https://logplex.azukiapp.com/sessions/#{uuid}?srv=#{Time.now.to_i}",
13
+ :status => 200
14
+ }
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,198 @@
1
+ module Azuki
2
+ class API
3
+ module Mock
4
+
5
+ # stub GET /apps/:app/ps
6
+ Excon.stub(:expects => 200, :method => :get, :path => %r{^/apps/([^/]+)/ps}) do |params|
7
+ request_params, mock_data = parse_stub_params(params)
8
+ app, _ = request_params[:captures][:path]
9
+ with_mock_app(mock_data, app) do |app_data|
10
+ {
11
+ :body => Azuki::API::OkJson.encode(get_mock_processes(mock_data, app)),
12
+ :status => 200
13
+ }
14
+ end
15
+ end
16
+
17
+ # stub POST /apps/:app/ps
18
+ Excon.stub(:expects => 200, :method => :post, :path => %r{^/apps/([^/]+)/ps\/?$}) do |params|
19
+ request_params, mock_data = parse_stub_params(params)
20
+ app, _ = request_params[:captures][:path]
21
+ with_mock_app(mock_data, app) do |app_data|
22
+ unless attached = request_params[:query].has_key?('attach') && request_params[:query]['attach'].to_s == 'true'
23
+ type = 'Ps'
24
+ end
25
+ command = request_params[:query].has_key?('command') && request_params[:query]['command']
26
+ rendezvous_url = if attached
27
+ "s1.runtime.azukiapp.com:5000/#{SecureRandom.hex(32)}"
28
+ end
29
+ max_run_id = mock_data[:ps][app].map { |process| process['process'].split('run.').last.to_i}.max
30
+ data = {
31
+ 'action' => 'complete',
32
+ 'attached' => attached,
33
+ 'command' => command,
34
+ 'elapsed' => 0,
35
+ 'pretty_state' => 'completed for 0s',
36
+ 'process' => "run.#{max_run_id + 1}",
37
+ 'rendezvous_url' => rendezvous_url,
38
+ 'slug' => 'NONE',
39
+ 'state' => 'created',
40
+ 'transitioned_at' => timestamp,
41
+ 'type' => type,
42
+ 'upid' => rand(99999999).to_s
43
+ }
44
+ mock_data[:ps][app] << data
45
+ {
46
+ :body => Azuki::API::OkJson.encode(data),
47
+ :status => 200,
48
+ }
49
+ end
50
+ end
51
+
52
+ # stub POST /apps/:app/ps/restart
53
+ Excon.stub(:expects => 200, :method => :post, :path => %r{^/apps/([^/]+)/ps/restart}) do |params|
54
+ request_params, mock_data = parse_stub_params(params)
55
+ app, _ = request_params[:captures][:path]
56
+ with_mock_app(mock_data, app) do |app_data|
57
+ ps = request_params[:query].has_key?('ps') && request_params[:query]['ps']
58
+ type = request_params[:query].has_key?('type') && request_params[:query]['type']
59
+ if !ps && !type
60
+ mock_data[:ps][app].select {|process| process['state'] != 'complete'}.each do |process|
61
+ process['transitioned_at'] = timestamp
62
+ end
63
+ elsif ps
64
+ mock_data[:ps][app].select {|process| process['process'] == 'ps' && process['state'] != 'complete'}.each do |process|
65
+ process['transitioned_at'] = timestamp
66
+ end
67
+ elsif type
68
+ mock_data[:ps][app].select {|process| process['process'] =~ %r{^#{type}\.\d+} && process['state'] != 'complete'}.each do |process|
69
+ process['transitioned_at'] = timestamp
70
+ end
71
+ end
72
+ {
73
+ :body => 'ok',
74
+ :status => 200
75
+ }
76
+ end
77
+ end
78
+
79
+ # stub POST /apps/:app/ps/scale
80
+ Excon.stub(:expects => 200, :method => :post, :path => %r{^/apps/([^/]+)/ps/scale}) do |params|
81
+ request_params, mock_data = parse_stub_params(params)
82
+ app, _ = request_params[:captures][:path]
83
+ with_mock_app(mock_data, app) do |app_data|
84
+ type = request_params[:query].has_key?('type') && request_params[:query]['type']
85
+ qty = request_params[:query].has_key?('qty') && request_params[:query]['qty']
86
+ if app_data['stack'] == 'cedar'
87
+ if type == 'web'
88
+ current_qty = mock_data[:ps][app].count {|process| process['process'] =~ %r{^web\.\d+}}
89
+
90
+ new_qty = case qty
91
+ when /[+-]\d+/
92
+ current_qty + qty.to_i
93
+ else
94
+ qty.to_i
95
+ end
96
+
97
+ if new_qty >= current_qty
98
+ (new_qty - current_qty).times do
99
+ max_web_id = mock_data[:ps][app].map {|process| process['process'].split('web.').last.to_i}.max
100
+ data = mock_data[:ps][app].first.merge({
101
+ 'process' => "web.#{max_web_id + 1}",
102
+ 'transitioned_at' => timestamp,
103
+ 'upid' => rand(99999999).to_s
104
+ })
105
+ mock_data[:ps][app] << data
106
+ end
107
+ elsif new_qty < current_qty
108
+ (current_qty - new_qty).times do
109
+ max_web_id = mock_data[:ps][app].map {|process| process['process'].split('web.').last.to_i}.max
110
+ process = mock_data[:ps][app].detect {|process| process['process'] == "web.#{max_web_id}"}
111
+ mock_data[:ps][app].delete(process)
112
+ end
113
+ end
114
+ {
115
+ :body => new_qty.to_s,
116
+ :status => 200
117
+ }
118
+ else
119
+ {
120
+ :body => Azuki::API::OkJson.encode('error' => "No such type as #{type}") ,
121
+ :status => 422
122
+ }
123
+ end
124
+ else
125
+ {
126
+ :body => Azuki::API::OkJson.encode('error' => "That feature is not available on this app's stack"),
127
+ :status => 422
128
+ }
129
+ end
130
+ end
131
+ end
132
+
133
+ # stub POST /apps/:app/ps/stop
134
+ Excon.stub(:expects => 200, :method => :post, :path => %r{^/apps/([^/]+)/ps/stop}) do |params|
135
+ request_params, mock_data = parse_stub_params(params)
136
+ app, _ = request_params[:captures][:path]
137
+ with_mock_app(mock_data, app) do |app_data|
138
+ ps = request_params[:query].has_key?('ps') && request_params[:query]['ps']
139
+ type = request_params[:query].has_key?('type') && request_params[:query]['type']
140
+ if !ps && !type
141
+ {
142
+ :body => Azuki::API::OkJson.encode({'error' => 'Missing process argument'}),
143
+ :status => 422
144
+ }
145
+ else
146
+ {
147
+ :body => 'ok',
148
+ :status => 200
149
+ }
150
+ end
151
+ end
152
+ end
153
+
154
+ # stub PUT /apps/:app/dynos
155
+ Excon.stub(:expects => 200, :method => :put, :path => %r{^/apps/([^/]+)/dynos}) do |params|
156
+ request_params, mock_data = parse_stub_params(params)
157
+ app, _ = request_params[:captures][:path]
158
+ with_mock_app(mock_data, app) do |app_data|
159
+ dynos = request_params[:query].has_key?('dynos') && request_params[:query]['dynos'].to_i
160
+ unless app_data['stack'] == 'cedar'
161
+ app_data['dynos'] = dynos
162
+ {
163
+ :body => Azuki::API::OkJson.encode({'name' => app, 'dynos' => dynos}),
164
+ :status => 200
165
+ }
166
+ else
167
+ {
168
+ :body => Azuki::API::OkJson.encode({'error' => "For Cedar apps, use `azuki scale web=#{dynos}`"}),
169
+ :status => 422
170
+ }
171
+ end
172
+ end
173
+ end
174
+
175
+ # stub PUT /apps/:app/workers
176
+ Excon.stub(:expects => 200, :method => :put, :path => %r{^/apps/([^/]+)/workers}) do |params|
177
+ request_params, mock_data = parse_stub_params(params)
178
+ app, _ = request_params[:captures][:path]
179
+ with_mock_app(mock_data, app) do |app_data|
180
+ workers = request_params[:query].has_key?('workers') && request_params[:query]['workers'].to_i
181
+ unless app_data['stack'] == 'cedar'
182
+ app_data['workers'] = workers
183
+ {
184
+ :body => Azuki::API::OkJson.encode({'name' => app, 'workers' => workers}),
185
+ :status => 200
186
+ }
187
+ else
188
+ {
189
+ :body => Azuki::API::OkJson.encode({'error' => "For Cedar apps, use `azuki scale worker=#{workers}`"}),
190
+ :status => 422
191
+ }
192
+ end
193
+ end
194
+ end
195
+
196
+ end
197
+ end
198
+ end