xplenty-api 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +20 -0
- data/.travis.yml +11 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +20 -0
- data/LICENSE +21 -0
- data/README.md +139 -0
- data/Rakefile +29 -0
- data/changelog.txt +0 -0
- data/lib/xplenty-api.rb +3 -0
- data/lib/xplenty/api.rb +107 -0
- data/lib/xplenty/api/cluster_plans.rb +12 -0
- data/lib/xplenty/api/clusters.rb +46 -0
- data/lib/xplenty/api/errors.rb +32 -0
- data/lib/xplenty/api/jobs.rb +53 -0
- data/lib/xplenty/api/mock.rb +87 -0
- data/lib/xplenty/api/mock/cache/clusters.json +1 -0
- data/lib/xplenty/api/mock/cache/jobs.json +1 -0
- data/lib/xplenty/api/mock/clusters.rb +86 -0
- data/lib/xplenty/api/mock/jobs.rb +86 -0
- data/lib/xplenty/api/vendor/okjson.rb +600 -0
- data/lib/xplenty/api/version.rb +5 -0
- data/lib/xplenty/api/watchers.rb +57 -0
- data/test/test_cluster_watchers.rb +31 -0
- data/test/test_clusters.rb +27 -0
- data/test/test_error_conditions.rb +11 -0
- data/test/test_helper.rb +32 -0
- data/test/test_job_watchers.rb +34 -0
- data/test/test_jobs.rb +34 -0
- data/xplenty.gemspec +23 -0
- metadata +128 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
xplenty-api (0.0.1)
|
5
|
+
excon (~> 0.20.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
excon (0.20.1)
|
11
|
+
minitest (4.7.4)
|
12
|
+
rake (10.0.4)
|
13
|
+
|
14
|
+
PLATFORMS
|
15
|
+
ruby
|
16
|
+
|
17
|
+
DEPENDENCIES
|
18
|
+
minitest
|
19
|
+
rake
|
20
|
+
xplenty-api!
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2012-2013 Xplenty, Ltd. http://www.xplenty.com
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
## Xplenty Ruby Wrapper
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/xplenty/xplenty.rb.png)](https://travis-ci.org/xplenty/xplenty.rb)
|
4
|
+
|
5
|
+
The Xplenty rb is a Ruby artifact that provides a simple wrapper for the [Xplenty REST Api](https://github.com/xplenty/xplenty-api-doc).
|
6
|
+
To use it, create an TODO:OBJECT-NAME object and call its methods to access the Xplenty API.
|
7
|
+
This page describes the available XplentyAPI methods.
|
8
|
+
|
9
|
+
### Create an XplentyAPI Object
|
10
|
+
|
11
|
+
Pass your account ID and API key to the XplentyAPI constructor.
|
12
|
+
```ruby
|
13
|
+
@xplenty = Xplenty::API.new(:api_key => 'your_api_key', :account_id => 'your_account_id')
|
14
|
+
```
|
15
|
+
If you want to supply custom values for the version, scheme or host that the Xplenty::API object will use,
|
16
|
+
you can use Xplenty::API builder methods to customize these properties.
|
17
|
+
```ruby
|
18
|
+
@xplenty = Xplenty::API.new(:api_key => 'your_api_key', :account_id => 'your_account_id', :scheme => 'https', :host => 'api.xplenty.com')
|
19
|
+
@xplenty = Xplenty::API.new(:api_key => 'your_api_key', :account_id => 'your_account_id', :headers => { 'User-Agent' => 'your user agent' })
|
20
|
+
TODO:INSERT-CODE
|
21
|
+
```
|
22
|
+
### List the Cluster Plans
|
23
|
+
|
24
|
+
A cluster plan is a definition of a cluster type, which includes the number of nodes in the cluster and its pricing. Cluster plan details can be viewed in the Xplenty web application.
|
25
|
+
After you've determined which cluster plan is appropriate for your needs, use this method to retrieve the cluster plan ID. The cluster plan ID can then be used when creating a new cluster.
|
26
|
+
```ruby
|
27
|
+
@xplenty.cluster_plans
|
28
|
+
```
|
29
|
+
### Create a Cluster
|
30
|
+
|
31
|
+
This method creates a new cluster. A cluster is a group of machines ("nodes") allocated to your account. The number of nodes in the cluster is determined by the "plan_id" value that you supply to the call. While the cluster is active, only your account's users can run jobs on the cluster.
|
32
|
+
You will need to provide an active cluster when starting a new job. Save the cluster ID value returned in the response "id" field. You will use the value to refer to this cluster in subsequent API calls.
|
33
|
+
```ruby
|
34
|
+
@xplenty.create_cluster(:nodes => 1, :type => "sandbox")
|
35
|
+
```
|
36
|
+
### List All Clusters
|
37
|
+
|
38
|
+
This method returns the list of clusters that were created by users in your account.
|
39
|
+
You can use this information to monitor and display your clusters and their statuses.
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
@xplenty.clusters # return all clusters
|
43
|
+
```
|
44
|
+
XplentyAPI.listClusters() is shorthand for XplentyAPI.listClusters(new Properties()), which returns all clusters with no filtering.
|
45
|
+
You can also pass property parameters which filter the clusters according to their status, and determine the order in which they'll be sorted.
|
46
|
+
Only clusters which have the status passed in the PARAMETER_STATUS property will be returned, sorted according to the field passed in PARAMETER_SORT,
|
47
|
+
in ascending or descending order according to the PARAMETER_DIRECTION property.
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
@xplenty.clusters(:status => 'terminated')
|
51
|
+
@xplenty.clusters(:sort => 'created', :direction => 'desc')
|
52
|
+
```
|
53
|
+
### Get Cluster Information
|
54
|
+
|
55
|
+
This method returns the details of the cluster with the given ID.
|
56
|
+
```ruby
|
57
|
+
@xplenty.get_cluster_info(1792)
|
58
|
+
```
|
59
|
+
### Terminate a Cluster
|
60
|
+
|
61
|
+
This method deactivates the given cluster, releasing its resources and terminating its runtime period. Use this method when all of the cluster's jobs are completed and it's no longer needed. The method returns the given cluster's details, including a status of "pending_terminate".
|
62
|
+
```ruby
|
63
|
+
@xplenty.terminate_cluster(1792)
|
64
|
+
```
|
65
|
+
### Run a Job
|
66
|
+
|
67
|
+
This method creates a new job and triggers it to run. The job performs the series of data processing tasks that are defined in the job's package. Unless the job encounters an error or is terminated by the user, it will run until it completes its tasks on all of the input data. Save the job ID value returned in the response "id" field. You will use the value to refer to this job in subsequent API calls.
|
68
|
+
```ruby
|
69
|
+
@xplenty.run_job(1792, 902, {:first_arg => 'Argument 1'})
|
70
|
+
```
|
71
|
+
### List All Jobs
|
72
|
+
|
73
|
+
This method returns information for all the jobs that have been created under your account.
|
74
|
+
```ruby
|
75
|
+
@xplenty.jobs
|
76
|
+
```
|
77
|
+
XplentyAPI.listJobs() is shorthand for XplentyAPI.listJobs(new Properties()), which returns all jobs with no filtering.
|
78
|
+
You can also pass property parameters which filter the jobs according to their status, and determine the order in which they'll be sorted.
|
79
|
+
Only jobs which have the status passed in the PARAMETER_STATUS property will be returned, sorted according to the field passed in PARAMETER_SORT,
|
80
|
+
in ascending or descending order according to the PARAMETER_DIRECTION property.
|
81
|
+
```ruby
|
82
|
+
@xplenty.jobs(:status => 'idle')
|
83
|
+
@xplenty.jobs(:status => 'completed', :sort => 'created_at', :direction => 'asc')
|
84
|
+
```
|
85
|
+
|
86
|
+
### Get Job Information
|
87
|
+
|
88
|
+
This method retrieves information for a job, according to the given job ID.
|
89
|
+
```ruby
|
90
|
+
@xplenty.get_job_info(9032)
|
91
|
+
```
|
92
|
+
### Terminate a Job
|
93
|
+
|
94
|
+
This method terminates an active job. Usually it's unnecessary to request to terminate a job, because normally the job will end when its tasks are completed. You may want to actively terminate a job if you need its cluster resources for a more urgent job, or if the job is taking too long to complete.
|
95
|
+
```ruby
|
96
|
+
@xplenty.terminate_job(9032)
|
97
|
+
```
|
98
|
+
### Watch Cluster
|
99
|
+
|
100
|
+
You can watch a cluster that you or others have created. If you'r watching a cluster, you'll receive notifications (email messages or web notifications) on important updates.
|
101
|
+
|
102
|
+
This method adds the calling user as a watcher of the specifeid cluster.
|
103
|
+
```ruby
|
104
|
+
@xplenty.watch_cluster(1792)
|
105
|
+
```
|
106
|
+
### Stop Watching Cluster
|
107
|
+
|
108
|
+
This method removes the calling user from the watcher list of the specified cluster.
|
109
|
+
```ruby
|
110
|
+
@xplenty.stop_watching_cluster(1792)
|
111
|
+
```
|
112
|
+
### Get Cluster Watchers
|
113
|
+
|
114
|
+
This call retrieves the list of users watching the specified cluster.
|
115
|
+
```ruby
|
116
|
+
@xplenty.cluster_watchers(1792)
|
117
|
+
```
|
118
|
+
### Watch Job
|
119
|
+
|
120
|
+
You can watch a job that you or others have executed. If you'r watching a job, you'll receive notifications (email messages or web notifications) on important updates.
|
121
|
+
|
122
|
+
This method adds the calling user as a watcher of the specifeid job.
|
123
|
+
```ruby
|
124
|
+
@xplenty.watch_job(9032)
|
125
|
+
```
|
126
|
+
### Stop Watching Job
|
127
|
+
|
128
|
+
This method removes the calling user from the watcher list of the specified job.
|
129
|
+
```ruby
|
130
|
+
@xplenty.stop_watching_job(9032)
|
131
|
+
```
|
132
|
+
### Get Job Watchers
|
133
|
+
|
134
|
+
This call retrieves the list of users watching the specified job.
|
135
|
+
```ruby
|
136
|
+
@xplenty.job_watchers(9032)
|
137
|
+
```
|
138
|
+
## License
|
139
|
+
Released under the [MIT license](http://www.opensource.org/licenses/mit-license.php).
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
task :default => :test
|
6
|
+
|
7
|
+
Rake::TestTask.new do |task|
|
8
|
+
task.name = :test
|
9
|
+
task.test_files = FileList['test/test*.rb']
|
10
|
+
end
|
11
|
+
|
12
|
+
task :cache, [:account_id, :api_key] do |task, args|
|
13
|
+
unless args.api_key && args.account_id
|
14
|
+
puts('cache requires an account id and api key, please call as `cache[account_id api_key]`')
|
15
|
+
else
|
16
|
+
require "#{File.dirname(__FILE__)}/lib/xplenty/api"
|
17
|
+
xplenty = Xplenty::API.new(:api_key => args.api_key, :account_id => args.account_id)
|
18
|
+
|
19
|
+
clusters = Xplenty::API::OkJson.encode(xplenty.clusters.body)
|
20
|
+
File.open("#{File.dirname(__FILE__)}/lib/xplenty/api/mock/cache/clusters.json", 'w') do |file|
|
21
|
+
file.write(clusters)
|
22
|
+
end
|
23
|
+
|
24
|
+
jobs = Xplenty::API::OkJson.encode(xplenty.jobs.body)
|
25
|
+
File.open("#{File.dirname(__FILE__)}/lib/xplenty/api/mock/cache/jobs.json", 'w') do |file|
|
26
|
+
file.write(jobs)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/changelog.txt
ADDED
File without changes
|
data/lib/xplenty-api.rb
ADDED
data/lib/xplenty/api.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require "base64"
|
2
|
+
require "excon"
|
3
|
+
require "securerandom"
|
4
|
+
require "uri"
|
5
|
+
require "zlib"
|
6
|
+
|
7
|
+
__LIB_DIR__ = File.expand_path(File.join(File.dirname(__FILE__), ".."))
|
8
|
+
unless $LOAD_PATH.include?(__LIB_DIR__)
|
9
|
+
$LOAD_PATH.unshift(__LIB_DIR__)
|
10
|
+
end
|
11
|
+
|
12
|
+
require "xplenty/api/vendor/okjson"
|
13
|
+
|
14
|
+
require "xplenty/api/errors"
|
15
|
+
require "xplenty/api/mock"
|
16
|
+
require "xplenty/api/version"
|
17
|
+
|
18
|
+
require "xplenty/api/jobs"
|
19
|
+
require "xplenty/api/cluster_plans"
|
20
|
+
require "xplenty/api/clusters"
|
21
|
+
require "xplenty/api/watchers"
|
22
|
+
|
23
|
+
srand
|
24
|
+
|
25
|
+
module Xplenty
|
26
|
+
class API
|
27
|
+
|
28
|
+
HEADERS = {
|
29
|
+
'Accept' => 'application/vnd.xplenty+json'
|
30
|
+
}
|
31
|
+
|
32
|
+
OPTIONS = {
|
33
|
+
:headers => {},
|
34
|
+
:host => 'api.xplenty.com',
|
35
|
+
:nonblock => false,
|
36
|
+
:scheme => 'https'
|
37
|
+
}
|
38
|
+
|
39
|
+
def initialize(options={})
|
40
|
+
options = OPTIONS.merge(options)
|
41
|
+
|
42
|
+
@api_key = options.delete(:api_key) || ENV['XPLENTY_API_KEY']
|
43
|
+
@account_id = options.delete(:account_id) || ENV['XPLENTY_ACCOUNT_ID']
|
44
|
+
@version = options.delete(:version)
|
45
|
+
@mime_type = "application/vnd.xplenty+json"
|
46
|
+
if @version && @version.is_a?(Fixnum)
|
47
|
+
@mime_type = @mime_type << "; version=#{@version}"
|
48
|
+
end
|
49
|
+
user_name = "#{@api_key}"
|
50
|
+
options[:headers] = HEADERS.merge({
|
51
|
+
'Authorization' => "Basic #{Base64.encode64(user_name).gsub("\n", '')}",
|
52
|
+
'Accept' => @mime_type
|
53
|
+
}).merge(options[:headers])
|
54
|
+
|
55
|
+
@connection = Excon.new("#{options[:scheme]}://#{options[:host]}", options)
|
56
|
+
end
|
57
|
+
|
58
|
+
def request(params, &block)
|
59
|
+
begin
|
60
|
+
response = @connection.request(params, &block)
|
61
|
+
rescue Excon::Errors::HTTPStatusError => error
|
62
|
+
klass = case error.response.status
|
63
|
+
when 400 then Xplenty::API::Errors::BadRequest
|
64
|
+
when 401 then Xplenty::API::Errors::Unauthorized
|
65
|
+
when 402 then Xplenty::API::Errors::PaymentRequired
|
66
|
+
when 403 then Xplenty::API::Errors::Forbidden
|
67
|
+
when 404 then Xplenty::API::Errors::NotFound
|
68
|
+
when 406 then Xplenty::API::Errors::NotAcceptable
|
69
|
+
when 415 then Xplenty::API::Errors::UnsupportedMediaType
|
70
|
+
when 422 then Xplenty::API::Errors::UnprocessableEntity
|
71
|
+
when 429 then Xplenty::API::Errors::TooManyRequests
|
72
|
+
when 500 then Xplenty::API::Errors::RequestFailed
|
73
|
+
when 502 then Xplenty::API::Errors::BadGateway
|
74
|
+
when 503 then Xplenty::API::Errors::ServiceUnavailable
|
75
|
+
else Xplenty::API::Errors::ErrorWithResponse
|
76
|
+
end
|
77
|
+
|
78
|
+
reerror = klass.new(error.message, error.response)
|
79
|
+
reerror.set_backtrace(error.backtrace)
|
80
|
+
raise(reerror)
|
81
|
+
end
|
82
|
+
|
83
|
+
if response.body && !response.body.empty?
|
84
|
+
if response.headers['Content-Encoding'] == 'gzip'
|
85
|
+
response.body = Zlib::GzipReader.new(StringIO.new(response.body)).read
|
86
|
+
end
|
87
|
+
begin
|
88
|
+
response.body = Xplenty::API::OkJson.decode(response.body)
|
89
|
+
#rescue
|
90
|
+
# leave non-JSON body as is
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# reset (non-persistent) connection
|
95
|
+
@connection.reset
|
96
|
+
|
97
|
+
response
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def account_id
|
103
|
+
@account_id
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Xplenty
|
2
|
+
class API
|
3
|
+
# POST /<accountID>/api/clusters
|
4
|
+
def create_cluster(cluster_info)
|
5
|
+
cluster_info = cluster_info.inject({}) { |v, hv|
|
6
|
+
v.merge({"cluster[#{hv.first}]" => hv.last})
|
7
|
+
}
|
8
|
+
|
9
|
+
request(
|
10
|
+
:expects => 201,
|
11
|
+
:method => :post,
|
12
|
+
:path => "/#{account_id}/api/clusters",
|
13
|
+
:query => cluster_info
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
# GET /<accountID>/api/clusters/<clusterID>
|
18
|
+
def get_cluster_info(clusterID)
|
19
|
+
request(
|
20
|
+
:expects => 200,
|
21
|
+
:method => :get,
|
22
|
+
:path => "/#{account_id}/api/clusters/#{clusterID}"
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
# GET /<accountID>/api/clusters
|
27
|
+
def clusters(options={})
|
28
|
+
request(
|
29
|
+
:expects => 200,
|
30
|
+
:method => :get,
|
31
|
+
:path => "/#{account_id}/api/clusters",
|
32
|
+
:query => options
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
# DELETE /<accountID>/api/clusters/<clusterID>
|
37
|
+
def terminate_cluster(clusterID)
|
38
|
+
request(
|
39
|
+
:expects => 200,
|
40
|
+
:method => :delete,
|
41
|
+
:path => "/#{account_id}/api/clusters/#{clusterID}"
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Xplenty
|
2
|
+
class API
|
3
|
+
module Errors
|
4
|
+
class Error < StandardError; end
|
5
|
+
|
6
|
+
class ErrorWithResponse < Error
|
7
|
+
attr_reader :response
|
8
|
+
|
9
|
+
def initialize(message, response)
|
10
|
+
super message
|
11
|
+
@response = response
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class RequestFailed < ErrorWithResponse; end
|
16
|
+
class NotModified < ErrorWithResponse; end
|
17
|
+
class BadRequest < ErrorWithResponse; end
|
18
|
+
class Unauthorized < ErrorWithResponse; end
|
19
|
+
class PaymentRequired < ErrorWithResponse; end
|
20
|
+
class Forbidden < ErrorWithResponse; end
|
21
|
+
class NotFound < ErrorWithResponse; end
|
22
|
+
class NotAcceptable < ErrorWithResponse; end
|
23
|
+
class UnsupportedMediaType < ErrorWithResponse; end
|
24
|
+
class UnprocessableEntity < ErrorWithResponse; end
|
25
|
+
class TooManyRequests < ErrorWithResponse; end
|
26
|
+
class InternalServerError < ErrorWithResponse; end
|
27
|
+
class BadGateway < ErrorWithResponse; end
|
28
|
+
class ServiceUnavailable < ErrorWithResponse; end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|