chronos-api 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.travis.yml +7 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +164 -0
- data/Rakefile +12 -0
- data/bin/chronos +134 -0
- data/chronos-api.gemspec +30 -0
- data/lib/chronos.rb +95 -0
- data/lib/chronos/connection.rb +107 -0
- data/lib/chronos/error.rb +68 -0
- data/lib/chronos/version.rb +3 -0
- data/spec/chronos/chronos_spec.rb +119 -0
- data/spec/chronos/connection_spec.rb +40 -0
- data/spec/chronos/error_spec.rb +57 -0
- data/spec/fixtures/Chronos/_add/adds_a_job.yml +25 -0
- data/spec/fixtures/Chronos/_delete/deletes_a_job.yml +25 -0
- data/spec/fixtures/Chronos/_delete/raises_a_400_if_the_job_to_delete_doesn_t_exist.yml +25 -0
- data/spec/fixtures/Chronos/_delete_all/deletes_all_jobs.yml +25 -0
- data/spec/fixtures/Chronos/_list/lists_jobs.yml +28 -0
- data/spec/fixtures/Chronos/_start/raises_a_400_if_the_jobs_to_start_doesn_t_exist.yml +25 -0
- data/spec/fixtures/Chronos/_start/starts_a_job.yml +25 -0
- data/spec/fixtures/add.json +9 -0
- data/spec/spec_helper.rb +30 -0
- metadata +221 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c8b3bb07696860917d8bccddca5b914f7919887e
|
4
|
+
data.tar.gz: f7e52fdd59a659d95095cc3ec032129a1edc1dff
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5e7ea78dbb616f3511a943aa28dcafb9ac394906fbcfa352a01feaf0e25f32fde0bd01c5e0285d8369e8e8c1adbb7a9539883968b15c657617f0174724b531e9
|
7
|
+
data.tar.gz: e9ad14c088f26051c7348efc8c7492984bbb6d29bc11c7441f715870313c833a265fd7c4dabea59bab06a074bece3bd40ebe664a927bb955fa75b9a9163ae189
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Algolia
|
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.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
chronos-api
|
2
|
+
============
|
3
|
+
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/chronos-api.svg)](http://badge.fury.io/rb/chronos-api) [![travis-ci](https://travis-ci.org/algolia/chronos-api.png?branch=master)](https://travis-ci.org/algolia/chronos-api)
|
5
|
+
|
6
|
+
This gem provides a CLI and a simple REST API client for [Chronos](https://github.com/mesos/chronos).
|
7
|
+
|
8
|
+
Installation
|
9
|
+
------------
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'chronos-api', :require => 'chronos'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then run:
|
18
|
+
|
19
|
+
```shell
|
20
|
+
$ bundle install
|
21
|
+
```
|
22
|
+
|
23
|
+
Usage
|
24
|
+
-----
|
25
|
+
|
26
|
+
If you're running Chronos locally on port 4400, there is no setup to do in Ruby. If you're not or change the path or port, you'll have to point the gem to your socket or local/remote port. For example:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
Chronos.url = 'http://example.com:8080'
|
30
|
+
```
|
31
|
+
|
32
|
+
It's possible to use `ENV` variables to configure the endpoint as well:
|
33
|
+
|
34
|
+
```shell
|
35
|
+
$ CHRONOS_URL=http://remote.chronos.example.com:8080 irb
|
36
|
+
irb(main):001:0> require 'chronos'
|
37
|
+
=> true
|
38
|
+
irb(main):002:0> Chronos.url
|
39
|
+
=> "http://remote.chronos.example.com:8080"
|
40
|
+
```
|
41
|
+
|
42
|
+
## Authentification
|
43
|
+
|
44
|
+
You have two options to set authentification if your Chronos API requires it:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
Chronos.options = {:username => 'your-user-name', :password => 'your-secret-password'}
|
48
|
+
```
|
49
|
+
|
50
|
+
or
|
51
|
+
|
52
|
+
```shell
|
53
|
+
$ export CHRONOS_USER=your-user-name
|
54
|
+
$ export CHRONOS_PASSWORD=your-secret-password
|
55
|
+
$ irb
|
56
|
+
irb(main):001:0> require 'chronos'
|
57
|
+
=> true
|
58
|
+
irb(main):002:0> Chronos.options
|
59
|
+
=> {:username => "your-user-name", :password => "your-secret-password"}
|
60
|
+
```
|
61
|
+
|
62
|
+
or
|
63
|
+
|
64
|
+
```shell
|
65
|
+
$ chronos -c http://USERNAME:PASSWORD@HOST:PORT
|
66
|
+
```
|
67
|
+
|
68
|
+
## List
|
69
|
+
|
70
|
+
To list the current scheduled jobs:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
require 'chronos'
|
74
|
+
jobs = Chronos.list
|
75
|
+
```
|
76
|
+
|
77
|
+
or
|
78
|
+
|
79
|
+
```shell
|
80
|
+
$ chronos list
|
81
|
+
```
|
82
|
+
|
83
|
+
## Add a job
|
84
|
+
|
85
|
+
To add a new job:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
require 'chronos'
|
89
|
+
Chronos.add({
|
90
|
+
name: 'myjob',
|
91
|
+
schedule: 'R10/2012-10-01T05:52:00Z/PT2S',
|
92
|
+
epsilon: 'PT15M',
|
93
|
+
command: 'echo foobar',
|
94
|
+
owner: 'chronos@algolia.com',
|
95
|
+
async: false
|
96
|
+
})
|
97
|
+
```
|
98
|
+
|
99
|
+
or
|
100
|
+
|
101
|
+
```shell
|
102
|
+
$ chronos add --job /path/to/job/details.json
|
103
|
+
```
|
104
|
+
|
105
|
+
## Delete a job
|
106
|
+
|
107
|
+
To delete a job:
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
require 'chronos'
|
111
|
+
Chronos.delete('job_name')
|
112
|
+
```
|
113
|
+
|
114
|
+
or
|
115
|
+
|
116
|
+
```shell
|
117
|
+
$ chronos delete --name job_name
|
118
|
+
```
|
119
|
+
|
120
|
+
## Delete all jobs
|
121
|
+
|
122
|
+
To delete all jobs:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
require 'chronos'
|
126
|
+
Chronos.delete_all
|
127
|
+
```
|
128
|
+
|
129
|
+
or
|
130
|
+
|
131
|
+
```shell
|
132
|
+
$ chronos delete_all
|
133
|
+
```
|
134
|
+
|
135
|
+
## Manually start a job
|
136
|
+
|
137
|
+
To manually start a job:
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
require 'chronos'
|
141
|
+
Chronos.start('job_name')
|
142
|
+
```
|
143
|
+
|
144
|
+
or
|
145
|
+
|
146
|
+
```shell
|
147
|
+
$ chronos start --name job_name
|
148
|
+
```
|
149
|
+
|
150
|
+
Contributing
|
151
|
+
------------
|
152
|
+
|
153
|
+
Please fork and send pull request.
|
154
|
+
Make sure to have test cases for your changes.
|
155
|
+
|
156
|
+
Credits
|
157
|
+
-------
|
158
|
+
|
159
|
+
This gem has been highly inspired by [marathon-api](https://github.com/otto-de/marathon-api).
|
160
|
+
|
161
|
+
License
|
162
|
+
-------
|
163
|
+
|
164
|
+
This program is licensed under the MIT license. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
2
|
+
|
3
|
+
require 'rake'
|
4
|
+
require 'json'
|
5
|
+
require 'chronos'
|
6
|
+
require 'rspec/core/rake_task'
|
7
|
+
|
8
|
+
task :default => [:spec]
|
9
|
+
|
10
|
+
RSpec::Core::RakeTask.new do |t|
|
11
|
+
t.pattern = 'spec/**/*_spec.rb'
|
12
|
+
end
|
data/bin/chronos
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'chronos'))
|
4
|
+
require 'trollop'
|
5
|
+
require 'json'
|
6
|
+
require 'terminal-table'
|
7
|
+
|
8
|
+
## Sub commands
|
9
|
+
|
10
|
+
SUB_COMMANDS = %w[list delete delete_all start add] # TODO: delete_tasks add_dependency add_docker graph task]
|
11
|
+
|
12
|
+
# list
|
13
|
+
def subcmd_list(cmd_opts)
|
14
|
+
rows = []
|
15
|
+
Chronos.list.each do |job|
|
16
|
+
cmd = job['command'] || ''
|
17
|
+
cmd = "#{cmd[0..50]}..." if cmd.size > 50
|
18
|
+
rows << [ job['name'], job['owner'], cmd, job['cpus'], job['mem'], job['disk'], job['disabled'], job['schedule'] ]
|
19
|
+
end
|
20
|
+
puts Terminal::Table.new :headings => ['Name', 'Owner', 'Command', 'CPU', 'MEM', 'Disk', 'Disabled', 'Schedule'], :rows => rows
|
21
|
+
end
|
22
|
+
|
23
|
+
# add
|
24
|
+
def subcmd_add(cmd_opts)
|
25
|
+
job = JSON.parse(cmd_opts[:job] == '-' ? $stdin.read : File.read(cmd_opts[:job]))
|
26
|
+
Chronos.add(job)
|
27
|
+
end
|
28
|
+
|
29
|
+
# delete
|
30
|
+
def subcmd_delete(cmd_opts)
|
31
|
+
Chronos.delete(cmd_opts[:name])
|
32
|
+
end
|
33
|
+
|
34
|
+
# delete_all
|
35
|
+
def subcmd_delete_all(cmd_opts)
|
36
|
+
Chronos.delete_all
|
37
|
+
end
|
38
|
+
|
39
|
+
# start
|
40
|
+
def subcmd_start(cmd_opts)
|
41
|
+
Chronos.start(cmd_opts[:name])
|
42
|
+
end
|
43
|
+
|
44
|
+
## Commands
|
45
|
+
|
46
|
+
# parse global options
|
47
|
+
def parse_global_opts
|
48
|
+
global_opts = Trollop.options do
|
49
|
+
version Chronos::VERSION
|
50
|
+
banner <<-EOS
|
51
|
+
Usage: chronos [global options] [command] [options]
|
52
|
+
|
53
|
+
Available commands:
|
54
|
+
|
55
|
+
list List all jobs
|
56
|
+
delete Delete a job
|
57
|
+
delete_all Delete all jobs
|
58
|
+
start Start a job
|
59
|
+
add Add a new job
|
60
|
+
|
61
|
+
TODO:
|
62
|
+
#delete_tasks Delete all tasks of a job
|
63
|
+
#add_dependency Add a new job add_dependency
|
64
|
+
#add_docker Add a new docker job
|
65
|
+
#graph See the dependencies graph
|
66
|
+
#task Get a task status
|
67
|
+
|
68
|
+
Global options:
|
69
|
+
EOS
|
70
|
+
|
71
|
+
opt :url, 'Chronos host (default http://localhost:4400, or CHRONOS_URL)',
|
72
|
+
:short => '-c', :type => String
|
73
|
+
opt :username, 'User name to authenticate against Chronos (optional, default unset, or CHRONOS_USER).',
|
74
|
+
:short => '-u', :type => String
|
75
|
+
opt :password, 'Password to authenticate against Chronos (optional, default unset, or CHRONOS_PASSWORD).',
|
76
|
+
:short => '-p', :type => String
|
77
|
+
stop_on SUB_COMMANDS
|
78
|
+
end
|
79
|
+
return global_opts
|
80
|
+
end
|
81
|
+
|
82
|
+
# set global options to Marathon API
|
83
|
+
def set_global_opts(global_opts)
|
84
|
+
# Set client's URL
|
85
|
+
Chronos.url = global_opts[:url] if global_opts[:url]
|
86
|
+
global_opts.delete(:url)
|
87
|
+
# Set client's options
|
88
|
+
Chronos.options = global_opts if global_opts.size > 0
|
89
|
+
end
|
90
|
+
|
91
|
+
# get the subcommand
|
92
|
+
def parse_subcmd
|
93
|
+
cmd = ARGV.shift
|
94
|
+
return cmd
|
95
|
+
end
|
96
|
+
|
97
|
+
# parse subcommand specific options
|
98
|
+
def parse_subcmd_opts(cmd)
|
99
|
+
cmd_opts = case cmd
|
100
|
+
when 'add'
|
101
|
+
Trollop.options do
|
102
|
+
opt :job, 'A JSON formatted file to read job details from. (use - to read from stdin)', short: 'j', type: String, required: true
|
103
|
+
end
|
104
|
+
when 'delete'
|
105
|
+
Trollop.options do
|
106
|
+
opt :name, 'The name of the job to delete', short: 'n', type: String, required: true
|
107
|
+
end
|
108
|
+
when 'start'
|
109
|
+
Trollop.options do
|
110
|
+
opt :name, 'The name of the job to start', short: 'n', type: String, required: true
|
111
|
+
end
|
112
|
+
else
|
113
|
+
{}
|
114
|
+
end
|
115
|
+
|
116
|
+
return cmd_opts
|
117
|
+
end
|
118
|
+
|
119
|
+
# Run selected subcmd
|
120
|
+
def run_subcmd(cmd, cmd_opts)
|
121
|
+
if SUB_COMMANDS.include?(cmd)
|
122
|
+
send("subcmd_#{cmd}", cmd_opts)
|
123
|
+
else
|
124
|
+
Trollop.die "unknown subcommand #{cmd.inspect}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
global_opts = parse_global_opts
|
129
|
+
set_global_opts(global_opts)
|
130
|
+
|
131
|
+
cmd = parse_subcmd
|
132
|
+
cmd_opts = parse_subcmd_opts(cmd)
|
133
|
+
|
134
|
+
run_subcmd(cmd, cmd_opts)
|
data/chronos-api.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/chronos/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = "chronos-api"
|
6
|
+
gem.version = Chronos::VERSION
|
7
|
+
gem.authors = ["Algolia Team"]
|
8
|
+
gem.email = %w{support@algolia.com}
|
9
|
+
gem.description = %q{A simple REST client for the Chronos API}
|
10
|
+
gem.summary = %q{A simple REST client for the Chronos API}
|
11
|
+
gem.homepage = 'https://github.com/algolia/chronos-api'
|
12
|
+
gem.license = 'MIT'
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split($\)
|
15
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
gem.require_paths = %w{lib}
|
18
|
+
|
19
|
+
gem.add_dependency 'json'
|
20
|
+
gem.add_dependency 'httparty', '~> 0.11.0'
|
21
|
+
gem.add_dependency 'trollop', '~> 2.0.0'
|
22
|
+
gem.add_dependency 'terminal-table', '~> 1.4.3'
|
23
|
+
|
24
|
+
gem.add_development_dependency 'rake'
|
25
|
+
gem.add_development_dependency 'rspec', '~> 3.0'
|
26
|
+
gem.add_development_dependency 'rspec-its'
|
27
|
+
gem.add_development_dependency 'vcr', '>= 2.7.0'
|
28
|
+
gem.add_development_dependency 'webmock'
|
29
|
+
gem.add_development_dependency 'pry'
|
30
|
+
end
|
data/lib/chronos.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'rubygems/package'
|
2
|
+
require 'httparty'
|
3
|
+
require 'json'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
7
|
+
|
8
|
+
# The top-level module for this gem. It's purpose is to hold global
|
9
|
+
# configuration variables that are used as defaults in other classes.
|
10
|
+
module Chronos
|
11
|
+
|
12
|
+
attr_accessor :logger
|
13
|
+
|
14
|
+
require 'chronos/version'
|
15
|
+
require 'chronos/error'
|
16
|
+
require 'chronos/connection'
|
17
|
+
|
18
|
+
DEFAULT_URL = 'http://localhost:4400'
|
19
|
+
|
20
|
+
# Get the chronos url from environment
|
21
|
+
def env_url
|
22
|
+
ENV['CHRONOS_URL']
|
23
|
+
end
|
24
|
+
|
25
|
+
# Get marathon options from environment
|
26
|
+
def env_options
|
27
|
+
opts = {}
|
28
|
+
opts[:username] = ENV['CHRONOS_USER'] if ENV['CHRONOS_USER']
|
29
|
+
opts[:password] = ENV['CHRONOS_PASSWORD'] if ENV['CHRONOS_PASSWORD']
|
30
|
+
opts
|
31
|
+
end
|
32
|
+
|
33
|
+
# Get the marathon API URL
|
34
|
+
def url
|
35
|
+
@url ||= env_url || DEFAULT_URL
|
36
|
+
@url
|
37
|
+
end
|
38
|
+
|
39
|
+
# Get options for connecting to marathon API
|
40
|
+
def options
|
41
|
+
@options ||= env_options
|
42
|
+
end
|
43
|
+
|
44
|
+
# Set a new url
|
45
|
+
def url=(new_url)
|
46
|
+
@url = new_url
|
47
|
+
reset_connection!
|
48
|
+
end
|
49
|
+
|
50
|
+
# Set new options
|
51
|
+
def options=(new_options)
|
52
|
+
@options = env_options.merge(new_options || {})
|
53
|
+
reset_connection!
|
54
|
+
end
|
55
|
+
|
56
|
+
# Set a new connection
|
57
|
+
def connection
|
58
|
+
@connection ||= Connection.new(url, options)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Reset the connection
|
62
|
+
def reset_connection!
|
63
|
+
@connection = nil
|
64
|
+
end
|
65
|
+
|
66
|
+
# list jobs
|
67
|
+
def list
|
68
|
+
connection.get('/scheduler/jobs')
|
69
|
+
end
|
70
|
+
|
71
|
+
## add job
|
72
|
+
def add(job)
|
73
|
+
connection.post('/scheduler/iso8601', nil, body: job)
|
74
|
+
end
|
75
|
+
|
76
|
+
# delete job
|
77
|
+
def delete(name)
|
78
|
+
connection.delete("/scheduler/job/#{URI.encode name}")
|
79
|
+
end
|
80
|
+
|
81
|
+
# delete all jobs
|
82
|
+
def delete_all
|
83
|
+
connection.delete('/scheduler/jobs')
|
84
|
+
end
|
85
|
+
|
86
|
+
# start a job
|
87
|
+
def start(name)
|
88
|
+
connection.put("/scheduler/job/#{URI.encode name}")
|
89
|
+
end
|
90
|
+
|
91
|
+
module_function :connection, :env_options, :env_url, :logger, :logger=,
|
92
|
+
:options, :options=, :url, :url= ,:reset_connection!,
|
93
|
+
:list, :add, :delete, :delete_all, :start
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# This class represents a Chronos API Connection.
|
2
|
+
class Chronos::Connection
|
3
|
+
|
4
|
+
include Chronos::Error
|
5
|
+
include HTTParty
|
6
|
+
|
7
|
+
headers(
|
8
|
+
'Content-Type' => 'application/json',
|
9
|
+
'Accept' => 'application/json',
|
10
|
+
'User-Agent' => "Chronos-API #{Chronos::VERSION}"
|
11
|
+
)
|
12
|
+
|
13
|
+
default_timeout 5
|
14
|
+
maintain_method_across_redirects
|
15
|
+
|
16
|
+
attr_reader :url, :options
|
17
|
+
|
18
|
+
# Create a new API connection.
|
19
|
+
# ++url++: URL of the chronos API.
|
20
|
+
# ++options++: Hash with options for chronos API.
|
21
|
+
def initialize(url, options = {})
|
22
|
+
@url = url
|
23
|
+
@options = options
|
24
|
+
if @options[:username] and @options[:password]
|
25
|
+
@options[:basic_auth] = {
|
26
|
+
:username => @options[:username],
|
27
|
+
:password => @options[:password]
|
28
|
+
}
|
29
|
+
@options.delete(:username)
|
30
|
+
@options.delete(:password)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Delegate all HTTP methods to the #request.
|
35
|
+
[:get, :put, :post, :delete].each do |method|
|
36
|
+
define_method(method) { |*args, &block| request(method, *args) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
"Chronos::Connection { :url => #{url} :options => #{options} }"
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Create URL suffix for a hash of query parameters.
|
46
|
+
# URL escaping is done internally.
|
47
|
+
# ++query++: Hash of query parameters.
|
48
|
+
def query_params(query)
|
49
|
+
query = query.select { |k,v| !v.nil? }
|
50
|
+
URI.escape(query.map { |k,v| "#{k}=#{v}" }.join('&'))
|
51
|
+
end
|
52
|
+
|
53
|
+
# Create request object.
|
54
|
+
# ++http_method++: GET/POST/PUT/DELETE.
|
55
|
+
# ++path++: Relative path to connection's URL.
|
56
|
+
# ++query++: Optional query parameters.
|
57
|
+
# ++opts++: Optional options. Ex. opts[:body] is used for PUT/POST request.
|
58
|
+
def compile_request_params(http_method, path, query = nil, opts = nil)
|
59
|
+
query ||= {}
|
60
|
+
opts ||= {}
|
61
|
+
headers = opts.delete(:headers) || {}
|
62
|
+
opts[:body] = opts[:body].to_json unless opts[:body].nil?
|
63
|
+
{
|
64
|
+
:method => http_method,
|
65
|
+
:url => "#{@url}#{path}",
|
66
|
+
:query => query
|
67
|
+
}.merge(opts).reject { |_, v| v.nil? }
|
68
|
+
end
|
69
|
+
|
70
|
+
# Create full URL with query parameters.
|
71
|
+
# ++request++: hash containing :url and optional :query
|
72
|
+
def build_url(request)
|
73
|
+
url = URI.escape(request[:url])
|
74
|
+
if request[:query].size > 0
|
75
|
+
url += '?' + query_params(request[:query])
|
76
|
+
end
|
77
|
+
url
|
78
|
+
end
|
79
|
+
|
80
|
+
# Parse response or raise error.
|
81
|
+
# ++response++: response from HTTParty call.
|
82
|
+
def parse_response(response)
|
83
|
+
if response.success?
|
84
|
+
response.parsed_response
|
85
|
+
else
|
86
|
+
raise Chronos::Error.from_response(response)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Send a request to the server and parse response.
|
91
|
+
# ++http_method++: GET/POST/PUT/DELETE.
|
92
|
+
# ++path++: Relative path to connection's URL.
|
93
|
+
# ++query++: Optional query parameters.
|
94
|
+
# ++opts++: Optional options. Ex. opts[:body] is used for PUT/POST request.
|
95
|
+
def request(*args)
|
96
|
+
request = compile_request_params(*args)
|
97
|
+
url = build_url(request)
|
98
|
+
parse_response(self.class.send(request[:method], url, request))
|
99
|
+
rescue => e
|
100
|
+
if e.class == SocketError or e.class.name.start_with?('Errno::')
|
101
|
+
raise IOError, "HTTP call failed: #{e.message}"
|
102
|
+
else
|
103
|
+
raise e
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# This module holds the Errors for the gem.
|
2
|
+
module Chronos::Error
|
3
|
+
# The default error. It's never actually raised, but can be used to catch all
|
4
|
+
# gem-specific errors that are thrown as they all subclass from this.
|
5
|
+
class ChronosError < StandardError; end
|
6
|
+
|
7
|
+
# Raised when invalid arguments are passed to a method.
|
8
|
+
class ArgumentError < ChronosError; end
|
9
|
+
|
10
|
+
# Raised when a request returns a 400 or 422.
|
11
|
+
class ClientError < ChronosError; end
|
12
|
+
|
13
|
+
# Raised when a request returns a 404.
|
14
|
+
class NotFoundError < ChronosError; end
|
15
|
+
|
16
|
+
# Raised when there is an unexpected response code / body.
|
17
|
+
class UnexpectedResponseError < ChronosError; end
|
18
|
+
|
19
|
+
# Raised when a request times out.
|
20
|
+
class TimeoutError < ChronosError; end
|
21
|
+
|
22
|
+
# Raised when login fails.
|
23
|
+
class AuthenticationError < ChronosError; end
|
24
|
+
|
25
|
+
# Raised when an IO action fails.
|
26
|
+
class IOError < ChronosError; end
|
27
|
+
|
28
|
+
# Raise error specific to http response.
|
29
|
+
# ++response++: HTTParty response object.
|
30
|
+
def from_response(response)
|
31
|
+
error_class(response).new(error_message(response))
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# Get reponse code specific error class.
|
37
|
+
# ++response++: HTTParty response object.
|
38
|
+
def error_class(response)
|
39
|
+
case response.code
|
40
|
+
when 400
|
41
|
+
ClientError
|
42
|
+
when 422
|
43
|
+
ClientError
|
44
|
+
when 404
|
45
|
+
NotFoundError
|
46
|
+
else
|
47
|
+
UnexpectedResponseError
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Get response code from http response.
|
52
|
+
# ++response++: HTTParty response object.
|
53
|
+
def error_message(response)
|
54
|
+
body = response.parsed_response
|
55
|
+
if not body.is_a?(Hash)
|
56
|
+
body
|
57
|
+
elsif body['message']
|
58
|
+
body['message']
|
59
|
+
elsif body['errors']
|
60
|
+
body['errors']
|
61
|
+
else
|
62
|
+
body
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
module_function :error_class, :error_message, :from_response
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Chronos do
|
4
|
+
|
5
|
+
describe '.url=' do
|
6
|
+
subject { described_class }
|
7
|
+
|
8
|
+
it 'sets new url' do
|
9
|
+
described_class.url = 'http://foo'
|
10
|
+
expect(described_class.url).to eq('http://foo')
|
11
|
+
|
12
|
+
# reset connection after running this spec
|
13
|
+
described_class.url = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'resets connection' do
|
17
|
+
old_connection = described_class.connection
|
18
|
+
described_class.url = 'http://bar'
|
19
|
+
|
20
|
+
expect(described_class.connection).not_to be(old_connection)
|
21
|
+
|
22
|
+
# reset connection after running this spec
|
23
|
+
described_class.url = nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '.options=' do
|
28
|
+
subject { described_class }
|
29
|
+
|
30
|
+
it 'sets new options' do
|
31
|
+
described_class.options = {:foo => 'bar'}
|
32
|
+
expect(described_class.options).to eq({:foo => 'bar'})
|
33
|
+
|
34
|
+
# reset connection after running this spec
|
35
|
+
described_class.options = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'resets connection' do
|
39
|
+
old_connection = described_class.connection
|
40
|
+
described_class.options = {:foo => 'bar'}
|
41
|
+
|
42
|
+
expect(described_class.connection).not_to be(old_connection)
|
43
|
+
|
44
|
+
# reset connection after running this spec
|
45
|
+
described_class.options = nil
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'adds :basic_auth options for :username and :password' do
|
49
|
+
described_class.options = {:username => 'user', :password => 'password'}
|
50
|
+
expect(described_class.connection.options)
|
51
|
+
.to eq({:basic_auth => {:username => 'user', :password => 'password'}})
|
52
|
+
|
53
|
+
# reset connection after running this spec
|
54
|
+
described_class.options = nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '.list' do
|
59
|
+
subject { described_class }
|
60
|
+
|
61
|
+
it 'lists jobs', :vcr do
|
62
|
+
jobs = described_class.list
|
63
|
+
expect(jobs.size).to eq(1)
|
64
|
+
expect(jobs[0]['name']).to eq('SAMPLE_JOB1')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '.add' do
|
69
|
+
subject { described_class }
|
70
|
+
|
71
|
+
it 'adds a job', :vcr do
|
72
|
+
described_class.add({
|
73
|
+
schedule: 'R10/2012-10-01T05:52:00Z/PT2S',
|
74
|
+
name: 'SAMPLE_JOB1',
|
75
|
+
epsilon: 'PT15M',
|
76
|
+
command: 'echo FOO',
|
77
|
+
owner: 'chronos@algolia.com',
|
78
|
+
async: false
|
79
|
+
})
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '.delete' do
|
84
|
+
subject { described_class }
|
85
|
+
|
86
|
+
it 'deletes a job', :vcr do
|
87
|
+
described_class.delete('SAMPLE_JOB1')
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'raises a 400 if the job to delete doesn\'t exist', :vcr do
|
91
|
+
expect {
|
92
|
+
described_class.delete('doesnt_exist')
|
93
|
+
}.to raise_error(Chronos::Error::ClientError)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
describe '.delete_all' do
|
99
|
+
subject { described_class }
|
100
|
+
|
101
|
+
it 'deletes all jobs', :vcr do
|
102
|
+
described_class.delete_all
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe '.start' do
|
107
|
+
subject { described_class }
|
108
|
+
|
109
|
+
it 'starts a job', :vcr do
|
110
|
+
described_class.start('SAMPLE_JOB1')
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'raises a 400 if the jobs to start doesn\'t exist', :vcr do
|
114
|
+
expect {
|
115
|
+
described_class.start('doesnt_exist')
|
116
|
+
}.to raise_error(Chronos::Error::ClientError)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Chronos::Connection do
|
4
|
+
|
5
|
+
describe '#to_s' do
|
6
|
+
subject { described_class.new('http://foo:4400') }
|
7
|
+
|
8
|
+
let(:expected_string) do
|
9
|
+
"Chronos::Connection { :url => http://foo:4400 :options => {} }"
|
10
|
+
end
|
11
|
+
|
12
|
+
its(:to_s) { should == expected_string }
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#request' do
|
16
|
+
subject { described_class.new('http://foo.example.org:8080') }
|
17
|
+
|
18
|
+
it 'raises IOError on SocketError' do
|
19
|
+
allow(described_class).to receive(:send) { raise SocketError.new }
|
20
|
+
expect {
|
21
|
+
subject.get('/path')
|
22
|
+
}.to raise_error(Chronos::Error::IOError)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'raises IOError on Errno' do
|
26
|
+
allow(described_class).to receive(:send) { raise Errno::EINTR.new }
|
27
|
+
expect {
|
28
|
+
subject.get('/path')
|
29
|
+
}.to raise_error(Chronos::Error::IOError)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'raises original error when unknown' do
|
33
|
+
allow(described_class).to receive(:send) { raise RuntimeError.new }
|
34
|
+
expect {
|
35
|
+
subject.get('/path')
|
36
|
+
}.to raise_error(RuntimeError)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Chronos::Error do
|
4
|
+
|
5
|
+
describe '.error_class' do
|
6
|
+
subject { described_class }
|
7
|
+
|
8
|
+
it 'returns ClientError on 400' do
|
9
|
+
expect(subject.error_class(Net::HTTPResponse.new(1.1, 400, 'Client Error')))
|
10
|
+
.to be(Chronos::Error::ClientError)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'returns ClientError on 422' do
|
14
|
+
expect(subject.error_class(Net::HTTPResponse.new(1.1, 422, 'Client Error')))
|
15
|
+
.to be(Chronos::Error::ClientError)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'returns NotFoundError on 404' do
|
19
|
+
expect(subject.error_class(Net::HTTPResponse.new(1.1, 404, 'Not Found')))
|
20
|
+
.to be(Chronos::Error::NotFoundError)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'returns UnexpectedResponseError anything else' do
|
24
|
+
expect(subject.error_class(Net::HTTPResponse.new(1.1, 599, 'Whatever')))
|
25
|
+
.to be(Chronos::Error::UnexpectedResponseError)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '.error_message' do
|
30
|
+
subject { described_class }
|
31
|
+
|
32
|
+
it 'returns "message" from respose json' do
|
33
|
+
r = { 'message' => 'fooo' }
|
34
|
+
expect(r).to receive(:parsed_response) { r }
|
35
|
+
expect(subject.error_message(r)).to eq('fooo')
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'returns "errors" from respose json' do
|
39
|
+
r = { 'errors' => 'fooo' }
|
40
|
+
expect(r).to receive(:parsed_response) { r }
|
41
|
+
expect(subject.error_message(r)).to eq('fooo')
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'returns full hash from respose json, if keys are missing' do
|
45
|
+
r = { 'bars' => 'fooo' }
|
46
|
+
expect(r).to receive(:parsed_response) { r }
|
47
|
+
expect(subject.error_message(r)).to eq(r)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'returns full body if not a hash with "message"' do
|
51
|
+
r = 'fooo'
|
52
|
+
expect(r).to receive(:parsed_response) { r }
|
53
|
+
expect(subject.error_message(r)).to eq('fooo')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: http://localhost:4400/scheduler/iso8601
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: '{"schedule":"R10/2012-10-01T05:52:00Z/PT2S","name":"SAMPLE_JOB1","epsilon":"PT15M","command":"echo FOO","owner":"chronos@algolia.com","async":false}'
|
9
|
+
headers:
|
10
|
+
Content-Type:
|
11
|
+
- application/json
|
12
|
+
Accept:
|
13
|
+
- application/json
|
14
|
+
User-Agent:
|
15
|
+
- Chronos-API 0.0.1
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
body:
|
21
|
+
encoding: UTF-8
|
22
|
+
string: ""
|
23
|
+
http_version:
|
24
|
+
recorded_at: Fri, 03 Apr 2015 19:18:04 GMT
|
25
|
+
recorded_with: VCR 2.9.3
|
@@ -0,0 +1,25 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: delete
|
5
|
+
uri: http://localhost:4400/scheduler/job/SAMPLE_JOB1
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Content-Type:
|
11
|
+
- application/json
|
12
|
+
Accept:
|
13
|
+
- application/json
|
14
|
+
User-Agent:
|
15
|
+
- Chronos-API 0.0.1
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 204
|
19
|
+
message: OK
|
20
|
+
body:
|
21
|
+
encoding: UTF-8
|
22
|
+
string: ""
|
23
|
+
http_version:
|
24
|
+
recorded_at: Fri, 03 Apr 2015 19:29:16 GMT
|
25
|
+
recorded_with: VCR 2.9.3
|
@@ -0,0 +1,25 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: delete
|
5
|
+
uri: http://localhost:4400/scheduler/job/doesnt_exist
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Content-Type:
|
11
|
+
- application/json
|
12
|
+
Accept:
|
13
|
+
- application/json
|
14
|
+
User-Agent:
|
15
|
+
- Chronos-API 0.0.1
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 400
|
19
|
+
message: Bad Request
|
20
|
+
body:
|
21
|
+
encoding: UTF-8
|
22
|
+
string: "requirement failed: Job 'doesnt_exist' not found"
|
23
|
+
http_version:
|
24
|
+
recorded_at: Fri, 03 Apr 2015 19:41:52 GMT
|
25
|
+
recorded_with: VCR 2.9.3
|
@@ -0,0 +1,25 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: delete
|
5
|
+
uri: http://localhost:4400/scheduler/jobs
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Content-Type:
|
11
|
+
- application/json
|
12
|
+
Accept:
|
13
|
+
- application/json
|
14
|
+
User-Agent:
|
15
|
+
- Chronos-API 0.0.1
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 204
|
19
|
+
message: OK
|
20
|
+
body:
|
21
|
+
encoding: UTF-8
|
22
|
+
string: ""
|
23
|
+
http_version:
|
24
|
+
recorded_at: Fri, 03 Apr 2015 19:31:17 GMT
|
25
|
+
recorded_with: VCR 2.9.3
|
@@ -0,0 +1,28 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://localhost:4400/scheduler/jobs
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Content-Type:
|
11
|
+
- application/json
|
12
|
+
Accept:
|
13
|
+
- application/json
|
14
|
+
User-Agent:
|
15
|
+
- Chronos-API 1.1.0
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Content-Type:
|
22
|
+
- application/json
|
23
|
+
body:
|
24
|
+
encoding: UTF-8
|
25
|
+
string: '[{"name":"SAMPLE_JOB1","command":"echo FOO","shell":true,"epsilon":"PT15M","executor":"","executorFlags":"","retries":2,"owner":"chronos@algolia.com","async":false,"successCount":0,"errorCount":0,"lastSuccess":"","lastError":"","cpus":0.1,"disk":256.0,"mem":128.0,"disabled":false,"softError":false,"errorsSinceLastSuccess":0,"uris":[],"environmentVariables":[],"arguments":[],"highPriority":false,"runAsUser":"root","schedule":"R10/2012-10-01T05:52:00Z/PT2S","scheduleTimeZone":""}]'
|
26
|
+
http_version:
|
27
|
+
recorded_at: Fri, 03 Apr 2015 19:18:04 GMT
|
28
|
+
recorded_with: VCR 2.9.3
|
@@ -0,0 +1,25 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: put
|
5
|
+
uri: http://localhost:4400/scheduler/job/doesnt_exist
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Content-Type:
|
11
|
+
- application/json
|
12
|
+
Accept:
|
13
|
+
- application/json
|
14
|
+
User-Agent:
|
15
|
+
- Chronos-API 0.0.1
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 400
|
19
|
+
message: Bad Request
|
20
|
+
body:
|
21
|
+
encoding: UTF-8
|
22
|
+
string: ""
|
23
|
+
http_version:
|
24
|
+
recorded_at: Fri, 03 Apr 2015 19:44:16 GMT
|
25
|
+
recorded_with: VCR 2.9.3
|
@@ -0,0 +1,25 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: put
|
5
|
+
uri: http://localhost:4400/scheduler/job/SAMPLE_JOB1
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Content-Type:
|
11
|
+
- application/json
|
12
|
+
Accept:
|
13
|
+
- application/json
|
14
|
+
User-Agent:
|
15
|
+
- Chronos-API 0.0.1
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 204
|
19
|
+
message: OK
|
20
|
+
body:
|
21
|
+
encoding: UTF-8
|
22
|
+
string: ""
|
23
|
+
http_version:
|
24
|
+
recorded_at: Fri, 03 Apr 2015 19:32:45 GMT
|
25
|
+
recorded_with: VCR 2.9.3
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
require 'rspec/its'
|
5
|
+
require 'vcr'
|
6
|
+
require 'webmock'
|
7
|
+
require 'chronos'
|
8
|
+
|
9
|
+
VCR.configure do |c|
|
10
|
+
c.allow_http_connections_when_no_cassette = false
|
11
|
+
c.cassette_library_dir = "spec/fixtures"
|
12
|
+
#c.debug_logger = $stdout
|
13
|
+
c.hook_into :webmock
|
14
|
+
c.configure_rspec_metadata!
|
15
|
+
end
|
16
|
+
|
17
|
+
RSpec.shared_context "local paths" do
|
18
|
+
def project_dir
|
19
|
+
File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
24
|
+
|
25
|
+
RSpec.configure do |c|
|
26
|
+
c.mock_with :rspec
|
27
|
+
c.color = true
|
28
|
+
c.formatter = :documentation
|
29
|
+
c.tty = true
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,221 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: chronos-api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Algolia Team
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: json
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: httparty
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.11.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.11.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: trollop
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.0.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.0.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: terminal-table
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.4.3
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.4.3
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec-its
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: vcr
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 2.7.0
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 2.7.0
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: webmock
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: pry
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
description: A simple REST client for the Chronos API
|
154
|
+
email:
|
155
|
+
- support@algolia.com
|
156
|
+
executables:
|
157
|
+
- chronos
|
158
|
+
extensions: []
|
159
|
+
extra_rdoc_files: []
|
160
|
+
files:
|
161
|
+
- ".gitignore"
|
162
|
+
- ".travis.yml"
|
163
|
+
- Gemfile
|
164
|
+
- LICENSE
|
165
|
+
- README.md
|
166
|
+
- Rakefile
|
167
|
+
- bin/chronos
|
168
|
+
- chronos-api.gemspec
|
169
|
+
- lib/chronos.rb
|
170
|
+
- lib/chronos/connection.rb
|
171
|
+
- lib/chronos/error.rb
|
172
|
+
- lib/chronos/version.rb
|
173
|
+
- spec/chronos/chronos_spec.rb
|
174
|
+
- spec/chronos/connection_spec.rb
|
175
|
+
- spec/chronos/error_spec.rb
|
176
|
+
- spec/fixtures/Chronos/_add/adds_a_job.yml
|
177
|
+
- spec/fixtures/Chronos/_delete/deletes_a_job.yml
|
178
|
+
- spec/fixtures/Chronos/_delete/raises_a_400_if_the_job_to_delete_doesn_t_exist.yml
|
179
|
+
- spec/fixtures/Chronos/_delete_all/deletes_all_jobs.yml
|
180
|
+
- spec/fixtures/Chronos/_list/lists_jobs.yml
|
181
|
+
- spec/fixtures/Chronos/_start/raises_a_400_if_the_jobs_to_start_doesn_t_exist.yml
|
182
|
+
- spec/fixtures/Chronos/_start/starts_a_job.yml
|
183
|
+
- spec/fixtures/add.json
|
184
|
+
- spec/spec_helper.rb
|
185
|
+
homepage: https://github.com/algolia/chronos-api
|
186
|
+
licenses:
|
187
|
+
- MIT
|
188
|
+
metadata: {}
|
189
|
+
post_install_message:
|
190
|
+
rdoc_options: []
|
191
|
+
require_paths:
|
192
|
+
- lib
|
193
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
194
|
+
requirements:
|
195
|
+
- - ">="
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: '0'
|
198
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
199
|
+
requirements:
|
200
|
+
- - ">="
|
201
|
+
- !ruby/object:Gem::Version
|
202
|
+
version: '0'
|
203
|
+
requirements: []
|
204
|
+
rubyforge_project:
|
205
|
+
rubygems_version: 2.2.2
|
206
|
+
signing_key:
|
207
|
+
specification_version: 4
|
208
|
+
summary: A simple REST client for the Chronos API
|
209
|
+
test_files:
|
210
|
+
- spec/chronos/chronos_spec.rb
|
211
|
+
- spec/chronos/connection_spec.rb
|
212
|
+
- spec/chronos/error_spec.rb
|
213
|
+
- spec/fixtures/Chronos/_add/adds_a_job.yml
|
214
|
+
- spec/fixtures/Chronos/_delete/deletes_a_job.yml
|
215
|
+
- spec/fixtures/Chronos/_delete/raises_a_400_if_the_job_to_delete_doesn_t_exist.yml
|
216
|
+
- spec/fixtures/Chronos/_delete_all/deletes_all_jobs.yml
|
217
|
+
- spec/fixtures/Chronos/_list/lists_jobs.yml
|
218
|
+
- spec/fixtures/Chronos/_start/raises_a_400_if_the_jobs_to_start_doesn_t_exist.yml
|
219
|
+
- spec/fixtures/Chronos/_start/starts_a_job.yml
|
220
|
+
- spec/fixtures/add.json
|
221
|
+
- spec/spec_helper.rb
|