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.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2 -0
  3. data/Gemfile +16 -0
  4. data/LICENSE +21 -0
  5. data/README.md +186 -0
  6. data/Rakefile +14 -0
  7. data/kuby-core.gemspec +27 -0
  8. data/lib/kuby.rb +112 -0
  9. data/lib/kuby/basic_logger.rb +22 -0
  10. data/lib/kuby/cli_base.rb +75 -0
  11. data/lib/kuby/definition.rb +29 -0
  12. data/lib/kuby/docker.rb +27 -0
  13. data/lib/kuby/docker/alpine.rb +62 -0
  14. data/lib/kuby/docker/assets_phase.rb +11 -0
  15. data/lib/kuby/docker/bundler_phase.rb +56 -0
  16. data/lib/kuby/docker/cli.rb +72 -0
  17. data/lib/kuby/docker/copy_phase.rb +23 -0
  18. data/lib/kuby/docker/credentials.rb +11 -0
  19. data/lib/kuby/docker/debian.rb +66 -0
  20. data/lib/kuby/docker/dockerfile.rb +128 -0
  21. data/lib/kuby/docker/errors.rb +22 -0
  22. data/lib/kuby/docker/layer_stack.rb +42 -0
  23. data/lib/kuby/docker/local_tags.rb +38 -0
  24. data/lib/kuby/docker/metadata.rb +69 -0
  25. data/lib/kuby/docker/package_list.rb +34 -0
  26. data/lib/kuby/docker/package_phase.rb +59 -0
  27. data/lib/kuby/docker/packages.rb +10 -0
  28. data/lib/kuby/docker/packages/managed_package.rb +29 -0
  29. data/lib/kuby/docker/packages/nodejs.rb +29 -0
  30. data/lib/kuby/docker/packages/package.rb +22 -0
  31. data/lib/kuby/docker/packages/yarn.rb +47 -0
  32. data/lib/kuby/docker/phase.rb +21 -0
  33. data/lib/kuby/docker/remote_tags.rb +24 -0
  34. data/lib/kuby/docker/setup_phase.rb +29 -0
  35. data/lib/kuby/docker/spec.rb +147 -0
  36. data/lib/kuby/docker/tags.rb +39 -0
  37. data/lib/kuby/docker/timestamp_tag.rb +36 -0
  38. data/lib/kuby/docker/webserver_phase.rb +51 -0
  39. data/lib/kuby/docker/yarn_phase.rb +11 -0
  40. data/lib/kuby/kubernetes.rb +16 -0
  41. data/lib/kuby/kubernetes/deployer.rb +94 -0
  42. data/lib/kuby/kubernetes/docker_config.rb +27 -0
  43. data/lib/kuby/kubernetes/errors.rb +22 -0
  44. data/lib/kuby/kubernetes/manifest.rb +56 -0
  45. data/lib/kuby/kubernetes/minikube_provider.rb +51 -0
  46. data/lib/kuby/kubernetes/plugin.rb +55 -0
  47. data/lib/kuby/kubernetes/plugins.rb +8 -0
  48. data/lib/kuby/kubernetes/plugins/nginx_ingress.rb +61 -0
  49. data/lib/kuby/kubernetes/plugins/rails_app.rb +16 -0
  50. data/lib/kuby/kubernetes/plugins/rails_app/database.rb +58 -0
  51. data/lib/kuby/kubernetes/plugins/rails_app/mysql.rb +142 -0
  52. data/lib/kuby/kubernetes/plugins/rails_app/plugin.rb +393 -0
  53. data/lib/kuby/kubernetes/plugins/rails_app/postgres.rb +10 -0
  54. data/lib/kuby/kubernetes/plugins/rails_app/rewrite_db_config.rb +13 -0
  55. data/lib/kuby/kubernetes/plugins/rails_app/sqlite.rb +10 -0
  56. data/lib/kuby/kubernetes/plugins/rails_app/tasks.rake +23 -0
  57. data/lib/kuby/kubernetes/provider.rb +77 -0
  58. data/lib/kuby/kubernetes/registry_secret.rb +26 -0
  59. data/lib/kuby/kubernetes/spec.rb +152 -0
  60. data/lib/kuby/middleware.rb +5 -0
  61. data/lib/kuby/middleware/health_check.rb +16 -0
  62. data/lib/kuby/railtie.rb +18 -0
  63. data/lib/kuby/tasks.rb +135 -0
  64. data/lib/kuby/tasks/kuby.rake +63 -0
  65. data/lib/kuby/trailing_hash.rb +19 -0
  66. data/lib/kuby/version.rb +3 -0
  67. 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,10 @@
1
+ module Kuby
2
+ module Kubernetes
3
+ module Plugins
4
+ module RailsApp
5
+ class Postgres < Kuby::Kubernetes::Plugin
6
+ end
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ module Kuby
2
+ module Kubernetes
3
+ module Plugins
4
+ module RailsApp
5
+ class RewriteDbConfig
6
+ def apply_to(dockerfile)
7
+ dockerfile.run('bundle exec rake kuby:rails_app:db:rewrite_config')
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ module Kuby
2
+ module Kubernetes
3
+ module Plugins
4
+ module RailsApp
5
+ class Sqlite < Kuby::Kubernetes::Plugin
6
+ end
7
+ end
8
+ end
9
+ end
10
+ 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