kuby-core 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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