resque-kubernetes 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5f22de9195620c7f00040e39027aebaefccb4e52
4
- data.tar.gz: 524587419d578689b3719c16bf21105a26fdfa03
3
+ metadata.gz: acdff0738bfd9d4f3aeb8c2ba1f8eb744b5d3f59
4
+ data.tar.gz: 80bafaa288815eb1ddfdea7041e0edc302b5e7f0
5
5
  SHA512:
6
- metadata.gz: 428eba2f1ba7707ccc9992a684530b50b83f280b824b297984b583ac068d3fdf738bcd8edd5806ac49865d9666fea41cd4f6a7a255790955d0c48c2d8a232113
7
- data.tar.gz: e87c50ee0bd9742e5495707069bbf97dd90a7f080bf78a5865a39ef912f6f59889d5feddddce309f5469ebfe67c9955c9909561714fe940d71994f0f3c6ed869
6
+ metadata.gz: a542e73f31e4e9eff7194ef03e356185d4e0cd89820b3f59b043b5521c58b557493ec76c37d2cd6c838e640daa66581aff8a5e7a44d212886743210f943733f8
7
+ data.tar.gz: 46132bfd101f9353cdc0eddd3777825ee93f893f2d3ddb9c0c33f645a75b3a477898f6f77c9bff02bb62c02d0ecbb5c5fc615a9f6355c37acb5edbccfa148c68
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ *.gem
data/.rubocop.yml CHANGED
@@ -34,7 +34,7 @@ Style/AsciiComments:
34
34
 
35
35
  Metrics/BlockLength:
36
36
  Exclude:
37
- - "keylime-service-api.gemspec"
37
+ - "*.gemspec"
38
38
  - "spec/**/*"
39
39
 
40
40
  Layout/EmptyLinesAroundBlockBody:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # 0.6.0
2
+ - Add support for ActiveJob when configured to be backed by Resque
3
+ - When authorizing with `~/.kube/config` use Google Default Application Credentials rather than require a
4
+ forked version of `kubeclient`
1
5
  # 0.5.0
2
6
  - Maximum workers can no be configured per job type
3
7
  - Fix a crash when cleaning up a job that was removed by another process
data/README.md CHANGED
@@ -1,28 +1,28 @@
1
1
  # Resque::Kubernetes
2
2
 
3
- Run Resque Jobs as Kubernetes Jobs!
3
+ Run Resque (and ActiveJob) Workers as Kubernetes Jobs!
4
4
 
5
5
  Kubernetes has a concept of "Job" which is a pod that runs a container until
6
6
  the container finishes and then it terminates the pod (as opposed to trying to
7
7
  restart the container).
8
8
 
9
- This gem takes advantage of that feature by starting up a Kubernetes Job when
10
- a Resque Job is enqueued. It then allows the Resque Worker to be modified to
11
- terminate when there are no more jobs in the queue.
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 allows the
11
+ Resque worker to be modified to terminate when there are no more jobs in the queue.
12
12
 
13
13
  Why would you do this?
14
14
 
15
15
  We have unpredictable, resource-intensive jobs. Rather than dedicating large
16
16
  nodes in our cluster to run the resque workers, where the resources would be
17
17
  idle when there are no jobs to run, we can use auto-scaling to add nodes when
18
- Kubernetes Job gets created and shut them down when those jobs are complete.
18
+ a Kubernetes Job gets created and shut them down when those jobs are complete.
19
19
 
20
20
  ## Installation
21
21
 
22
22
  Add this line to your application's Gemfile:
23
23
 
24
24
  ```ruby
25
- gem 'resque-kubernetes'
25
+ gem "resque-kubernetes"
26
26
  ```
27
27
 
28
28
  And then execute:
@@ -35,42 +35,96 @@ Or install it yourself as:
35
35
 
36
36
  ## Usage
37
37
 
38
- For any Resque job that you want to run in a Kubernetes job, you'll need to
39
- modify the job class with two things:
38
+ This works with native/pure Resque jobs and with ActiveJob _backed by Resque_.
39
+ Under ActiveJob, the workers are still Resque workers, so the same set up
40
+ applies. You just configure the job class differently.
40
41
 
