adstax-spark-job-manager 0.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cc80ab6b9302b75a919ebb704ea9aae8d9fdd4dd
4
+ data.tar.gz: ea6fec36162de3c22244ae096674e2d6c4de0f13
5
+ SHA512:
6
+ metadata.gz: 3c368582d4553c96e46b7e405cf4c82be59e2448c3a40a1e7f3dc923bba0b3dc8286029ebaae71a8b7350d8a1d20da193af1c2f3bcf95312f09f0bbd419c43c5
7
+ data.tar.gz: d7d00b40bbc6d4214805f3b39f0268ecaccf773e0891d5772f7ebbacdbea578275bc7821945821a2d246a5bec1342d2ef49e4b2b93ea5fd838aeadda7ace71d7
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ *.iml
16
+
17
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in adstax-spark-job-manager.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ This software is licensed under the Apache 2 license, quoted below.
2
+
3
+ Copyright 2016 ShiftForward, S.A. [http://www.shiftforward.eu]
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not
6
+ use this file except in compliance with the License. You may obtain a copy of
7
+ the License at
8
+
9
+ [http://www.apache.org/licenses/LICENSE-2.0]
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ License for the specific language governing permissions and limitations under
15
+ the License.
data/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # AdStax Spark Job Manager
2
+
3
+ The AdStax Spark Job Manager is a gem to manager Spark jobs running in an AdStax
4
+ cluster.
5
+
6
+ ## Installation
7
+
8
+ ### From RubyGems
9
+
10
+ Make sure you have [ruby][ruby-install] (at least v2.0.0) installed, and just
11
+ run:
12
+
13
+ $ gem install adstax-spark-job-manager
14
+
15
+ [ruby-install]: https://www.ruby-lang.org/en/documentation/installation/
16
+
17
+ ### From source
18
+
19
+ Clone this repo and build the gem:
20
+
21
+ $ git clone git://github.com/ShiftForward/adstax-spark-job-manager.git
22
+ $ gem build adstax-spark-job-manager.gemspec
23
+ $ gem install adstax-spark-job-manager-0.0.1.gem
24
+
25
+ ## Usage
26
+
27
+ The AdStax Spark Job Manager publishes an `adstax-spark-job-manager` binary,
28
+ which provides a set of utilities to submit, kill and query the status of Spark
29
+ jobs running on an AdStax cluster. See the help for the command (running it with
30
+ `-h`) for more details.
31
+
32
+ The methods available are `submit`, `kill`, `status` or `log`. To submit a job,
33
+ one has to provide the task with the `--adstax-host` parameter (pointing to
34
+ where the AdStax instance is running), a `--jar` parameter pointing to a bundled
35
+ jar with your application, all required dependencies, and which includes at
36
+ least one implementation of `eu.shiftforward.adstax.spark.SparkJob`. Note that
37
+ you don't need to bundle the `spark-core` dependency, as it will be provided at
38
+ runtime. The `--job` parameter should be the fully qualified name of the class
39
+ extending `eu.shiftforward.adstax.spark.SparkJob` and which is going to be used
40
+ as the Spark job to run. Everything following the required parameters will be
41
+ used as arguments for the `SparkJob`. For example, in order to submit the
42
+ `SparkPI` example, one can use the following command:
43
+
44
+ ```
45
+ $ adstax-spark-job-manager submit --adstax-host sample-adstax-instance.dev.adstax.io --jar http://s3.amazonaws.com/shiftforward-public/bin/spark/adstax-spark-examples-1.0.jar --job eu.shiftforward.adstax.spark.examples.SparkPi 100
46
+ ```
47
+
48
+ This command should return information about the submission, for example:
49
+
50
+ ```
51
+ {
52
+ "action" : "CreateSubmissionResponse",
53
+ "serverSparkVersion" : "2.0.0-SNAPSHOT",
54
+ "submissionId" : "driver-20160713161243-0002",
55
+ "success" : true
56
+ }
57
+ ```
58
+
59
+ You can now use the returned submission id to query the status of the job, as
60
+ well as list its standard output. In order to query the status of the job, use
61
+ the `status` command:
62
+
63
+ ```
64
+ $ adstax-spark-job-manager status --adstax-host sample-adstax-instance.dev.adstax.io --submission-id driver-20160713161243-0002
65
+ {
66
+ "action" : "SubmissionStatusResponse",
67
+ "driverState" : "FINISHED",
68
+ "message" : "task_id {\n value: \"driver-20160713161243-0002\"\n}\nstate: TASK_FINISHED\nmessage: \"Command exited with status 0\"\nslave_id {\n value: \"9f18159e-ebe9-4a70-89e1-9774adf2cdd6-S9\"\n}\ntimestamp: 1.468426400438861E9\nexecutor_id {\n value: \"driver-20160713161243-0002\"\n}\nsource: SOURCE_EXECUTOR\n11: \"A\\371\\330\\365+\\027Ds\\237\\243\\\"\\317\\276\\353\\363\\367\"\n13: \"\\n\\036\\022\\f10.0.174.173*\\016\\022\\f10.0.174.173\"\n",
69
+ "serverSparkVersion" : "2.0.0-SNAPSHOT",
70
+ "submissionId" : "driver-20160713161243-0002",
71
+ "success" : true
72
+ }
73
+ ```
74
+
75
+ The `log` command allows you to output the stdout and stderr of the job's
76
+ driver. You can hide the stderr with the `--hide-stderr` command and keep
77
+ tailing the output with the `--follow` command:
78
+
79
+ ```
80
+ $ adstax-spark-job-manager log --adstax-host sample-adstax-instance.dev.adstax.io --submission-id driver-20160713161243-0002 --hide-stderr --follow
81
+ Registered executor on ec2-54-87-240-29.compute-1.amazonaws.com
82
+ Starting task driver-20160713161243-0002
83
+ Forked command at 22260
84
+ sh -c 'cd spark-2*; bin/spark-submit --name eu.shiftforward.adstax.spark.SparkJobRunner --master mesos://zk://zk.sample-adstax-instance.dev.adstax.io:2181/mesos --driver-cores 1.0 --driver-memory 1024M --class eu.shiftforward.adstax.spark.SparkJobRunner --conf spark.driver.supervise=false --conf spark.app.name=eu.shiftforward.adstax.spark.SparkJobRunner --conf spark.es.port=49200 --conf spark.es.nodes=localhost --conf spark.mesos.coarse=false --conf spark.executor.uri=https://s3.amazonaws.com/shiftforward-public/bin/spark/spark-2.0.0-SNAPSHOT-bin-2.4.0.tgz ../adstax-spark-examples-1.0.jar --job eu.shiftforward.adstax.spark.examples.SparkPi 100'
85
+ Pi is roughly 3.1407
86
+ Command exited with status 0 (pid: 22260)
87
+ ```
88
+
89
+ The `kill` command allows you to cancel and kill an ongoing job. Killing already
90
+ finished jobs has no effect:
91
+
92
+ ```
93
+ $ adstax-spark-job-manager kill --adstax-host sample-adstax-instance.dev.adstax.io --submission-id driver-20160713161243-0002
94
+ {
95
+ "action" : "KillSubmissionResponse",
96
+ "message" : "Driver already terminated",
97
+ "serverSparkVersion" : "2.0.0-SNAPSHOT",
98
+ "submissionId" : "driver-20160713161243-0002",
99
+ "success" : false
100
+ }
101
+ ```
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task "publish" do
4
+ gem_helper = Bundler::GemHelper.instance
5
+ built_gem_path = gem_helper.build_gem
6
+ Process.wait spawn("gem nexus #{built_gem_path}")
7
+ Bundler.ui.confirm "#{gem_helper.gemspec.name} (#{gem_helper.gemspec.version}) pushed to Nexus."
8
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "adstax-spark-job-manager"
7
+ spec.version = "0.1.0"
8
+ spec.authors = ["ShiftForward"]
9
+ spec.email = ["info@shiftforward.eu"]
10
+ spec.summary = "Manage Spark jobs running on an AdStax cluster."
11
+ spec.description = "Allow submitting, querying the status, outputting the log and killing Spark jobs on an AdStax cluster."
12
+
13
+ spec.licenses = ['Apache-2.0']
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.required_ruby_version = '>= 2.0.0'
20
+
21
+ spec.add_runtime_dependency "file-tail", "~> 1.1"
22
+ spec.add_runtime_dependency "json", "~> 1.8"
23
+ spec.add_runtime_dependency "colorize", "~> 0.7"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.7"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ end
@@ -0,0 +1,325 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'colorize'
4
+ require 'file-tail'
5
+ require 'json'
6
+ require 'net/http'
7
+ require 'optparse'
8
+ require 'tempfile'
9
+
10
+ # -----------------
11
+ # Constants
12
+ # -----------------
13
+
14
+ MAIN_CLASS = 'eu.shiftforward.adstax.spark.SparkJobRunner'
15
+ SPARK_EXECUTOR_URI = 'https://s3.amazonaws.com/shiftforward-public/bin/spark/spark-2.0.0-SNAPSHOT-bin-2.4.0.tgz'
16
+ SPARK_SCALA_VERSION = '2.11' # TODO: Support other versions and use different executors
17
+
18
+ # -----------------
19
+ # CLI arguments parsing
20
+ # -----------------
21
+
22
+ $cli_args = {
23
+ follow: false,
24
+ show_stderr: true
25
+ }
26
+
27
+ ARGV << '-h' if ARGV.empty?
28
+
29
+ OptionParser.new do |opts|
30
+ opts.banner = "Usage: #{$PROGRAM_NAME} <action> --adstax-host <adstax_host> [<options>]"
31
+ opts.separator ''
32
+ opts.separator 'Submit, kill, query the status, or inspect the log of a Spark job running in an AdStax cluster.'
33
+ opts.separator "<action> is one of 'submit', 'kill', 'status' or 'log'."
34
+ opts.separator "Example: #{$PROGRAM_NAME} submit --adstax-host apollo.dev.adstax.io --jar http://s3.amazonaws.com/shiftforward-public/bin/spark/adstax-spark-examples-1.0.jar --job eu.shiftforward.adstax.spark.examples.SparkPi 1000"
35
+ opts.separator "Example: #{$PROGRAM_NAME} kill driver-20160420105830-0001"
36
+ opts.separator ''
37
+ opts.separator 'Options:'
38
+
39
+ opts.on('--adstax-host STRING', 'Host suffix to the AdStax cluster services.') do |host_suffix|
40
+ $cli_args[:host_suffix] = host_suffix
41
+ end
42
+
43
+ opts.on('--jar STRING',
44
+ 'Path to a bundled jar including your application and all dependencies.',
45
+ 'The URL must be globally visible inside of your cluster.') do |jar|
46
+ $cli_args[:jar] = jar
47
+ end
48
+
49
+ opts.on('--job STRING',
50
+ 'Fully qualified name of the class extending `eu.shiftforward.adstax.spark.SparkJob`.',
51
+ 'The class will be used as the Spark job to run.') do |job|
52
+ $cli_args[:job] = job
53
+ end
54
+
55
+ opts.on('--submission-id STRING',
56
+ 'Id of the submission (required for the kill, status and log actions).') do |submission_id|
57
+ $cli_args[:submission_id] = submission_id
58
+ end
59
+
60
+ opts.on('-f', '--follow',
61
+ "Enables following the file updates in the 'log' action.") do
62
+ $cli_args[:follow] = true
63
+ end
64
+
65
+ opts.on('--hide-stderr',
66
+ "Hides stderr output in the 'log' action.") do
67
+ $cli_args[:show_stderr] = false
68
+ end
69
+
70
+ opts.on_tail('-h', '--help', 'Show this message.') do
71
+ puts opts
72
+ exit
73
+ end
74
+ end.parse!
75
+
76
+ def warn_missing(name)
77
+ puts "Missing required argument: #{name}"
78
+ exit 1
79
+ end
80
+
81
+ def get_http(uri)
82
+ uri = URI.parse(uri)
83
+ Net::HTTP.new(uri.host, uri.port)
84
+ end
85
+
86
+ def get_task(state_response, task_id)
87
+ target_tasks = []
88
+ state_response['completed_frameworks'].concat(state_response['frameworks']).each do |framework|
89
+ framework['completed_tasks'].concat(framework['tasks']).each do |task|
90
+ target_tasks.push(task) if task['id'] == task_id
91
+ end
92
+ end
93
+ target_tasks[0]
94
+ end
95
+
96
+ def get_executor(state_response, task_id)
97
+ target_executors = []
98
+ state_response['completed_frameworks'].concat(state_response['frameworks']).each do |framework|
99
+ framework['completed_executors'].concat(framework['executors']).each do |executor|
100
+ target_executors.push(executor) if executor['id'] == task_id
101
+ end
102
+ end
103
+ target_executors[0]
104
+ end
105
+
106
+ def mesos_download(http, remote_file, local_file)
107
+ params = { path: remote_file }
108
+ encoded_params = URI.encode_www_form(params)
109
+ file_response = http.request(Net::HTTP::Get.new(['/files/download', encoded_params].join('?')))
110
+ unless file_response.class.body_permitted?
111
+ puts 'Unable to fetch file from slave'
112
+ exit 1
113
+ end
114
+ local_file.rewind
115
+ local_file.write(file_response.body)
116
+ end
117
+
118
+ def tail_file(file, output_method = Proc.new { |line| puts line })
119
+ Thread.new do
120
+ File.open(file.path) do |log|
121
+ log.extend(File::Tail)
122
+ log.interval = 1
123
+ log.backward(10)
124
+ begin
125
+ log.tail { |line| output_method.call(line) }
126
+ rescue Interrupt => e
127
+ exit 1
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ $action = ARGV.shift || begin
134
+ warn_missing('action')
135
+ end
136
+
137
+ warn_missing('--adstax-host') unless $cli_args[:host_suffix]
138
+ $cluster_dispatcher_host = "http://spark-cluster-dispatcher.#{$cli_args[:host_suffix]}:7077"
139
+
140
+ def submit_job(jar, job)
141
+ uri = URI.parse($cluster_dispatcher_host)
142
+ http = Net::HTTP.new(uri.host, uri.port)
143
+ payload = {
144
+ 'action' => 'CreateSubmissionRequest',
145
+ 'appArgs' => ['--job', job].concat(ARGV),
146
+ 'appResource' => jar,
147
+ 'mainClass' => MAIN_CLASS,
148
+ 'clientSparkVersion' => '1.6.1',
149
+ 'environmentVariables' => {
150
+ 'SPARK_SCALA_VERSION' => SPARK_SCALA_VERSION
151
+ },
152
+ 'sparkProperties' => {
153
+ 'spark.jars' => $cli_args[:jar],
154
+ 'spark.driver.supervise' => 'false',
155
+ 'spark.app.name' => MAIN_CLASS,
156
+ 'spark.es.port' => '49200',
157
+ 'spark.es.nodes' => 'localhost',
158
+ 'spark.submit.deployMode' => 'cluster',
159
+ 'spark.mesos.coarse' => 'false',
160
+ 'spark.master' => "mesos://spark-cluster-dispatcher.#{$cli_args[:host_suffix]}:7077",
161
+ 'spark.executor.uri' => SPARK_EXECUTOR_URI
162
+ }
163
+ }.to_json
164
+ request = Net::HTTP::Post.new(
165
+ '/v1/submissions/create',
166
+ initheader = { 'Content-Type' => 'application/json' })
167
+ request.body = payload
168
+ http.request(request)
169
+ end
170
+
171
+ def kill_job(submission_id)
172
+ uri = URI.parse($cluster_dispatcher_host)
173
+ http = Net::HTTP.new(uri.host, uri.port)
174
+ request = Net::HTTP::Post.new("/v1/submissions/kill/#{submission_id}")
175
+ http.request(request)
176
+ end
177
+
178
+ def status_job(submission_id)
179
+ uri = URI.parse($cluster_dispatcher_host)
180
+ http = Net::HTTP.new(uri.host, uri.port)
181
+ request = Net::HTTP::Get.new("/v1/submissions/status/#{submission_id}")
182
+ http.request(request)
183
+ end
184
+
185
+ def log_job(submission_id, follow, show_stderr)
186
+ status_response = JSON.parse(status_job(submission_id).body)
187
+ if status_response['driverState'] == "NOT_FOUND"
188
+ puts "Unable to find submission with id #{submission_id}"
189
+ exit 1
190
+ end
191
+ if status_response['driverState'] == "QUEUED"
192
+ puts "Submission with id #{submission_id} is still queued for execution"
193
+ if follow
194
+ print "Waiting for submission with id #{submission_id} to start"
195
+ waiting_thread = Thread.new do
196
+ queued = true
197
+ while queued do
198
+ begin
199
+ sleep 1
200
+ print "."
201
+ rescue Interrupt => e
202
+ exit 1
203
+ end
204
+ res = JSON.parse(status_job(submission_id).body)
205
+ queued = res['driverState'] == "QUEUED"
206
+ end
207
+ end
208
+ waiting_thread.join
209
+ puts ""
210
+ else
211
+ exit 1
212
+ end
213
+ end
214
+ marathon_http = get_http("http://marathon.#{$cli_args[:host_suffix]}")
215
+ marathon_response = marathon_http.request(Net::HTTP::Get.new('/v2/info'))
216
+ unless marathon_response.class.body_permitted?
217
+ puts 'Unable to fetch Mesos leader url from Marathon'
218
+ exit 1
219
+ end
220
+ res = JSON.parse(marathon_response.body)
221
+ mesos_http = get_http(res['marathon_config']['mesos_leader_ui_url'])
222
+ mesos_response = mesos_http.request(Net::HTTP::Get.new('/state.json'))
223
+ unless mesos_response.class.body_permitted?
224
+ puts 'Unable to fetch Mesos status'
225
+ exit 1
226
+ end
227
+ res = JSON.parse(mesos_response.body)
228
+ target_task = get_task(res, submission_id)
229
+ unless target_task
230
+ puts "Unable to find submission with id #{submission_id} in Mesos. Maybe the submission is too old?"
231
+ exit 1
232
+ end
233
+ slaves = res['slaves']
234
+ slave_id = target_task['slave_id']
235
+ target_slaves = slaves.select do |slave|
236
+ slave['id'] == slave_id
237
+ end
238
+ if target_slaves.empty?
239
+ puts "Unable to find slave with id #{slave_id}"
240
+ exit 1
241
+ end
242
+ if target_slaves.length != 1
243
+ puts "Multiple slaves with id #{slave_id}"
244
+ exit 1
245
+ end
246
+ target_slave = target_slaves[0]
247
+ slave_http = get_http('http://' + target_slave['hostname'] + ':5051')
248
+ slave_response = slave_http.request(Net::HTTP::Get.new('/state.json'))
249
+ unless slave_response.class.body_permitted?
250
+ puts 'Unable to fetch file from slave'
251
+ exit 1
252
+ end
253
+ res = JSON.parse(slave_response.body)
254
+ target_executor = get_executor(res, submission_id)
255
+ unless target_executor
256
+ puts "Unable to find submission with id #{submission_id} in executor. Maybe the submission is too old?"
257
+ exit 1
258
+ end
259
+ directory = target_executor['directory']
260
+ stdout_file = Tempfile.new('spark' + submission_id)
261
+ stderr_file = Tempfile.new('spark' + submission_id)
262
+ threads = []
263
+ if follow
264
+ threads.push(Thread.new do
265
+ loop do
266
+ begin
267
+ sleep 1
268
+ rescue Interrupt => e
269
+ exit 1
270
+ end
271
+ mesos_download(slave_http, directory + '/stdout', stdout_file)
272
+ mesos_download(slave_http, directory + '/stderr', stderr_file)
273
+ end
274
+ end)
275
+ else
276
+ mesos_download(slave_http, directory + '/stdout', stdout_file)
277
+ mesos_download(slave_http, directory + '/stderr', stderr_file)
278
+ end
279
+ if follow
280
+ threads.push(tail_file(stdout_file))
281
+ threads.push(tail_file(stderr_file, Proc.new { |line| puts line.chomp.red })) if show_stderr
282
+ begin
283
+ threads.each { |thread| thread.join }
284
+ rescue Interrupt => e
285
+ exit 1
286
+ end
287
+ else
288
+ if show_stderr
289
+ stderr_file.rewind
290
+ puts stderr_file.read.chomp.red
291
+ end
292
+ stdout_file.rewind
293
+ puts stdout_file.read
294
+ end
295
+ end
296
+
297
+ # -----------------
298
+ # Program start
299
+ # -----------------
300
+
301
+ case $action
302
+ when 'submit'
303
+ warn_missing('--jar') unless $cli_args[:jar]
304
+ warn_missing('--job') unless $cli_args[:job]
305
+ response = submit_job($cli_args[:jar], $cli_args[:job])
306
+ puts response.body
307
+
308
+ when 'kill'
309
+ warn_missing('--submission_id') unless $cli_args[:submission_id]
310
+ response = kill_job($cli_args[:submission_id])
311
+ puts response.body
312
+
313
+ when 'status'
314
+ warn_missing('--submission_id') unless $cli_args[:submission_id]
315
+ response = status_job($cli_args[:submission_id])
316
+ puts response.body
317
+
318
+ when 'log'
319
+ warn_missing('--submission_id') unless $cli_args[:submission_id]
320
+ log_job($cli_args[:submission_id], $cli_args[:follow], $cli_args[:show_stderr])
321
+
322
+ else
323
+ puts "Unrecognized action: #{$action}"
324
+ exit 1
325
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: adstax-spark-job-manager
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - ShiftForward
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-07-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: file-tail
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.8'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.8'
41
+ - !ruby/object:Gem::Dependency
42
+ name: colorize
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '0.7'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '0.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.7'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.7'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ description: Allow submitting, querying the status, outputting the log and killing
84
+ Spark jobs on an AdStax cluster.
85
+ email:
86
+ - info@shiftforward.eu
87
+ executables:
88
+ - adstax-spark-job-manager
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - .gitignore
93
+ - Gemfile
94
+ - LICENSE
95
+ - README.md
96
+ - Rakefile
97
+ - adstax-spark-job-manager.gemspec
98
+ - bin/adstax-spark-job-manager
99
+ homepage:
100
+ licenses:
101
+ - Apache-2.0
102
+ metadata: {}
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - '>='
110
+ - !ruby/object:Gem::Version
111
+ version: 2.0.0
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubyforge_project:
119
+ rubygems_version: 2.4.6
120
+ signing_key:
121
+ specification_version: 4
122
+ summary: Manage Spark jobs running on an AdStax cluster.
123
+ test_files: []