kube_queue 0.2.0 → 0.3.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 +5 -5
- data/.gcloudignore +3 -0
- data/.rubocop.yml +3 -0
- data/Dockerfile +2 -2
- data/Dockerfile.myapp +22 -0
- data/README.md +64 -15
- data/bin/console +1 -1
- data/cloudbuild.yaml +5 -1
- data/exe/kube_queue +58 -9
- data/kube_queue.gemspec +2 -1
- data/lib/active_job/adapters/kube_queue_adapter.rb +31 -0
- data/lib/kube_queue/client.rb +12 -3
- data/lib/kube_queue/executor.rb +3 -4
- data/lib/kube_queue/job_specification.rb +64 -0
- data/lib/kube_queue/manifest_builder.rb +25 -0
- data/lib/kube_queue/railties.rb +3 -0
- data/lib/kube_queue/version.rb +1 -1
- data/lib/kube_queue/worker/dsl.rb +49 -0
- data/lib/kube_queue/worker.rb +38 -41
- data/lib/kube_queue.rb +51 -1
- data/template/job.yaml +34 -6
- metadata +23 -10
- data/docker/Gemfile +0 -3
- data/docker/test_worker.rb +0 -14
- data/k8s/service-account.yaml +0 -18
- data/lib/kube_queue/cli.rb +0 -56
- data/lib/kube_queue/job_configuration.rb +0 -54
- data/lib/kube_queue/runner.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1c864d0acb58ed1b538edeebcdec417d6284b9a4df1d151a9c008eb190e9b9f3
|
4
|
+
data.tar.gz: 49d9aff0aca53a00836f9358149741694173b19df4ea1e16aef40ff0a23441df
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af4c1d02900ac0e864e59ee4fa81dcb43771ddafcdd50bf2dfc4956f2e37b04f121ca6bf13c7be185d2876f74ab582425022fe687f7ca9993e4261fd6b65bedb
|
7
|
+
data.tar.gz: 314585e098b2578c124ce28b3accf08e6f51c05058a0b2590ee5ca47c65ef0b58cabe6c5e105372693c986d6c2dc1ec51a8c3fb5a4052323f11ce4897419528d
|
data/.gcloudignore
CHANGED
data/.rubocop.yml
CHANGED
data/Dockerfile
CHANGED
@@ -5,13 +5,13 @@ WORKDIR /app
|
|
5
5
|
RUN apt-get update -y && apt-get install git --no-install-recommends && rm -r /var/cache/apt /var/lib/apt/lists
|
6
6
|
|
7
7
|
RUN gem install bundler:2.0.2
|
8
|
-
COPY docker/Gemfile Gemfile
|
8
|
+
COPY examples/docker/Gemfile Gemfile
|
9
9
|
COPY Gemfile kube_queue.gemspec .git /vendor/kube_queue/
|
10
10
|
COPY exe/kube_queue /vendor/kube_queue/exe/kube_queue
|
11
11
|
COPY lib/kube_queue/version.rb /vendor/kube_queue/lib/kube_queue/version.rb
|
12
12
|
RUN bundle install -j4
|
13
13
|
|
14
|
-
COPY docker/test_worker.rb .
|
14
|
+
COPY examples/docker/test_worker.rb .
|
15
15
|
COPY . /vendor/kube_queue
|
16
16
|
|
17
17
|
CMD ["bundle", "exec", "kube_queue", "TestWorker", "-r", "./test_worker.rb"]
|
data/Dockerfile.myapp
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
FROM ruby
|
2
|
+
|
3
|
+
RUN apt-get update -y && apt-get install git --no-install-recommends && rm -r /var/cache/apt /var/lib/apt/lists
|
4
|
+
|
5
|
+
RUN gem install bundler:2.0.2
|
6
|
+
|
7
|
+
ENV KUBE_QUEUE_PATH /vendor/kube_queue
|
8
|
+
ENV WORKDIR /app
|
9
|
+
|
10
|
+
WORKDIR $WORKDIR
|
11
|
+
|
12
|
+
COPY .git $KUBE_QUEUE_PATH/.git
|
13
|
+
COPY Gemfile kube_queue.gemspec $KUBE_QUEUE_PATH/
|
14
|
+
COPY exe/kube_queue $KUBE_QUEUE_PATH/exe/kube_queue
|
15
|
+
COPY lib/kube_queue/version.rb $KUBE_QUEUE_PATH/lib/kube_queue/version.rb
|
16
|
+
COPY examples/myapp/Gemfile examples/myapp/Gemfile.lock $WORKDIR/
|
17
|
+
RUN bundle install -j4
|
18
|
+
|
19
|
+
COPY lib $KUBE_QUEUE_PATH/lib
|
20
|
+
COPY examples/myapp/ $WORKDIR
|
21
|
+
|
22
|
+
CMD ["bundle", "exec", "rails", "console"]
|
data/README.md
CHANGED
@@ -1,13 +1,5 @@
|
|
1
1
|
# KubeQueue
|
2
2
|
|
3
|
-
## Features
|
4
|
-
|
5
|
-
- ActiveJob integration
|
6
|
-
- Support multiple kubernetes client configuration.
|
7
|
-
- Support templating and customization for kubernetes job manifest.
|
8
|
-
- Job dosen't returns id. Can not track job details from code.
|
9
|
-
- Logging
|
10
|
-
|
11
3
|
## Installation
|
12
4
|
|
13
5
|
Add this line to your application's Gemfile:
|
@@ -44,29 +36,86 @@ class TestWorker
|
|
44
36
|
end
|
45
37
|
```
|
46
38
|
|
47
|
-
Setting kubernetes configuration
|
39
|
+
Setting kubernetes configuration.
|
48
40
|
|
49
|
-
```
|
41
|
+
```ruby
|
50
42
|
KubeQueue.kubernetes_configure do |client|
|
51
43
|
client.url = ENV['K8S_URL']
|
52
44
|
client.ssl_ca_file = ENV['K8S_CA_CERT_FILE']
|
53
45
|
client.auth_token = File.read(ENV['K8S_TOKEN'])
|
54
46
|
end
|
47
|
+
```
|
48
|
+
|
49
|
+
and run:
|
55
50
|
|
51
|
+
```ruby
|
56
52
|
TestWorker.perform(message: 'hello')
|
57
53
|
```
|
58
54
|
|
59
|
-
|
55
|
+
### ActiveJob Support
|
56
|
+
|
57
|
+
Write to `application.rb`:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
Rails.application.config.active_job.adapter = :kube_queue
|
61
|
+
```
|
62
|
+
|
63
|
+
Just put your job into `app/jobs` . Example:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
# app/jobs/print_message_job.rb
|
67
|
+
class PrintMessageJob < ApplicationJob
|
68
|
+
include KubeQueue::Worker
|
69
|
+
|
70
|
+
worker_name 'print-message-job'
|
71
|
+
image "your-registry/your-image"
|
72
|
+
container_name 'your-container-name'
|
73
|
+
|
74
|
+
def perform(payload)
|
75
|
+
logger.info payload[:message]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
```
|
79
|
+
|
80
|
+
and run:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
irb(main):001:0> job = PrintMessageJob.perform_later(message: 'hello, kubernetes!')
|
84
|
+
Enqueued PrintMessageJob (Job ID: 0bf15b35-62d8-4380-9173-99839ce735ff) to KubeQueue(default) with arguments: {:message=>"hello, kubernetes!"}
|
85
|
+
=> #<PrintMessageJob:0x00007fbfd00c7848 @arguments=[{:message=>"hello, kubernetes!"}], @job_id="0bf15b35-62d8-4380-9173-99839ce735ff", @queue_name="default", @priority=nil, @executions=0>
|
86
|
+
irb(main):002:0> job.status
|
87
|
+
=> #<K8s::Resource startTime="2019-08-12T15:56:37Z", active=1>
|
88
|
+
irb(main):003:0> job.status
|
89
|
+
=> #<K8s::Resource conditions=[{:type=>"Complete", :status=>"True", :lastProbeTime=>"2019-08-12T15:57:03Z", :lastTransitionTime=>"2019-08-12T15:57:03Z"}], startTime="2019-08-12T15:56:37Z", completionTime="2019-08-12T15:57:03Z", succeeded=1>
|
90
|
+
```
|
91
|
+
|
92
|
+
See more examples in [here](examples).
|
93
|
+
|
94
|
+
### Run job on locally
|
95
|
+
|
96
|
+
```
|
97
|
+
bundle exec kube_queue runner JOB_NAME [PAYLOAD]
|
98
|
+
```
|
99
|
+
|
100
|
+
See more information by `kube_queue help` or [here](exe/kube_queue).
|
101
|
+
|
102
|
+
## Features
|
103
|
+
|
104
|
+
- Add tests.
|
105
|
+
- Support multiple kubernetes client configuration.
|
106
|
+
- Logging informations.
|
107
|
+
|
108
|
+
## Development(on GCP/GKE)
|
60
109
|
|
61
110
|
setup:
|
62
111
|
|
63
112
|
```
|
64
113
|
# create service account and cluster role.
|
65
|
-
kubectl apply -f k8s/service-account.yaml
|
114
|
+
kubectl apply -f examples/k8s/service-account.yaml
|
66
115
|
|
67
116
|
# get ca.crt and token
|
68
|
-
|
69
|
-
|
117
|
+
kubectl get secret -n kube-system kube-queue-test-token-xxx -o jsonpath="{['data']['token']}" | base64 -d > secrets/token
|
118
|
+
kubectl get secret -n kube-system kube-queue-test-token-xxx -o jsonpath="{['data']['ca\.crt']}" | base64 -d > secrets/ca.crt
|
70
119
|
|
71
120
|
# build image
|
72
121
|
gcloud builds submit --config cloudbuild.yaml .
|
@@ -77,5 +126,5 @@ run:
|
|
77
126
|
```
|
78
127
|
K8S_URL=https://xx.xxx.xxx.xxx K8S_CA_CERT_FILE=$(pwd)/secrets/ca.crt K8S_TOKEN=$(pwd)/secrets/token IMAGE_NAME=gcr.io/your-project/kube-queue bin/console
|
79
128
|
|
80
|
-
irb(main):001:0> TestWorker.perform_async(message: 'hello kubernetes')
|
129
|
+
irb(main):001:0> TestWorker.perform_async(message: 'hello, kubernetes!')
|
81
130
|
```
|
data/bin/console
CHANGED
@@ -16,7 +16,7 @@ require "irb"
|
|
16
16
|
KubeQueue.kubernetes_configure do |client|
|
17
17
|
client.url = ENV['K8S_URL']
|
18
18
|
client.ssl_ca_file = ENV['K8S_CA_CERT_FILE']
|
19
|
-
client.auth_token =
|
19
|
+
client.auth_token = ENV['K8S_TOKEN']
|
20
20
|
end
|
21
21
|
|
22
22
|
IRB.start(__FILE__)
|
data/cloudbuild.yaml
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
-
images: ['gcr.io/$PROJECT_ID/kube-queue:latest']
|
1
|
+
images: ['gcr.io/$PROJECT_ID/kube-queue:latest', 'gcr.io/$PROJECT_ID/kube-queue-test-app:latest']
|
2
2
|
|
3
3
|
steps:
|
4
4
|
- name: 'gcr.io/cloud-builders/docker'
|
5
5
|
args: ['build', '-f', '/workspace/Dockerfile', '/workspace', '-t', 'gcr.io/$PROJECT_ID/kube-queue:latest']
|
6
|
+
|
7
|
+
- name: 'gcr.io/cloud-builders/docker'
|
8
|
+
args: ['build', '-f', '/workspace/Dockerfile.myapp', '/workspace', '-t', 'gcr.io/$PROJECT_ID/kube-queue-test-app:latest']
|
9
|
+
waitFor: ['-']
|
data/exe/kube_queue
CHANGED
@@ -1,14 +1,63 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
3
|
+
require 'rubygems'
|
4
|
+
require 'thor'
|
5
|
+
require 'kube_queue'
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
rescue StandardError => e
|
9
|
-
raise e if $DEBUG
|
7
|
+
module KubeQueue
|
8
|
+
class CLI < Thor
|
9
|
+
default_task :version
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
desc 'runner JOB_NAME [PAYLOAD]', 'run worker'
|
12
|
+
method_option :require, aliases: '-r', type: :string, desc: 'Location of Rails application with workers or file to require'
|
13
|
+
method_option :rails, aliases: '-R', type: :boolean, desc: 'Location of Rails application with workers or file to require'
|
14
|
+
def runner(job_name, payload = nil)
|
15
|
+
load_files!
|
16
|
+
|
17
|
+
# Infer application work on rails if require option does not specified.
|
18
|
+
load_rails! if !options[:require] || options[:rails]
|
19
|
+
|
20
|
+
payload ||= ENV['KUBE_QUEUE_MESSAGE_PAYLOAD']
|
21
|
+
payload = JSON.parse(payload) if payload
|
22
|
+
# Compatibility for ActiveJob serialized payload
|
23
|
+
payload = [payload] unless payload.is_a?(Array)
|
24
|
+
payload = ActiveJob::Arguments.deserialize(payload) if defined?(ActiveJob::Arguments)
|
25
|
+
|
26
|
+
job = KubeQueue.fetch_worker(job_name).new(*payload)
|
27
|
+
|
28
|
+
job.perform_now
|
29
|
+
end
|
30
|
+
|
31
|
+
desc 'version', 'Prints version'
|
32
|
+
def version
|
33
|
+
say "KubeQueue version #{KubeQueue::VERSION}"
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def load_files!
|
39
|
+
return unless options[:require]
|
40
|
+
|
41
|
+
raise "#{options[:require]} dosent exist." unless File.exist?(options[:require])
|
42
|
+
|
43
|
+
files = File.directory?(options[:require]) ? Dir.glob(File.join(options[:require], '**/*.rb')) : [options[:require]]
|
44
|
+
|
45
|
+
files.each do |file|
|
46
|
+
require file
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def load_rails!
|
51
|
+
require "rails"
|
52
|
+
|
53
|
+
raise "KubeQueue does not supports this version of Rails" if ::Rails::VERSION::MAJOR < 5
|
54
|
+
|
55
|
+
require 'rails'
|
56
|
+
require 'kube_queue/railties'
|
57
|
+
require File.expand_path('config/environment.rb')
|
58
|
+
Rails.application.eager_load!
|
59
|
+
end
|
60
|
+
end
|
14
61
|
end
|
62
|
+
|
63
|
+
KubeQueue::CLI.start
|
data/kube_queue.gemspec
CHANGED
@@ -18,13 +18,14 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.metadata["changelog_uri"] = "https://github.com/yuemori/kube_queue/CHANGELOG.md"
|
19
19
|
|
20
20
|
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
21
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
21
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|examples|cloudbuild.yaml)/}) }
|
22
22
|
end
|
23
23
|
spec.bindir = "exe"
|
24
24
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
25
25
|
spec.require_paths = ["lib"]
|
26
26
|
|
27
27
|
spec.add_dependency "k8s-client"
|
28
|
+
spec.add_dependency "thor"
|
28
29
|
spec.add_development_dependency "bundler", "~> 2.0"
|
29
30
|
spec.add_development_dependency "rake", "~> 10.0"
|
30
31
|
spec.add_development_dependency "rspec", "~> 3.0"
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'kube_queue'
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module QueueAdapters
|
5
|
+
# == KubeQueue adapter for ActiveJob ==
|
6
|
+
#
|
7
|
+
# To use KubeQueue set the queue_adapter config to +:kube_queue+.
|
8
|
+
# Rails.application.config.active_job.queue_adapter = :kube_queue
|
9
|
+
class KubeQueueAdapter
|
10
|
+
class << self
|
11
|
+
# Interface for ActiveJob 4.2
|
12
|
+
def enqueue(job)
|
13
|
+
KubeQueue.executor.enqueue(job, ActiveJob::Arguments.serialize(job.arguments))
|
14
|
+
end
|
15
|
+
|
16
|
+
def enqueue_at(_job, _timestamp)
|
17
|
+
raise NotImplementedError
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Interface for ActiveJob 5.0
|
22
|
+
def enqueue(job)
|
23
|
+
KubeQueueAdapter.enqueue(job)
|
24
|
+
end
|
25
|
+
|
26
|
+
def enqueue_at(job, timestamp)
|
27
|
+
KubeQueueAdapter.enqueue_at(job, timestamp)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/kube_queue/client.rb
CHANGED
@@ -5,8 +5,17 @@ module KubeQueue
|
|
5
5
|
class Client
|
6
6
|
def create_job(manifest)
|
7
7
|
job = K8s::Resource.new(manifest)
|
8
|
-
job.metadata.namespace
|
9
|
-
client.
|
8
|
+
job.metadata.namespace ||= 'default'
|
9
|
+
client.resource('jobs').create_resource(job)
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_job(namespace, name)
|
13
|
+
client.resource('jobs', namespace: namespace).get(name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def list_job(namespace = nil)
|
17
|
+
selector = { 'kube-queue-job': 'true' }
|
18
|
+
client.resource('jobs', namespace: namespace).list(labelSelector: selector)
|
10
19
|
end
|
11
20
|
|
12
21
|
attr_accessor :url, :ssl_ca_file, :auth_token
|
@@ -14,7 +23,7 @@ module KubeQueue
|
|
14
23
|
private
|
15
24
|
|
16
25
|
def client
|
17
|
-
@client ||= K8s.client(url, ssl_ca_file: ssl_ca_file, auth_token: auth_token)
|
26
|
+
@client ||= K8s.client(url, ssl_ca_file: ssl_ca_file, auth_token: auth_token).api('batch/v1')
|
18
27
|
end
|
19
28
|
end
|
20
29
|
end
|
data/lib/kube_queue/executor.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
|
-
require '
|
2
|
-
require 'json'
|
1
|
+
require 'kube_queue/manifest_builder'
|
3
2
|
|
4
3
|
module KubeQueue
|
5
4
|
class Executor
|
6
|
-
def
|
7
|
-
manifest =
|
5
|
+
def enqueue(job, payload)
|
6
|
+
manifest = ManifestBuilder.new(job, payload).build
|
8
7
|
KubeQueue.client.create_job(manifest)
|
9
8
|
end
|
10
9
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module KubeQueue
|
5
|
+
class JobSpecification
|
6
|
+
class MissingParameterError < StandardError; end
|
7
|
+
|
8
|
+
|
9
|
+
attr_reader :job_class
|
10
|
+
|
11
|
+
attr_accessor :payload, :name, :active_deadline_seconds, :backoff_limit
|
12
|
+
|
13
|
+
attr_writer :image, :namespace, :worker_name, :command,
|
14
|
+
:container_name, :restart_policy, :job_labels, :pod_labels
|
15
|
+
|
16
|
+
def initialize(job_class)
|
17
|
+
@job_class = job_class
|
18
|
+
end
|
19
|
+
|
20
|
+
def job_name(job_id)
|
21
|
+
"#{worker_name}-#{job_id}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def image
|
25
|
+
@image || raise_not_found_required_parameter('image')
|
26
|
+
end
|
27
|
+
|
28
|
+
def namespace
|
29
|
+
@namespace || 'default'
|
30
|
+
end
|
31
|
+
|
32
|
+
def worker_name
|
33
|
+
@worker_name || raise_not_found_required_parameter('worker_name')
|
34
|
+
end
|
35
|
+
|
36
|
+
def container_name
|
37
|
+
@container_name || worker_name
|
38
|
+
end
|
39
|
+
|
40
|
+
def command
|
41
|
+
@command || ['bundle', 'exec', 'kube_queue', 'runner', job_class.name]
|
42
|
+
end
|
43
|
+
|
44
|
+
def restart_policy
|
45
|
+
@restart_policy || 'Never'
|
46
|
+
end
|
47
|
+
|
48
|
+
def job_labels
|
49
|
+
@job_labels || {}
|
50
|
+
end
|
51
|
+
|
52
|
+
def pod_labels
|
53
|
+
@pod_labels || {}
|
54
|
+
end
|
55
|
+
|
56
|
+
def env
|
57
|
+
KubeQueue.default_env.merge(@env || {})
|
58
|
+
end
|
59
|
+
|
60
|
+
def raise_not_found_required_parameter(field)
|
61
|
+
raise MissingParameterError, "#{field} is required"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module KubeQueue
|
5
|
+
class ManifestBuilder
|
6
|
+
attr_reader :job
|
7
|
+
|
8
|
+
def initialize(job, payload = nil)
|
9
|
+
@job = job
|
10
|
+
@payload = payload
|
11
|
+
end
|
12
|
+
|
13
|
+
def spec
|
14
|
+
job.job_spec
|
15
|
+
end
|
16
|
+
|
17
|
+
def payload
|
18
|
+
@payload ? JSON.generate(@payload, quirks_mode: true) : nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def build
|
22
|
+
YAML.safe_load(ERB.new(job.template, nil, "-").result(binding))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/kube_queue/version.rb
CHANGED
@@ -0,0 +1,49 @@
|
|
1
|
+
module KubeQueue
|
2
|
+
module Worker
|
3
|
+
module DSL
|
4
|
+
def job_spec
|
5
|
+
@job_spec ||= JobSpecification.new(self)
|
6
|
+
end
|
7
|
+
|
8
|
+
def worker_name(name)
|
9
|
+
job_spec.worker_name = name
|
10
|
+
end
|
11
|
+
|
12
|
+
def container_name(container_name)
|
13
|
+
job_spec.container_name = container_name
|
14
|
+
end
|
15
|
+
|
16
|
+
def image(image)
|
17
|
+
job_spec.image = image
|
18
|
+
end
|
19
|
+
|
20
|
+
def namespace(namespace)
|
21
|
+
job_spec.namespace = namespace
|
22
|
+
end
|
23
|
+
|
24
|
+
def command(*command)
|
25
|
+
job_spec.command = command
|
26
|
+
end
|
27
|
+
|
28
|
+
def restart_policy(policy)
|
29
|
+
job_spec.restart_policy = policy
|
30
|
+
end
|
31
|
+
|
32
|
+
def active_deadline_seconds(seconds)
|
33
|
+
job_spec.active_deadline_seconds = seconds
|
34
|
+
end
|
35
|
+
|
36
|
+
def backoff_limit(limit)
|
37
|
+
job_spec.backoff_limit = limit
|
38
|
+
end
|
39
|
+
|
40
|
+
def env(env)
|
41
|
+
job_spec.env = env
|
42
|
+
end
|
43
|
+
|
44
|
+
def labels(labels)
|
45
|
+
job_spec.labels = labels
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/kube_queue/worker.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require 'kube_queue/job_configuration'
|
1
|
+
require 'kube_queue/worker/dsl'
|
2
|
+
require 'kube_queue/job_specification'
|
4
3
|
|
5
4
|
module KubeQueue
|
6
5
|
module Worker
|
@@ -11,57 +10,55 @@ module KubeQueue
|
|
11
10
|
end
|
12
11
|
|
13
12
|
module ClassMethods
|
14
|
-
|
15
|
-
@job_name = name
|
16
|
-
end
|
13
|
+
include DSL
|
17
14
|
|
18
|
-
def
|
19
|
-
|
15
|
+
def enqueue(body = nil)
|
16
|
+
KubeQueue.executor.enqueue(new, body)
|
20
17
|
end
|
18
|
+
alias_method :perform_async, :enqueue
|
21
19
|
|
22
|
-
def
|
23
|
-
|
20
|
+
def enqueue_at(body = nil)
|
21
|
+
KubeQueue.executor.enqueue(new, body)
|
24
22
|
end
|
25
23
|
|
26
|
-
def
|
27
|
-
@
|
24
|
+
def read_template
|
25
|
+
File.read(@template || File.expand_path('../../../template/job.yaml', __FILE__))
|
28
26
|
end
|
27
|
+
end
|
29
28
|
|
30
|
-
|
31
|
-
|
32
|
-
|
29
|
+
def template
|
30
|
+
self.class.read_template
|
31
|
+
end
|
33
32
|
|
34
|
-
|
35
|
-
|
36
|
-
|
33
|
+
def job_spec
|
34
|
+
self.class.job_spec
|
35
|
+
end
|
37
36
|
|
38
|
-
|
39
|
-
@backoff_limit = limit
|
40
|
-
end
|
37
|
+
attr_accessor :job_id, :arguments
|
41
38
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
s.image = @image
|
46
|
-
s.name = name
|
47
|
-
s.command = @command
|
48
|
-
s.job_name = @job_name
|
49
|
-
s.container_name = @job_name
|
50
|
-
s.template = @template
|
51
|
-
s.backoff_limit = @backoff_limit
|
52
|
-
s.payload = JSON.generate(body, quirks_mode: true)
|
53
|
-
end
|
39
|
+
def initialize(*arguments)
|
40
|
+
# Compatibility for ActiveJob interface
|
41
|
+
super
|
54
42
|
|
55
|
-
|
56
|
-
|
43
|
+
@arguments = arguments
|
44
|
+
@job_id = SecureRandom.uuid
|
45
|
+
end
|
57
46
|
|
58
|
-
|
59
|
-
|
60
|
-
|
47
|
+
def perform_now
|
48
|
+
# Compatibility for ActiveJob interface
|
49
|
+
return super if defined?(super)
|
61
50
|
|
62
|
-
|
63
|
-
|
64
|
-
|
51
|
+
perform(*arguments)
|
52
|
+
end
|
53
|
+
|
54
|
+
def perform(*)
|
55
|
+
raise NotImplementedError
|
56
|
+
end
|
57
|
+
|
58
|
+
# FIXME: improve performance
|
59
|
+
def status
|
60
|
+
res = KubeQueue.client.get_job(job_spec.namespace, job_spec.job_name(job_id))
|
61
|
+
res.status
|
65
62
|
end
|
66
63
|
end
|
67
64
|
end
|
data/lib/kube_queue.rb
CHANGED
@@ -3,9 +3,10 @@ require "kube_queue/executor"
|
|
3
3
|
require "kube_queue/configuration"
|
4
4
|
require "kube_queue/worker"
|
5
5
|
require "kube_queue/client"
|
6
|
+
require "active_job/adapters/kube_queue_adapter" if defined?(Rails)
|
6
7
|
|
7
8
|
module KubeQueue
|
8
|
-
class
|
9
|
+
class JobNotFound < StandardError; end
|
9
10
|
|
10
11
|
class << self
|
11
12
|
attr_writer :executor
|
@@ -34,6 +35,31 @@ module KubeQueue
|
|
34
35
|
@configuration ||= Configuration.new
|
35
36
|
end
|
36
37
|
|
38
|
+
def list(namespace = nil)
|
39
|
+
client.list_job(namespace).map do |job|
|
40
|
+
worker = fetch_worker(job.metadata.annotations['kube-queue-job-class'])
|
41
|
+
job_id = job.metadata.annotations['kube-queue-job-id']
|
42
|
+
payload = deserialize(job.metadata.annotations['kube-queue-message-payload'])
|
43
|
+
|
44
|
+
job = worker.new(*payload)
|
45
|
+
job.job_id = job_id
|
46
|
+
job
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
attr_writer :default_env
|
51
|
+
|
52
|
+
def default_env
|
53
|
+
return @default_env if @default_env
|
54
|
+
|
55
|
+
return {} unless defined?(Rails)
|
56
|
+
|
57
|
+
{
|
58
|
+
RAILS_LOG_TO_STDOUT: ENV['RAILS_LOG_TO_STDOUT'],
|
59
|
+
RAILS_ENV: ENV['RAILS_ENV']
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
37
63
|
def fetch_worker(name)
|
38
64
|
worker_registry.fetch(name)
|
39
65
|
end
|
@@ -45,5 +71,29 @@ module KubeQueue
|
|
45
71
|
def worker_registry
|
46
72
|
@worker_registry ||= {}
|
47
73
|
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def deserialize(payload)
|
78
|
+
return payload if payload.blank?
|
79
|
+
|
80
|
+
payload = JSON.parse(payload)
|
81
|
+
# Compatibility for ActiveJob serialized payload
|
82
|
+
payload = [payload] unless payload.is_a?(Array)
|
83
|
+
|
84
|
+
if defined?(ActiveJob::Arguments)
|
85
|
+
begin
|
86
|
+
payload = ActiveJob::Arguments.deserialize(payload)
|
87
|
+
rescue ActiveJob::DeserializationError => e
|
88
|
+
logger.warn e.message
|
89
|
+
logger.warn "#{payload} can not deserialized"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
payload
|
94
|
+
rescue JSON::ParseError => e
|
95
|
+
logger.warn e.message
|
96
|
+
logger.warn "#{payload} can not deserialized"
|
97
|
+
end
|
48
98
|
end
|
49
99
|
end
|
data/template/job.yaml
CHANGED
@@ -1,16 +1,44 @@
|
|
1
1
|
apiVersion: batch/v1
|
2
2
|
kind: Job
|
3
3
|
metadata:
|
4
|
-
|
4
|
+
annotations:
|
5
|
+
kube-queue-message-payload: '<%= payload %>'
|
6
|
+
kube-queue-job-class: "<%= spec.job_class %>"
|
7
|
+
kube-queue-job-id: "<%= job.job_id %>"
|
8
|
+
name: "<%= spec.job_name(job.job_id) %>"
|
9
|
+
namespace: <%= spec.namespace %>
|
10
|
+
labels:
|
11
|
+
kube-queue-job: "true"
|
12
|
+
kube-queue-worker-name: "<%= spec.worker_name %>"
|
13
|
+
kube-queue-job-class: "<%= spec.job_class %>"
|
14
|
+
kube-queue-job-id: "<%= job.job_id %>"
|
5
15
|
spec:
|
6
16
|
template:
|
17
|
+
metadata:
|
18
|
+
annotations:
|
19
|
+
kube-queue-message-payload: '<%= payload %>'
|
20
|
+
kube-queue-job-class: "<%= spec.job_class %>"
|
21
|
+
kube-queue-job-id: "<%= job.job_id %>"
|
22
|
+
labels:
|
23
|
+
kube-queue-job: "true"
|
24
|
+
kube-queue-worker-name: "<%= spec.worker_name %>"
|
25
|
+
kube-queue-job-class: "<%= spec.job_class %>"
|
26
|
+
kube-queue-job-id: "<%= job.job_id %>"
|
27
|
+
<%- spec.job_labels.each do |key, value| %>
|
28
|
+
<%= key %>: "<%= value %>"
|
29
|
+
<%- end %>
|
7
30
|
spec:
|
8
31
|
containers:
|
9
|
-
- name: "<%= container_name %>"
|
10
|
-
image: "<%= image %>"
|
11
|
-
command: <%= command %>
|
32
|
+
- name: "<%= spec.container_name %>"
|
33
|
+
image: "<%= spec.image %>"
|
34
|
+
command: <%= spec.command %>
|
12
35
|
env:
|
13
36
|
- name: "KUBE_QUEUE_MESSAGE_PAYLOAD"
|
14
37
|
value: '<%= payload %>'
|
15
|
-
|
16
|
-
|
38
|
+
<%- spec.env.each do |key, value| %>
|
39
|
+
- name: "<%= key %>"
|
40
|
+
value: "<%= value %>"
|
41
|
+
<%- end %>
|
42
|
+
restartPolicy: "<%= spec.restart_policy %>"
|
43
|
+
backoffLimit: <%= spec.backoff_limit %>
|
44
|
+
activeDeadlineSeconds: <%= spec.active_deadline_seconds %>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kube_queue
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- yuemori
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-08-
|
11
|
+
date: 2019-08-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: k8s-client
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: thor
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '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'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -152,6 +166,7 @@ files:
|
|
152
166
|
- ".travis.yml"
|
153
167
|
- CODE_OF_CONDUCT.md
|
154
168
|
- Dockerfile
|
169
|
+
- Dockerfile.myapp
|
155
170
|
- Gemfile
|
156
171
|
- LICENSE.txt
|
157
172
|
- README.md
|
@@ -159,20 +174,19 @@ files:
|
|
159
174
|
- bin/console
|
160
175
|
- bin/setup
|
161
176
|
- cloudbuild.yaml
|
162
|
-
- docker/Gemfile
|
163
|
-
- docker/test_worker.rb
|
164
177
|
- exe/kube_queue
|
165
|
-
- k8s/service-account.yaml
|
166
178
|
- kube_queue.gemspec
|
179
|
+
- lib/active_job/adapters/kube_queue_adapter.rb
|
167
180
|
- lib/kube_queue.rb
|
168
|
-
- lib/kube_queue/cli.rb
|
169
181
|
- lib/kube_queue/client.rb
|
170
182
|
- lib/kube_queue/configuration.rb
|
171
183
|
- lib/kube_queue/executor.rb
|
172
|
-
- lib/kube_queue/
|
173
|
-
- lib/kube_queue/
|
184
|
+
- lib/kube_queue/job_specification.rb
|
185
|
+
- lib/kube_queue/manifest_builder.rb
|
186
|
+
- lib/kube_queue/railties.rb
|
174
187
|
- lib/kube_queue/version.rb
|
175
188
|
- lib/kube_queue/worker.rb
|
189
|
+
- lib/kube_queue/worker/dsl.rb
|
176
190
|
- template/job.yaml
|
177
191
|
homepage: https://github.com/yuemori/kube_queue
|
178
192
|
licenses:
|
@@ -196,8 +210,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
196
210
|
- !ruby/object:Gem::Version
|
197
211
|
version: '0'
|
198
212
|
requirements: []
|
199
|
-
|
200
|
-
rubygems_version: 2.6.11
|
213
|
+
rubygems_version: 3.0.3
|
201
214
|
signing_key:
|
202
215
|
specification_version: 4
|
203
216
|
summary: A background job processing with Kubernetes job for Ruby
|
data/docker/Gemfile
DELETED
data/docker/test_worker.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'kube_queue'
|
2
|
-
|
3
|
-
class TestWorker
|
4
|
-
include KubeQueue::Worker
|
5
|
-
|
6
|
-
job_name 'kube-queue-test'
|
7
|
-
image ENV['IMAGE_NAME']
|
8
|
-
container_name 'kube-queue-test'
|
9
|
-
command 'bundle', 'exec', 'kube_queue', 'TestWorker', '-r', './test_worker.rb'
|
10
|
-
|
11
|
-
def perform(payload)
|
12
|
-
puts payload['message']
|
13
|
-
end
|
14
|
-
end
|
data/k8s/service-account.yaml
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
apiVersion: v1
|
2
|
-
kind: ServiceAccount
|
3
|
-
metadata:
|
4
|
-
name: kube-queue-test
|
5
|
-
namespace: kube-system
|
6
|
-
---
|
7
|
-
apiVersion: rbac.authorization.k8s.io/v1
|
8
|
-
kind: ClusterRoleBinding
|
9
|
-
metadata:
|
10
|
-
name: kube-queue-test
|
11
|
-
roleRef:
|
12
|
-
apiGroup: rbac.authorization.k8s.io
|
13
|
-
kind: ClusterRole
|
14
|
-
name: cluster-admin
|
15
|
-
subjects:
|
16
|
-
- kind: ServiceAccount
|
17
|
-
name: kube-queue-test
|
18
|
-
namespace: kube-system
|
data/lib/kube_queue/cli.rb
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
require 'logger'
|
2
|
-
require 'json'
|
3
|
-
require 'optparse'
|
4
|
-
require 'kube_queue/runner'
|
5
|
-
|
6
|
-
module KubeQueue
|
7
|
-
class CLI
|
8
|
-
attr_reader :argv
|
9
|
-
|
10
|
-
def initialize(argv = ARGV)
|
11
|
-
parse_options(argv)
|
12
|
-
|
13
|
-
@argv = argv
|
14
|
-
end
|
15
|
-
|
16
|
-
def run
|
17
|
-
load_files!
|
18
|
-
|
19
|
-
runner = Runner.new(job_name)
|
20
|
-
|
21
|
-
runner.run(payload)
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def job_name
|
27
|
-
argv[0]
|
28
|
-
end
|
29
|
-
|
30
|
-
def payload
|
31
|
-
payload = ENV['KUBE_QUEUE_MESSAGE_PAYLOAD']
|
32
|
-
|
33
|
-
return payload if payload
|
34
|
-
|
35
|
-
raise 'Payload is missing. Please set payload to KUBE_QUEUE_MESSAGE_PAYLOAD environment variable'
|
36
|
-
end
|
37
|
-
|
38
|
-
def load_files!
|
39
|
-
require options[:require] if options[:require]
|
40
|
-
end
|
41
|
-
|
42
|
-
def options
|
43
|
-
@options ||= {}
|
44
|
-
end
|
45
|
-
|
46
|
-
def parse_options(argv)
|
47
|
-
parser = OptionParser.new do |o|
|
48
|
-
o.on '-r', '--require [PATH|DIR]', 'Location of Rails application with workers or file to require' do |arg|
|
49
|
-
options[:require] = arg
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
parser.parse!(argv)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
|
3
|
-
module KubeQueue
|
4
|
-
class JobSpecification
|
5
|
-
extend Forwardable
|
6
|
-
|
7
|
-
attr_accessor :payload, :id, :name
|
8
|
-
|
9
|
-
attr_writer :image, :job_name, :command, :container_name, :template, :backoff_limit, :restart_policy
|
10
|
-
|
11
|
-
def configure
|
12
|
-
yield self
|
13
|
-
self
|
14
|
-
end
|
15
|
-
|
16
|
-
def backoff_limit
|
17
|
-
@backoff_limit || 0
|
18
|
-
end
|
19
|
-
|
20
|
-
def restart_policy
|
21
|
-
@restart_policy || 'Never'
|
22
|
-
end
|
23
|
-
|
24
|
-
def command
|
25
|
-
@command || ['bundle', 'exec', 'kube_queue', name]
|
26
|
-
end
|
27
|
-
|
28
|
-
def image
|
29
|
-
@image || raise
|
30
|
-
end
|
31
|
-
|
32
|
-
def job_name
|
33
|
-
@job_name || raise
|
34
|
-
end
|
35
|
-
|
36
|
-
def container_name
|
37
|
-
@container_name || raise
|
38
|
-
end
|
39
|
-
|
40
|
-
def template
|
41
|
-
@template || File.read(File.expand_path('../../../template/job.yaml', __FILE__))
|
42
|
-
end
|
43
|
-
|
44
|
-
def to_manifest
|
45
|
-
YAML.safe_load(ERB.new(template, nil, "%").result(binding))
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
def binding
|
51
|
-
super
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
data/lib/kube_queue/runner.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
require 'logger'
|
2
|
-
require 'json'
|
3
|
-
|
4
|
-
module KubeQueue
|
5
|
-
class Runner
|
6
|
-
def initialize(job_name)
|
7
|
-
@job_name = job_name
|
8
|
-
end
|
9
|
-
|
10
|
-
def run(payload)
|
11
|
-
payload = JSON.parse(payload)
|
12
|
-
|
13
|
-
worker = KubeQueue.fetch_worker(@job_name)
|
14
|
-
worker.new.perform(payload)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|