41
- - extend the class with `Resque::Kubernetes::Job`
42
- - and add a method `job_manifest` that returns the Kubernetes manifest for the job
42
+ ### Pure Resque
43
+
44
+ For any Resque job that you want to run in a Kubernetes job, you'll need
45
+ to modify the class with two things:
46
+
47
+ - `extend` the class with `Resque::Kubernetes::Job`
48
+ - add a class method `job_manifest` that returns the Kubernetes manifest for the job
49
+ as a `Hash`
43
50
 
44
51
  ```ruby
45
52
  class ResourceIntensiveJob
46
53
  extend Resque::Kubernetes::Job
47
-
54
+
55
+ class << self
56
+ def perform
57
+ # ... your existing code
58
+ end
59
+
60
+ def job_manifest
61
+ YAML.safe_load(
62
+ <<~MANIFEST
63
+ apiVersion: batch/v1
64
+ kind: Job
65
+ metadata:
66
+ name: worker-job
67
+ spec:
68
+ template:
69
+ metadata:
70
+ name: worker-job
71
+ spec:
72
+ containers:
73
+ - name: worker
74
+ image: us.gcr.io/project-id/some-resque-worker
75
+ env:
76
+ - name: QUEUE
77
+ value: high-memory
78
+ MANIFEST
79
+ )
80
+ end
81
+ end
82
+ end
83
+ ```
84
+
85
+ ### ActiveJob (on Resque)
86
+
87
+ For any ActiveJob that you want to run in a Kubernetes job, you'll need to
88
+ modify the class with two things:
89
+
90
+ - `include` `Resque::Kubernetes::Job` in the class
91
+ - add an instance method `job_manifest` that returns the Kubernetes manifest for the job
92
+ as a `Hash`
93
+
94
+ ```ruby
95
+ class ResourceIntensiveJob < ApplicationJob
96
+ include Resque::Kubernetes::Job
97
+
48
98
  def perform
49
99
  # ... your existing code
50
100
  end
51
-
101
+
52
102
  def job_manifest
53
- <<-EOD
54
- apiVersion: batch/v1
55
- kind: Job
56
- metadata:
57
- name: worker-job
58
- spec:
59
- template:
60
- metadata:
61
- name: worker-job
62
- spec:
63
- containers:
64
- - name: worker
65
- image: us.gcr.io/project-id/some-resque-worker
66
- env:
67
- - name: QUEUE
68
- value: high-memory
69
- EOD
103
+ YAML.safe_load(
104
+ <<~MANIFEST
105
+ apiVersion: batch/v1
106
+ kind: Job
107
+ metadata:
108
+ name: worker-job
109
+ spec:
110
+ template:
111
+ metadata:
112
+ name: worker-job
113
+ spec:
114
+ containers:
115
+ - name: worker
116
+ image: us.gcr.io/project-id/some-resque-worker
117
+ env:
118
+ - name: QUEUE
119
+ value: high-memory
120
+ MANIFEST
121
+ )
70
122
  end
71
123
  end
72
124
  ```
73
125
 
126
+ ### Workers (for both)
127
+
74
128
  Make sure that the container image above, which is used to run the resque
75
129
  worker, is built to include the `resque-kubernetes` gem as well. The gem will
76
130
  add `TERM_ON_EMPTY` to the environment variables. This tells the worker that
@@ -78,6 +132,13 @@ whenever the queue is empty it should terminate the worker. Kubernetes will
78
132
  then terminate the Job when the container is done running and will release the
79
133
  resources.
80
134
 
135
+ ### Job manifest
136
+
137
+ In the example above we show the manifest as a HEREDOC, just to make it
138
+ simple. But you could also read this from a file, parse a template and insert
139
+ values, or anything else you want to do in the method, as long as you return
140
+ a valid Kubernetes Job manifest as a `Hash`.
141
+
81
142
  ## Configuration
82
143
 
83
144
  You can modify the configuration of the gem by creating an initializer in
@@ -87,28 +148,26 @@ your project:
87
148
  # config/initializers/resque-kubernetes.rb
88
149
 
89
150
  Resque::Kubernetes.configuration do |config|
90
-
91
151
  config.environments << "staging"
92
152
  config.max_workers = 10
