kuby-core 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 +7 -0
- data/CHANGELOG.md +2 -0
- data/Gemfile +16 -0
- data/LICENSE +21 -0
- data/README.md +186 -0
- data/Rakefile +14 -0
- data/kuby-core.gemspec +27 -0
- data/lib/kuby.rb +112 -0
- data/lib/kuby/basic_logger.rb +22 -0
- data/lib/kuby/cli_base.rb +75 -0
- data/lib/kuby/definition.rb +29 -0
- data/lib/kuby/docker.rb +27 -0
- data/lib/kuby/docker/alpine.rb +62 -0
- data/lib/kuby/docker/assets_phase.rb +11 -0
- data/lib/kuby/docker/bundler_phase.rb +56 -0
- data/lib/kuby/docker/cli.rb +72 -0
- data/lib/kuby/docker/copy_phase.rb +23 -0
- data/lib/kuby/docker/credentials.rb +11 -0
- data/lib/kuby/docker/debian.rb +66 -0
- data/lib/kuby/docker/dockerfile.rb +128 -0
- data/lib/kuby/docker/errors.rb +22 -0
- data/lib/kuby/docker/layer_stack.rb +42 -0
- data/lib/kuby/docker/local_tags.rb +38 -0
- data/lib/kuby/docker/metadata.rb +69 -0
- data/lib/kuby/docker/package_list.rb +34 -0
- data/lib/kuby/docker/package_phase.rb +59 -0
- data/lib/kuby/docker/packages.rb +10 -0
- data/lib/kuby/docker/packages/managed_package.rb +29 -0
- data/lib/kuby/docker/packages/nodejs.rb +29 -0
- data/lib/kuby/docker/packages/package.rb +22 -0
- data/lib/kuby/docker/packages/yarn.rb +47 -0
- data/lib/kuby/docker/phase.rb +21 -0
- data/lib/kuby/docker/remote_tags.rb +24 -0
- data/lib/kuby/docker/setup_phase.rb +29 -0
- data/lib/kuby/docker/spec.rb +147 -0
- data/lib/kuby/docker/tags.rb +39 -0
- data/lib/kuby/docker/timestamp_tag.rb +36 -0
- data/lib/kuby/docker/webserver_phase.rb +51 -0
- data/lib/kuby/docker/yarn_phase.rb +11 -0
- data/lib/kuby/kubernetes.rb +16 -0
- data/lib/kuby/kubernetes/deployer.rb +94 -0
- data/lib/kuby/kubernetes/docker_config.rb +27 -0
- data/lib/kuby/kubernetes/errors.rb +22 -0
- data/lib/kuby/kubernetes/manifest.rb +56 -0
- data/lib/kuby/kubernetes/minikube_provider.rb +51 -0
- data/lib/kuby/kubernetes/plugin.rb +55 -0
- data/lib/kuby/kubernetes/plugins.rb +8 -0
- data/lib/kuby/kubernetes/plugins/nginx_ingress.rb +61 -0
- data/lib/kuby/kubernetes/plugins/rails_app.rb +16 -0
- data/lib/kuby/kubernetes/plugins/rails_app/database.rb +58 -0
- data/lib/kuby/kubernetes/plugins/rails_app/mysql.rb +142 -0
- data/lib/kuby/kubernetes/plugins/rails_app/plugin.rb +393 -0
- data/lib/kuby/kubernetes/plugins/rails_app/postgres.rb +10 -0
- data/lib/kuby/kubernetes/plugins/rails_app/rewrite_db_config.rb +13 -0
- data/lib/kuby/kubernetes/plugins/rails_app/sqlite.rb +10 -0
- data/lib/kuby/kubernetes/plugins/rails_app/tasks.rake +23 -0
- data/lib/kuby/kubernetes/provider.rb +77 -0
- data/lib/kuby/kubernetes/registry_secret.rb +26 -0
- data/lib/kuby/kubernetes/spec.rb +152 -0
- data/lib/kuby/middleware.rb +5 -0
- data/lib/kuby/middleware/health_check.rb +16 -0
- data/lib/kuby/railtie.rb +18 -0
- data/lib/kuby/tasks.rb +135 -0
- data/lib/kuby/tasks/kuby.rake +63 -0
- data/lib/kuby/trailing_hash.rb +19 -0
- data/lib/kuby/version.rb +3 -0
- metadata +233 -0
@@ -0,0 +1,393 @@
|
|
1
|
+
require 'kube-dsl'
|
2
|
+
require 'kuby/cert-manager'
|
3
|
+
|
4
|
+
module Kuby
|
5
|
+
module Kubernetes
|
6
|
+
module Plugins
|
7
|
+
module RailsApp
|
8
|
+
class Plugin < Kuby::Kubernetes::Plugin
|
9
|
+
extend ::KubeDSL::ValueFields
|
10
|
+
|
11
|
+
WEB_ROLE = 'web'.freeze
|
12
|
+
MASTER_KEY_VAR = 'RAILS_MASTER_KEY'.freeze
|
13
|
+
ENV_SECRETS = [MASTER_KEY_VAR].freeze
|
14
|
+
ENV_EXCLUDE = ['RAILS_ENV'].freeze
|
15
|
+
|
16
|
+
value_fields :hostname, :tls_enabled, :database
|
17
|
+
|
18
|
+
def initialize(definition)
|
19
|
+
@definition = definition
|
20
|
+
@tls_enabled = true
|
21
|
+
end
|
22
|
+
|
23
|
+
def configure(&block)
|
24
|
+
instance_eval(&block) if block
|
25
|
+
end
|
26
|
+
|
27
|
+
def after_configuration
|
28
|
+
# currently Database.get doesn't return nil, but this if statement
|
29
|
+
# is here as a placeholder to indicate we'd like to be able to
|
30
|
+
# handle Rails apps that don't use a database, i.e. don't have
|
31
|
+
# activerecord configured
|
32
|
+
if @database = Database.get(definition)
|
33
|
+
definition.kubernetes.plugins[database] = @database
|
34
|
+
definition.kubernetes.add_plugin(:kube_db)
|
35
|
+
|
36
|
+
definition.docker do
|
37
|
+
insert :rewrite_db_config, RewriteDbConfig.new, after: :copy_phase
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# do we always want this?
|
42
|
+
definition.kubernetes.add_plugin(:nginx_ingress)
|
43
|
+
|
44
|
+
if @tls_enabled
|
45
|
+
definition.kubernetes.add_plugin(:cert_manager)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def before_deploy(manifest)
|
50
|
+
# Make sure plugin has been configured. If not, do nothing.
|
51
|
+
if cert_manager = definition.kubernetes.plugin(:cert_manager)
|
52
|
+
if ing = manifest.find(:ingress, ingress.metadata.name)
|
53
|
+
cert_manager.annotate_ingress(ing)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def database(&block)
|
59
|
+
@database.instance_eval(&block) if block
|
60
|
+
@database
|
61
|
+
end
|
62
|
+
|
63
|
+
def set_image(image_with_tag)
|
64
|
+
deployment do
|
65
|
+
spec do
|
66
|
+
template do
|
67
|
+
spec do
|
68
|
+
container(:web) do
|
69
|
+
image image_with_tag
|
70
|
+
end
|
71
|
+
|
72
|
+
init_container(:create_db) do
|
73
|
+
image image_with_tag
|
74
|
+
end
|
75
|
+
|
76
|
+
init_container(:migrate_db) do
|
77
|
+
image image_with_tag
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def service(&block)
|
86
|
+
spec = self
|
87
|
+
|
88
|
+
@service ||= KubeDSL.service do
|
89
|
+
metadata do
|
90
|
+
name "#{spec.selector_app}-svc"
|
91
|
+
namespace spec.namespace.metadata.name
|
92
|
+
|
93
|
+
labels do
|
94
|
+
add :app, spec.selector_app
|
95
|
+
add :role, spec.role
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
spec do
|
100
|
+
type 'NodePort'
|
101
|
+
|
102
|
+
selector do
|
103
|
+
add :app, spec.selector_app
|
104
|
+
add :role, spec.role
|
105
|
+
end
|
106
|
+
|
107
|
+
port do
|
108
|
+
name 'http'
|
109
|
+
port 8080
|
110
|
+
protocol 'TCP'
|
111
|
+
target_port 'http'
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
@service.instance_eval(&block) if block
|
117
|
+
@service
|
118
|
+
end
|
119
|
+
|
120
|
+
def service_account(&block)
|
121
|
+
spec = self
|
122
|
+
|
123
|
+
@service_account ||= KubeDSL.service_account do
|
124
|
+
metadata do
|
125
|
+
name "#{spec.selector_app}-sa"
|
126
|
+
namespace spec.namespace.metadata.name
|
127
|
+
|
128
|
+
labels do
|
129
|
+
add :app, spec.selector_app
|
130
|
+
add :role, spec.role
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
@service_account.instance_eval(&block) if block
|
136
|
+
@service_account
|
137
|
+
end
|
138
|
+
|
139
|
+
def config_map(&block)
|
140
|
+
spec = self
|
141
|
+
|
142
|
+
@config_map ||= KubeDSL.config_map do
|
143
|
+
metadata do
|
144
|
+
name "#{spec.selector_app}-config"
|
145
|
+
namespace spec.namespace.metadata.name
|
146
|
+
end
|
147
|
+
|
148
|
+
data do
|
149
|
+
ENV.each_pair do |key, val|
|
150
|
+
include_key = key.start_with?('RAILS_') &&
|
151
|
+
!ENV_SECRETS.include?(key) &&
|
152
|
+
!ENV_EXCLUDE.include?(key)
|
153
|
+
|
154
|
+
if include_key
|
155
|
+
add key.to_sym, val
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
@config_map.instance_eval(&block) if block
|
162
|
+
@config_map
|
163
|
+
end
|
164
|
+
|
165
|
+
def app_secrets(&block)
|
166
|
+
spec = self
|
167
|
+
|
168
|
+
@app_secrets ||= KubeDSL.secret do
|
169
|
+
metadata do
|
170
|
+
name "#{spec.selector_app}-secrets"
|
171
|
+
namespace spec.namespace.metadata.name
|
172
|
+
end
|
173
|
+
|
174
|
+
type 'Opaque'
|
175
|
+
|
176
|
+
data do
|
177
|
+
if master_key = ENV[MASTER_KEY_VAR]
|
178
|
+
add MASTER_KEY_VAR.to_sym, master_key
|
179
|
+
else
|
180
|
+
master_key_path = spec.app.root.join('config', 'master.key')
|
181
|
+
|
182
|
+
if master_key_path.exist?
|
183
|
+
add MASTER_KEY_VAR.to_sym, File.read(master_key_path).strip
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
@app_secrets.instance_eval(&block) if block
|
190
|
+
@app_secrets
|
191
|
+
end
|
192
|
+
|
193
|
+
def registry_secret(&block)
|
194
|
+
spec = self
|
195
|
+
|
196
|
+
@registry_secret ||= RegistrySecret.new do
|
197
|
+
metadata do
|
198
|
+
name "#{spec.selector_app}-registry-secret"
|
199
|
+
namespace spec.namespace.metadata.name
|
200
|
+
end
|
201
|
+
|
202
|
+
docker_config do
|
203
|
+
registry_host spec.docker.metadata.image_host
|
204
|
+
username spec.docker.credentials.username
|
205
|
+
password spec.docker.credentials.password
|
206
|
+
email spec.docker.credentials.email
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
@registry_secret.instance_eval(&block) if block
|
211
|
+
@registry_secret
|
212
|
+
end
|
213
|
+
|
214
|
+
def deployment(&block)
|
215
|
+
kube_spec = self
|
216
|
+
|
217
|
+
@deployment ||= KubeDSL.deployment do
|
218
|
+
metadata do
|
219
|
+
name "#{kube_spec.selector_app}-deployment"
|
220
|
+
namespace kube_spec.namespace.metadata.name
|
221
|
+
|
222
|
+
labels do
|
223
|
+
add :app, kube_spec.selector_app
|
224
|
+
add :role, kube_spec.role
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
spec do
|
229
|
+
selector do
|
230
|
+
match_labels do
|
231
|
+
add :app, kube_spec.selector_app
|
232
|
+
add :role, kube_spec.role
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
strategy do
|
237
|
+
type 'RollingUpdate'
|
238
|
+
|
239
|
+
rolling_update do
|
240
|
+
max_surge '25%'
|
241
|
+
max_unavailable 0
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
template do
|
246
|
+
metadata do
|
247
|
+
labels do
|
248
|
+
add :app, kube_spec.selector_app
|
249
|
+
add :role, kube_spec.role
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
spec do
|
254
|
+
container(:web) do
|
255
|
+
name "#{kube_spec.selector_app}-#{kube_spec.role}"
|
256
|
+
image_pull_policy 'IfNotPresent'
|
257
|
+
|
258
|
+
port do
|
259
|
+
container_port kube_spec.docker.webserver_phase.port
|
260
|
+
name 'http'
|
261
|
+
protocol 'TCP'
|
262
|
+
end
|
263
|
+
|
264
|
+
env_from do
|
265
|
+
config_map_ref do
|
266
|
+
name kube_spec.config_map.metadata.name
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
env_from do
|
271
|
+
secret_ref do
|
272
|
+
name kube_spec.app_secrets.metadata.name
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
readiness_probe do
|
277
|
+
success_threshold 1
|
278
|
+
failure_threshold 2
|
279
|
+
initial_delay_seconds 15
|
280
|
+
period_seconds 3
|
281
|
+
timeout_seconds 1
|
282
|
+
|
283
|
+
http_get do
|
284
|
+
path '/healthz'
|
285
|
+
port kube_spec.docker.webserver_phase.port
|
286
|
+
scheme 'HTTP'
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
init_container(:create_db) do
|
292
|
+
name "#{kube_spec.selector_app}-create-db"
|
293
|
+
command %w(bundle exec rake kuby:rails_app:db:create_unless_exists)
|
294
|
+
end
|
295
|
+
|
296
|
+
init_container(:migrate_db) do
|
297
|
+
name "#{kube_spec.selector_app}-migrate-db"
|
298
|
+
command %w(bundle exec rake db:migrate)
|
299
|
+
end
|
300
|
+
|
301
|
+
image_pull_secret do
|
302
|
+
name kube_spec.registry_secret.metadata.name
|
303
|
+
end
|
304
|
+
|
305
|
+
restart_policy 'Always'
|
306
|
+
service_account_name kube_spec.service_account.metadata.name
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
@deployment.instance_eval(&block) if block
|
313
|
+
@deployment
|
314
|
+
end
|
315
|
+
|
316
|
+
def ingress(&block)
|
317
|
+
spec = self
|
318
|
+
tls_enabled = @tls_enabled
|
319
|
+
|
320
|
+
@ingress ||= KubeDSL::DSL::Extensions::V1beta1::Ingress.new do
|
321
|
+
metadata do
|
322
|
+
name "#{spec.selector_app}-ingress"
|
323
|
+
namespace spec.namespace.metadata.name
|
324
|
+
|
325
|
+
annotations do
|
326
|
+
add :'kubernetes.io/ingress.class', 'nginx'
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
spec do
|
331
|
+
rule do
|
332
|
+
host spec.hostname
|
333
|
+
|
334
|
+
http do
|
335
|
+
path do
|
336
|
+
backend do
|
337
|
+
service_name spec.service.metadata.name
|
338
|
+
service_port spec.service.spec.ports.first.port
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
if tls_enabled
|
345
|
+
tls do
|
346
|
+
secret_name "#{spec.selector_app}-tls"
|
347
|
+
hosts [spec.hostname]
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
@ingress.instance_eval(&block) if block
|
354
|
+
@ingress
|
355
|
+
end
|
356
|
+
|
357
|
+
def resources
|
358
|
+
@resources ||= [
|
359
|
+
service,
|
360
|
+
service_account,
|
361
|
+
config_map,
|
362
|
+
app_secrets,
|
363
|
+
registry_secret,
|
364
|
+
deployment,
|
365
|
+
ingress,
|
366
|
+
*database.resources
|
367
|
+
]
|
368
|
+
end
|
369
|
+
|
370
|
+
def selector_app
|
371
|
+
definition.kubernetes.selector_app
|
372
|
+
end
|
373
|
+
|
374
|
+
def role
|
375
|
+
WEB_ROLE
|
376
|
+
end
|
377
|
+
|
378
|
+
def docker
|
379
|
+
definition.docker
|
380
|
+
end
|
381
|
+
|
382
|
+
def app
|
383
|
+
definition.app
|
384
|
+
end
|
385
|
+
|
386
|
+
def namespace
|
387
|
+
definition.kubernetes.namespace
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
namespace :kuby do
|
4
|
+
namespace :rails_app do
|
5
|
+
namespace :db do
|
6
|
+
task rewrite_config: :environment do
|
7
|
+
config_file = Kuby.definition.app.root.join('config', 'database.yml')
|
8
|
+
database = Kuby.definition.kubernetes.plugin(:rails_app).database
|
9
|
+
File.write(config_file, YAML.dump(database.rewritten_configs))
|
10
|
+
Kuby.logger.info("Wrote #{config_file}")
|
11
|
+
end
|
12
|
+
|
13
|
+
task :create_unless_exists do
|
14
|
+
begin
|
15
|
+
Rake::Task['environment'].invoke
|
16
|
+
ActiveRecord::Base.connection
|
17
|
+
rescue ActiveRecord::NoDatabaseError => e
|
18
|
+
Rake::Task['db:create'].invoke
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|