resque-kubernetes 0.10.0 → 1.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 +4 -4
- data/.gitignore +2 -1
- data/Appraisals +9 -0
- data/CHANGELOG.md +15 -0
- data/README.md +37 -19
- data/Rakefile +8 -2
- data/gemfiles/kubeclient_3.gemfile +7 -0
- data/gemfiles/kubeclient_4.gemfile +7 -0
- data/lib/resque/kubernetes/context/kubectl.rb +43 -0
- data/lib/resque/kubernetes/context/well_known.rb +40 -0
- data/lib/resque/kubernetes/context_factory.rb +8 -53
- data/lib/resque/kubernetes/jobs_manager.rb +11 -13
- data/lib/resque/kubernetes/version.rb +1 -1
- data/lib/resque/kubernetes.rb +5 -3
- data/resque-kubernetes.gemspec +2 -1
- metadata +26 -8
- data/lib/resque/kubernetes/worker.rb +0 -52
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd653a29a38d097f457584f8bb8a4b305dc14f1f
|
4
|
+
data.tar.gz: 4ac673bc617fdd91c99fe3b51f0c943f214655f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 142a38eea17c227b6339eef837b58e4835d3fdf05d7f76f85f30cde00c25f6d20b3ee1fa59795b030549283bfcae28eb49da8159449bd3004066c258c5a7b4d0
|
7
|
+
data.tar.gz: 707c7a76de380e87d8da7c4f4651d924f9b7992d5bdedcf48150a0ce8c225cda21d9f252f4a4b907dae373c5c012be2f9ec449bb69d90501a711607f2161b3e2
|
data/.gitignore
CHANGED
data/Appraisals
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
# v1.1.0
|
2
|
+
- Fix design to set `INTERVAL=0` for the worker in the Kubernetes
|
3
|
+
job manifest, which tells `resque` to work until there are no more jobs,
|
4
|
+
rather than monkey-patching `Resque::Worker` to look for `TERM_ON_EMPTY`
|
5
|
+
environment variable.
|
6
|
+
|
7
|
+
# v1.0.0
|
8
|
+
**Breaking Change:**
|
9
|
+
- Requires `kubeclient` 3.1.2 or 4.x
|
10
|
+
|
11
|
+
**Changes:**
|
12
|
+
- Add `kubeclient` configuration option for connecting to any Kubernetes server
|
13
|
+
- Use kubernetes namespace provided by cluster or `kubectl` configuration when available
|
14
|
+
- Add Appraisal for testing with kubeclient 3.1.2 and 4.x
|
15
|
+
|
1
16
|
# v0.10.0
|
2
17
|
- `kubeclient` may not be later than 3.0.0 due to change in signature of `Kubeclient::Config::Context#initialize`
|
3
18
|
in `kubeclient` 3.1.0
|
data/README.md
CHANGED
@@ -7,8 +7,8 @@ the container finishes and then it terminates the pod (as opposed to trying to
|
|
7
7
|
restart the container).
|
8
8
|
|
9
9
|
This gem takes advantage of that feature by starting up a Kubernetes Job to
|
10
|
-
run a worker when a Resque job or ActiveJob is enqueued. It then
|
11
|
-
Resque worker to
|
10
|
+
run a worker when a Resque job or ActiveJob is enqueued. It then tells the
|
11
|
+
Resque worker to run until there are no more jobs in the queue.
|
12
12
|
|
13
13
|
Why would you do this?
|
14
14
|
|
@@ -122,15 +122,18 @@ class ResourceIntensiveJob < ApplicationJob
|
|
122
122
|
end
|
123
123
|
end
|
124
124
|
```
|
125
|
-
|
126
125
|
### Workers (for both)
|
127
126
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
127
|
+
The resque worker can can be any container image that runs the `resque:work` `rake` task, for example:
|
128
|
+
|
129
|
+
```bash
|
130
|
+
bin/rails environment resque:work
|
131
|
+
```
|
132
|
+
|
133
|
+
The gem sets the environment variable `INTERVAL=0` for the Kubernetes Job which the `rake` task uses
|
134
|
+
to when calling `Resque::Worker#work(interval)`. The value 0 tells Resque to terminate when the queue
|
135
|
+
is empty. If your Docker image does not run the rake task, then you'll need to make sure you pass 0
|
136
|
+
for the interval when calling `Resque::Worker#work`.
|
134
137
|
|
135
138
|
### Job manifest
|
136
139
|
|
@@ -200,13 +203,28 @@ class ResourceIntensiveJob
|
|
200
203
|
end
|
201
204
|
```
|
202
205
|
|
203
|
-
|
206
|
+
### kubeclient
|
207
|
+
|
208
|
+
The gem will automatically connect to the Kubernetes server in the following cases:
|
209
|
+
- You are running this in [a standard Kubernetes cluster](https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/#accessing-the-api-from-a-pod)
|
210
|
+
- You are running on a system with `kubeclient` installed and
|
211
|
+
- the default cluster context has credentials
|
212
|
+
- the default cluster is GKE and your system has
|
213
|
+
[Google application default credentials](https://developers.google.com/identity/protocols/application-default-credentials)
|
214
|
+
installed
|
215
|
+
|
216
|
+
There are many other ways to connect and you can do so by providing your own
|
217
|
+
[configured `kubeclient`](https://github.com/abonas/kubeclient#usage):
|
218
|
+
|
219
|
+
```ruby
|
220
|
+
# config/initializers/resque-kubernetes.rb
|
221
|
+
|
222
|
+
Resque::Kubernetes.configuration do |config|
|
223
|
+
config.kubeclient = Kubeclient::Client.new("http://localhost:8080/apis/batch")
|
224
|
+
end
|
225
|
+
```
|
204
226
|
|
205
|
-
|
206
|
-
See [the many examples](https://github.com/abonas/kubeclient#usage) in their
|
207
|
-
README.
|
208
|
-
- We probably need better namespace support, particularly for reaping
|
209
|
-
finished jobs and pods.
|
227
|
+
Because this uses the `Job` resource, make sure to connect to the `/apis/batch` API endpoint in your client.
|
210
228
|
|
211
229
|
## Contributing
|
212
230
|
|
@@ -231,7 +249,7 @@ experiment.
|
|
231
249
|
|
232
250
|
Write test for any code that you add. Test all changes by running `rake`.
|
233
251
|
This does the following, which you can also run separately while working.
|
234
|
-
1.
|
252
|
+
1. Run unit tests: `appraisal rake spec`
|
235
253
|
2. Make sure that your code matches the styles: `rubocop`
|
236
254
|
3. Verify if any dependent gems have open CVEs (you must update these):
|
237
255
|
`rake bundle:audit`
|
@@ -239,10 +257,10 @@ This does the following, which you can also run separately while working.
|
|
239
257
|
### End to End Tests
|
240
258
|
|
241
259
|
We don't run End to End (e2e) tests in the regular suite because
|
242
|
-
they require a connection to a cluster. You
|
243
|
-
|
260
|
+
they require a connection to a cluster. You should run these on your changes
|
261
|
+
to verify that the jobs are created correctly.
|
244
262
|
|
245
|
-
This will use the default authentication on your system, which
|
263
|
+
This will use the default authentication on your system, which is either
|
246
264
|
the cluster the tests are running in (if you are doing that), your `kubclient`
|
247
265
|
configuration, or your Google Default Application Credentials.
|
248
266
|
|
data/Rakefile
CHANGED
@@ -1,16 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "appraisal/task"
|
4
|
+
require "bundler/audit/task"
|
3
5
|
require "bundler/gem_tasks"
|
4
6
|
require "rspec/core/rake_task"
|
5
7
|
require "rubocop/rake_task"
|
6
|
-
require "bundler/audit/task"
|
7
8
|
|
8
9
|
RSpec::Core::RakeTask.new(:spec)
|
9
10
|
RuboCop::RakeTask.new
|
10
11
|
Bundler::Audit::Task.new
|
12
|
+
Appraisal::Task.new
|
11
13
|
|
12
14
|
# Remove default and replace with a series of test tasks
|
13
15
|
task default: []
|
14
16
|
Rake::Task[:default].clear
|
15
17
|
|
16
|
-
|
18
|
+
if ENV["APPRAISAL_INITIALIZED"]
|
19
|
+
task default: %i[spec]
|
20
|
+
else
|
21
|
+
task default: %i[rubocop bundle:audit appraisal]
|
22
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Resque
|
4
|
+
module Kubernetes
|
5
|
+
module Context
|
6
|
+
# Kubeclient Context from `kubectl` config file.
|
7
|
+
class Kubectl
|
8
|
+
def applicable?
|
9
|
+
File.exist?(kubeconfig)
|
10
|
+
end
|
11
|
+
|
12
|
+
def context
|
13
|
+
config = Kubeclient::Config.read(kubeconfig)
|
14
|
+
|
15
|
+
Resque::Kubernetes::ContextFactory::Context.new(
|
16
|
+
config.context.api_endpoint,
|
17
|
+
config.context.api_version,
|
18
|
+
config.context.namespace,
|
19
|
+
auth_options: auth_options(config),
|
20
|
+
ssl_options: config.context.ssl_options
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def kubeconfig
|
27
|
+
File.join(ENV["HOME"], ".kube", "config")
|
28
|
+
end
|
29
|
+
|
30
|
+
def auth_options(config)
|
31
|
+
options = config.context.auth_options
|
32
|
+
return options unless options.empty?
|
33
|
+
google_application_default_credentials
|
34
|
+
end
|
35
|
+
|
36
|
+
def google_application_default_credentials
|
37
|
+
return unless defined?(Google) && defined?(Google::Auth)
|
38
|
+
{bearer_token: Kubeclient::GoogleApplicationDefaultCredentials.token}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Resque
|
4
|
+
module Kubernetes
|
5
|
+
module Context
|
6
|
+
# Kubeclient Context from well-known locations within a Kubernetes cluster.
|
7
|
+
class WellKnown
|
8
|
+
TOKEN_FILE = "/var/run/secrets/kubernetes.io/serviceaccount/token"
|
9
|
+
CA_FILE = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
|
10
|
+
NAMESPACE_FILE = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
|
11
|
+
|
12
|
+
def applicable?
|
13
|
+
File.exist?(TOKEN_FILE)
|
14
|
+
end
|
15
|
+
|
16
|
+
def context
|
17
|
+
Resque::Kubernetes::ContextFactory::Context.new(
|
18
|
+
"https://kubernetes.default.svc",
|
19
|
+
"v1",
|
20
|
+
namespace,
|
21
|
+
auth_options: {bearer_token_file: TOKEN_FILE},
|
22
|
+
ssl_options: ssl_options
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def namespace
|
29
|
+
return nil unless File.exist?(NAMESPACE_FILE)
|
30
|
+
File.read(NAMESPACE_FILE)
|
31
|
+
end
|
32
|
+
|
33
|
+
def ssl_options
|
34
|
+
return {} unless File.exist?(CA_FILE)
|
35
|
+
{ca_file: CA_FILE}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -6,64 +6,19 @@ module Resque
|
|
6
6
|
module Kubernetes
|
7
7
|
# Create a context for `Kubeclient` depending on the environment.
|
8
8
|
class ContextFactory
|
9
|
+
Context = Struct.new(:endpoint, :version, :namespace, :options)
|
10
|
+
|
9
11
|
class << self
|
10
12
|
def context
|
11
13
|
# TODO: Add ability to load this from config
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
kubectl_context
|
14
|
+
[
|
15
|
+
Resque::Kubernetes::Context::WellKnown,
|
16
|
+
Resque::Kubernetes::Context::Kubectl
|
17
|
+
].each do |context_type|
|
18
|
+
context = context_type.new
|
19
|
+
return context.context if context.applicable?
|
19
20
|
end
|
20
21
|
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def well_known_context
|
25
|
-
Kubeclient::Config::Context.new(
|
26
|
-
"https://kubernetes",
|
27
|
-
"v1",
|
28
|
-
{ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"},
|
29
|
-
bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token"
|
30
|
-
)
|
31
|
-
end
|
32
|
-
|
33
|
-
def kubectl_context
|
34
|
-
config = Kubeclient::Config.read(kubeconfig)
|
35
|
-
auth_options = config.context.auth_options
|
36
|
-
|
37
|
-
auth_options = google_default_application_credentials(config) if auth_options.empty?
|
38
|
-
|
39
|
-
Kubeclient::Config::Context.new(
|
40
|
-
config.context.api_endpoint,
|
41
|
-
config.context.api_version,
|
42
|
-
config.context.ssl_options,
|
43
|
-
auth_options
|
44
|
-
)
|
45
|
-
end
|
46
|
-
|
47
|
-
def kubeconfig
|
48
|
-
File.join(ENV["HOME"], ".kube", "config")
|
49
|
-
end
|
50
|
-
|
51
|
-
# TODO: Move this logic to kubeclient. See abonas/kubeclient#213
|
52
|
-
def google_default_application_credentials(config)
|
53
|
-
return unless defined?(Google) && defined?(Google::Auth)
|
54
|
-
|
55
|
-
_cluster, user = config.send(:fetch_context, config.instance_variable_get(:@kcfg)["current-context"])
|
56
|
-
return {} unless user["auth-provider"] && user["auth-provider"]["name"] == "gcp"
|
57
|
-
|
58
|
-
{bearer_token: new_google_token}
|
59
|
-
end
|
60
|
-
|
61
|
-
def new_google_token
|
62
|
-
scopes = ["https://www.googleapis.com/auth/cloud-platform"]
|
63
|
-
authorization = Google::Auth.get_application_default(scopes)
|
64
|
-
authorization.apply({})
|
65
|
-
authorization.access_token
|
66
|
-
end
|
67
22
|
end
|
68
23
|
end
|
69
24
|
end
|
@@ -10,7 +10,8 @@ module Resque
|
|
10
10
|
private :owner
|
11
11
|
|
12
12
|
def initialize(owner)
|
13
|
-
@owner
|
13
|
+
@owner = owner
|
14
|
+
@default_namespace = "default"
|
14
15
|
end
|
15
16
|
|
16
17
|
def reap_finished_jobs
|
@@ -39,20 +40,17 @@ module Resque
|
|
39
40
|
private
|
40
41
|
|
41
42
|
def jobs_client
|
42
|
-
|
43
|
-
@jobs_client = client("/apis/batch")
|
43
|
+
@jobs_client ||= client("/apis/batch")
|
44
44
|
end
|
45
45
|
|
46
46
|
def client(scope)
|
47
|
+
return Resque::Kubernetes.kubeclient if Resque::Kubernetes.kubeclient
|
48
|
+
|
47
49
|
context = ContextFactory.context
|
48
50
|
return unless context
|
51
|
+
@default_namespace = context.namespace if context.namespace
|
49
52
|
|
50
|
-
Kubeclient::Client.new(
|
51
|
-
context.api_endpoint + scope,
|
52
|
-
context.api_version,
|
53
|
-
ssl_options: context.ssl_options,
|
54
|
-
auth_options: context.auth_options
|
55
|
-
)
|
53
|
+
Kubeclient::Client.new(context.endpoint + scope, context.version, context.options)
|
56
54
|
end
|
57
55
|
|
58
56
|
def finished_jobs
|
@@ -92,12 +90,12 @@ module Resque
|
|
92
90
|
|
93
91
|
def container_term_on_empty(container)
|
94
92
|
container["env"] ||= []
|
95
|
-
term_on_empty = container["env"].find { |env| env["name"] == "
|
93
|
+
term_on_empty = container["env"].find { |env| env["name"] == "INTERVAL" }
|
96
94
|
unless term_on_empty
|
97
|
-
term_on_empty = {"name" => "
|
95
|
+
term_on_empty = {"name" => "INTERVAL"}
|
98
96
|
container["env"] << term_on_empty
|
99
97
|
end
|
100
|
-
term_on_empty["value"] = "
|
98
|
+
term_on_empty["value"] = "0"
|
101
99
|
end
|
102
100
|
|
103
101
|
def ensure_reset_policy(manifest)
|
@@ -105,7 +103,7 @@ module Resque
|
|
105
103
|
end
|
106
104
|
|
107
105
|
def ensure_namespace(manifest)
|
108
|
-
manifest["metadata"]["namespace"] ||=
|
106
|
+
manifest["metadata"]["namespace"] ||= @default_namespace
|
109
107
|
end
|
110
108
|
|
111
109
|
def update_job_name(manifest)
|
data/lib/resque/kubernetes.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "resque/kubernetes/configurable"
|
4
|
+
require "resque/kubernetes/context/kubectl"
|
5
|
+
require "resque/kubernetes/context/well_known"
|
4
6
|
require "resque/kubernetes/context_factory"
|
5
7
|
require "resque/kubernetes/deep_hash"
|
6
8
|
require "resque/kubernetes/dns_safe_random"
|
7
9
|
require "resque/kubernetes/job"
|
8
10
|
require "resque/kubernetes/jobs_manager"
|
9
11
|
require "resque/kubernetes/version"
|
10
|
-
require "resque/kubernetes/worker"
|
11
12
|
|
12
13
|
module Resque
|
13
14
|
# Run Resque Jobs as Kubernetes Jobs with autoscaling.
|
@@ -19,7 +20,8 @@ module Resque
|
|
19
20
|
|
20
21
|
# Limit the number of workers that should be spun up, default 10
|
21
22
|
define_setting :max_workers, 10
|
23
|
+
|
24
|
+
# A `kubeclient` for connection context, default attempts to read from cluster or `~/.kube/config`
|
25
|
+
define_setting :kubeclient, nil
|
22
26
|
end
|
23
27
|
end
|
24
|
-
|
25
|
-
Resque::Worker.include Resque::Kubernetes::Worker
|
data/resque-kubernetes.gemspec
CHANGED
@@ -23,6 +23,7 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
24
|
spec.require_paths = ["lib"]
|
25
25
|
|
26
|
+
spec.add_development_dependency "appraisal"
|
26
27
|
spec.add_development_dependency "bundler", "~> 1.16"
|
27
28
|
spec.add_development_dependency "bundler-audit", "~> 0"
|
28
29
|
spec.add_development_dependency "googleauth", "~> 0.6"
|
@@ -30,6 +31,6 @@ Gem::Specification.new do |spec|
|
|
30
31
|
spec.add_development_dependency "rspec", "~> 3.7"
|
31
32
|
spec.add_development_dependency "rubocop", "~> 0.52", ">= 0.52.1"
|
32
33
|
|
33
|
-
spec.add_dependency "kubeclient", ">=
|
34
|
+
spec.add_dependency "kubeclient", ">= 3.1.2", "< 5.0"
|
34
35
|
spec.add_dependency "resque", "~> 1.26"
|
35
36
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resque-kubernetes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Wadsack
|
@@ -10,6 +10,20 @@ bindir: exe
|
|
10
10
|
cert_chain: []
|
11
11
|
date: 2018-09-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: appraisal
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -106,20 +120,20 @@ dependencies:
|
|
106
120
|
requirements:
|
107
121
|
- - ">="
|
108
122
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
110
|
-
- - "
|
123
|
+
version: 3.1.2
|
124
|
+
- - "<"
|
111
125
|
- !ruby/object:Gem::Version
|
112
|
-
version:
|
126
|
+
version: '5.0'
|
113
127
|
type: :runtime
|
114
128
|
prerelease: false
|
115
129
|
version_requirements: !ruby/object:Gem::Requirement
|
116
130
|
requirements:
|
117
131
|
- - ">="
|
118
132
|
- !ruby/object:Gem::Version
|
119
|
-
version:
|
120
|
-
- - "
|
133
|
+
version: 3.1.2
|
134
|
+
- - "<"
|
121
135
|
- !ruby/object:Gem::Version
|
122
|
-
version:
|
136
|
+
version: '5.0'
|
123
137
|
- !ruby/object:Gem::Dependency
|
124
138
|
name: resque
|
125
139
|
requirement: !ruby/object:Gem::Requirement
|
@@ -147,6 +161,7 @@ files:
|
|
147
161
|
- ".rubocop.yml"
|
148
162
|
- ".ruby-gemset"
|
149
163
|
- ".ruby-version"
|
164
|
+
- Appraisals
|
150
165
|
- CHANGELOG.md
|
151
166
|
- Gemfile
|
152
167
|
- LICENSE.txt
|
@@ -154,15 +169,18 @@ files:
|
|
154
169
|
- Rakefile
|
155
170
|
- bin/console
|
156
171
|
- bin/setup
|
172
|
+
- gemfiles/kubeclient_3.gemfile
|
173
|
+
- gemfiles/kubeclient_4.gemfile
|
157
174
|
- lib/resque/kubernetes.rb
|
158
175
|
- lib/resque/kubernetes/configurable.rb
|
176
|
+
- lib/resque/kubernetes/context/kubectl.rb
|
177
|
+
- lib/resque/kubernetes/context/well_known.rb
|
159
178
|
- lib/resque/kubernetes/context_factory.rb
|
160
179
|
- lib/resque/kubernetes/deep_hash.rb
|
161
180
|
- lib/resque/kubernetes/dns_safe_random.rb
|
162
181
|
- lib/resque/kubernetes/job.rb
|
163
182
|
- lib/resque/kubernetes/jobs_manager.rb
|
164
183
|
- lib/resque/kubernetes/version.rb
|
165
|
-
- lib/resque/kubernetes/worker.rb
|
166
184
|
- resque-kubernetes.gemspec
|
167
185
|
homepage: https://github.com/keylimetoolbox/resque-kubernetes
|
168
186
|
licenses:
|
@@ -1,52 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "resque"
|
4
|
-
|
5
|
-
module Resque
|
6
|
-
module Kubernetes
|
7
|
-
# Patches the resque worker to terminate when the queue is empty.
|
8
|
-
#
|
9
|
-
# This patch enables setting an environment variable, `TERM_ON_EMPTY`
|
10
|
-
# that causes the worker to shutdown when the queue no longer has any
|
11
|
-
# resque jobs. This allows running workers as Kuberenetes Jobs that will
|
12
|
-
# terminate when there is no longer any work to do.
|
13
|
-
#
|
14
|
-
# To use, make sure that the container images that hold your workers are
|
15
|
-
# built to include the `resque-kubernetes` gem and set `TERM_ON_EMPTY` in
|
16
|
-
# their environment to a truthy value (e.g. "1").
|
17
|
-
module Worker
|
18
|
-
def self.included(base)
|
19
|
-
base.class_eval do
|
20
|
-
prepend InstanceMethods
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
attr_accessor :term_on_empty
|
25
|
-
|
26
|
-
# Replace methods on the worker instance
|
27
|
-
module InstanceMethods
|
28
|
-
def prepare
|
29
|
-
self.term_on_empty = ENV["TERM_ON_EMPTY"] if ENV["TERM_ON_EMPTY"]
|
30
|
-
super
|
31
|
-
end
|
32
|
-
|
33
|
-
def shutdown?
|
34
|
-
if term_on_empty
|
35
|
-
if queues_empty?
|
36
|
-
log_with_severity :info, "shutdown: queues are empty"
|
37
|
-
shutdown
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
super
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
def queues_empty?
|
48
|
-
queues.all? { |queue| Resque.size(queue).zero? }
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|