93
-
94
153
  end
95
154
  ```
96
155
 
97
156
  ### `environments`
98
157
 
158
+ > This only works under Rails, when `Rails.env` is set.
159
+
99
160
  By default `Resque::Kubernetes` will only manage Kubernetes Jobs in
100
161
  `:production`. If you want to add other environments you can update this list
101
162
  (`config.environments << "staging"`) or replace it (`config.environments =
102
163
  ["production", "development"]`).
103
164
 
104
- Note that this only works under Rails, when `Rails.env` is set.
105
-
106
165
  ### `max_workers`
107
166
 
108
167
  `Resque::Kubernetes` will spin up a Kuberentes Job each time you enqueue a
109
168
  Resque Job. This allows for parallel processing of jobs using the resources
110
- available to your cluster. By default this is limited to 10 workers, so an not
111
- to have run-away cloud resource usage.
169
+ available to your cluster. By default this is limited to 10 workers, to prevent
170
+ run-away cloud resource usage.
112
171
 
113
172
  You can set this higher if you need massive scaling and your structure supports
114
173
  it.
@@ -116,7 +175,7 @@ it.
116
175
  If you don't want more than one job running at a time then set this to 1.
117
176
 
118
177
  Beyond this global scope you can adjust the total number of workers on each
119
- individual Resque Job type by overriding the `max_workers` class method for the job.
178
+ individual Resque Job type by overriding the `max_workers` method for the job.
120
179
  If you change this, the value returned by that method takes precedence over the
121
180
  global value.
122
181
 
@@ -124,38 +183,43 @@ global value.
124
183
  class ResourceIntensiveJob
125
184
  extend Resque::Kubernetes::Job
126
185
 
127
- def perform
128
- # ...
129
- end
186
+ class << self
187
+ def perform
188
+ # ...
189
+ end
130
190
 
131
- def job_manifest
132
- # ...
133
- end
191
+ def job_manifest
192
+ # ...
193
+ end
134
194
 
135
- def max_workers
136
- # Simply return an integer value, or do something more complicated if needed.
137
- 105
195
+ def max_workers
196
+ # Simply return an integer value, or do something more complicated if needed.
197
+ 105
198
+ end
138
199
  end
139
200
  end
140
201
  ```
141
202
 
142
203
  ## To Do
143
204
 
144
- - We probably need better namespace support, particularly for reaping
145
- finished jobs and pods.
146
205
  - Support for other authentication and server URL options for `kubeclient`.
147
206
  See [the many examples](https://github.com/abonas/kubeclient#usage) in their
148
207
  README.
208
+ - We probably need better namespace support, particularly for reaping
209
+ finished jobs and pods.
149
210
 
150
211
  ## Development
151
212
 
152
213
  After checking out the repo, run `bin/setup` to install dependencies. Then,
153
- run `rake spec` to run the tests. You can also run `bin/console` for an
154
- interactive prompt that will allow you to experiment.
214
+ run `rake spec` to run the tests.
215
+
216
+ You can run `bin/console` for an interactive prompt that will allow you to experiment.
217
+
218
+ To install this gem onto your local machine, run `bundle exec rake install`.
155
219
 
156
- To install this gem onto your local machine, run `bundle exec rake install`.
157
- To release a new version, update the version number in `version.rb`, and then
158
- run `bundle exec rake release`, which will create a git tag for the version,
220
+ To release a new version, update the version number in
221
+ `lib/resque/kubernetes/version.rb` and the `CHANGELOG.md`, then run
222
+ `bundle exec rake release`, which will create a git tag for the version,
159
223
  push git commits and tags, and push the `.gem` file to
160
224
  [rubygems.org](https://rubygems.org).
161
225
 
@@ -32,17 +32,38 @@ module Resque
32
32
 
33
33
  def kubectl_context
34
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
+
35
39
  Kubeclient::Config::Context.new(
36
40
  config.context.api_endpoint,
37
41
  config.context.api_version,
38
42
  config.context.ssl_options,
39
- use_default_gcp: true
43
+ auth_options
40
44
  )
41
45
  end
42
46
 
43
47
  def kubeconfig
44
48
  File.join(ENV["HOME"], ".kube", "config")
45
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
46
67
  end
47
68
  end
48
69
  end
@@ -6,39 +6,83 @@ module Resque
6
6
  module Kubernetes
7
7
  # Resque hook to autoscale Kubernetes Jobs for workers.
8
8
  #
9
- # To use, extend your Resque job class with this module and then define a
10
- # class method `job_manifest` that produces the Kubernetes Job manifest.
9
+ # To use with pure Resque, extend your Resque job class with this module
10
+ # and then define a class method `job_manifest` that produces the
11
+ # Kubernetes Job manifest.
11
12
  #
12
- # Example:
13
+ # To use with ActiveJob, include this module in your ActiveJob class
14
+ # and then define an instance method `job_manifest` that produces the
15
+ # Kubernetes Job manifest.
16
+ #
17
+ # Example (pure Resque):
13
18
  #
14
19
  # class ResourceIntensiveJob
15
20
  # extend Resque::Kubernetes::Job
21
+ # class << self
22
+ # def perform
23
+ # # ... your existing code
24
+ # end
25
+ #
26
+ # def job_manifest
27
+ # YAML.safe_load(
28
+ # <<~MANIFEST
29
+ # apiVersion: batch/v1
30
+ # kind: Job
31
+ # metadata:
32
+ # name: worker-job
33
+ # spec:
34
+ # template:
35
+ # metadata:
36
+ # name: worker-job
37
+ # spec:
38
+ # containers:
39
+ # - name: worker
40
+ # image: us.gcr.io/project-id/some-resque-worker
41
+ # env:
42
+ # - name: QUEUE
43
+ # value: high-memory
44
+ # MANIFEST
45
+ # )
46
+ # end
47
+ # end
48
+ # end
16
49
  #
50
+ # Example (ActiveJob backed by Resque):
51
+ #
52
+ # class ResourceIntensiveJob < ApplicationJob
53
+ # include Resque::Kubernetes::Job
17
54
  # def perform
18
55
  # # ... your existing code
19
56
  # end
20
57
  #
21
58
  # def job_manifest
22
- # <<-EOD
23
- # apiVersion: batch/v1
24
- # kind: Job
25
- # metadata:
26
- # name: worker-job
27
- # spec:
28
- # template:
29
- # metadata:
30
- # name: worker-job
31
- # spec:
32
- # containers:
33
- # - name: worker
34
- # image: us.gcr.io/project-id/some-resque-worker
35
- # env:
36
- # - name: QUEUE
37
- # value: high-memory
38
- # EOD
59
+ # YAML.safe_load(
60
+ # <<~MANIFEST
61
+ # apiVersion: batch/v1
62
+ # kind: Job
63
+ # metadata:
64
+ # name: worker-job
65
+ # spec:
66
+ # template:
67
+ # metadata:
68
+ # name: worker-job
69
+ # spec:
70
+ # containers:
71
+ # - name: worker
72
+ # image: us.gcr.io/project-id/some-resque-worker
73
+ # env:
74
+ # - name: QUEUE
75
+ # value: high-memory
76
+ # MANIFEST
77
+ # )
39
78
  # end
40
79
  # end
41
80
  module Job
81
+ def self.included(base)
82
+ return unless base.respond_to?(:before_enqueue)
83
+ base.before_enqueue :before_enqueue_kubernetes_job
84
+ end
85
+
42
86
  # A before_enqueue hook that adds worker jobs to the cluster.
43
87
  def before_enqueue_kubernetes_job(*_)
44
88
  if defined? Rails
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Resque
4
4
  module Kubernetes
5
- VERSION = "0.5.0"
5
+ VERSION = "0.6.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque-kubernetes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Wadsack
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-02-01 00:00:00.000000000 Z
11
+ date: 2018-04-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -127,7 +127,6 @@ files:
127
127
  - ".rubocop.yml"
128
128
  - ".ruby-gemset"
129
129
  - ".ruby-version"
130
- - ".travis.yml"
131
130
  - CHANGELOG.md
132
131
  - Gemfile
133
132
  - LICENSE.txt
data/.travis.yml DELETED
@@ -1,5 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - 2.3.1
5
- before_install: gem install bundler -v 1.13.6