kuby-core 0.8.1 → 0.9.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -1
  3. data/README.md +11 -1
  4. data/bin/kuby +4 -0
  5. data/kuby-core.gemspec +5 -2
  6. data/lib/kuby.rb +46 -18
  7. data/lib/kuby/basic_logger.rb +13 -0
  8. data/lib/kuby/cli_base.rb +81 -8
  9. data/lib/kuby/commands.rb +220 -0
  10. data/lib/kuby/definition.rb +1 -3
  11. data/lib/kuby/dev_setup.rb +255 -0
  12. data/lib/kuby/docker.rb +1 -0
  13. data/lib/kuby/docker/bundler_phase.rb +3 -3
  14. data/lib/kuby/docker/cli.rb +13 -1
  15. data/lib/kuby/docker/dev_spec.rb +131 -0
  16. data/lib/kuby/docker/dockerfile.rb +16 -1
  17. data/lib/kuby/docker/layer_stack.rb +4 -0
  18. data/lib/kuby/docker/local_tags.rb +4 -0
  19. data/lib/kuby/docker/metadata.rb +0 -22
  20. data/lib/kuby/docker/setup_phase.rb +3 -2
  21. data/lib/kuby/docker/spec.rb +31 -5
  22. data/lib/kuby/environment.rb +10 -1
  23. data/lib/kuby/kubernetes.rb +9 -9
  24. data/lib/kuby/kubernetes/deploy_task.rb +4 -0
  25. data/lib/kuby/kubernetes/deployer.rb +63 -11
  26. data/lib/kuby/kubernetes/{minikube_provider.rb → docker_desktop_provider.rb} +4 -4
  27. data/lib/kuby/kubernetes/provider.rb +8 -4
  28. data/lib/kuby/kubernetes/spec.rb +23 -22
  29. data/lib/kuby/plugin_registry.rb +27 -0
  30. data/lib/kuby/plugins/rails_app/generators/kuby.rb +3 -15
  31. data/lib/kuby/plugins/rails_app/plugin.rb +230 -40
  32. data/lib/kuby/rails_commands.rb +89 -0
  33. data/lib/kuby/railtie.rb +0 -4
  34. data/lib/kuby/tasks.rb +76 -23
  35. data/lib/kuby/version.rb +1 -1
  36. data/spec/docker/metadata_spec.rb +0 -108
  37. data/spec/docker/spec_spec.rb +266 -0
  38. data/spec/spec_helper.rb +8 -1
  39. metadata +44 -9
  40. data/lib/kuby/tasks/kuby.rake +0 -70
@@ -0,0 +1,27 @@
1
+ module Kuby
2
+ class PluginRegistry
3
+ ANY = 'any'.freeze
4
+
5
+ def register(plugin_name, plugin_klass, environment: ANY)
6
+ plugins[plugin_name] ||= {}
7
+ plugins[plugin_name][environment] ||= plugin_klass
8
+ end
9
+
10
+ def find(plugin_name, environment: Kuby.env)
11
+ plugins_by_env = plugins[plugin_name]
12
+
13
+ unless plugins_by_env
14
+ raise MissingPluginError, "no plugin registered with name #{plugin_name}, "\
15
+ 'do you need to add a gem to your Gemfile?'
16
+ end
17
+
18
+ plugins_by_env[environment] || plugins_by_env[ANY]
19
+ end
20
+
21
+ private
22
+
23
+ def plugins
24
+ @plugins ||= {}
25
+ end
26
+ end
27
+ end
@@ -2,17 +2,6 @@ require 'rails/generators'
2
2
  require 'rails/generators/base'
3
3
 
4
4
  class KubyGenerator < Rails::Generators::Base
5
- def create_initializer_file
6
- initializer(
7
- 'kuby.rb',
8
- <<~END
9
- require 'kuby'
10
-
11
- Kuby.load!
12
- END
13
- )
14
- end
15
-
16
5
  def create_config_file
17
6
  create_file(
18
7
  'kuby.rb',
@@ -48,15 +37,14 @@ class KubyGenerator < Rails::Generators::Base
48
37
  # Add a plugin that facilitates deploying a Rails app.
49
38
  add_plugin :rails_app
50
39
 
51
- # Use minikube as the provider, which is the default installed by
52
- # Docker Desktop.
53
- # See: https://github.com/kubernetes/minikube
40
+ # Use Docker Desktop as the provider.
41
+ # See: https://www.docker.com/products/docker-desktop
54
42
  #
55
43
  # Note: you will likely want to use a different provider when deploying
56
44
  # your application into a production environment. To configure a different
57
45
  # provider, add the corresponding gem to your gemfile and update the
58
46
  # following line according to the provider gem's README.
59
- provider :minikube
47
+ provider :docker_desktop
60
48
  end
61
49
  end
62
50
  END
@@ -45,24 +45,27 @@ module Kuby
45
45
  environment.kubernetes.plugins[database.plugin_name] = @database.plugin
46
46
  environment.kubernetes.add_plugin(:kube_db)
47
47
 
48
- environment.docker do
49
- insert :rewrite_db_config, RewriteDbConfig.new, after: :copy_phase
48
+ unless environment.development?
49
+ environment.docker do
50
+ insert :rewrite_db_config, RewriteDbConfig.new, after: :copy_phase
51
+ end
50
52
  end
51
53
  end
52
54
 
53
- # do we always want this?
54
- environment.kubernetes.add_plugin(:nginx_ingress)
55
- environment.kubernetes.add_plugin(:rails_assets) do
56
- asset_url context.asset_url
57
- packs_url context.packs_url
58
- asset_path context.asset_path
59
- end
55
+ unless environment.development?
56
+ environment.kubernetes.add_plugin(:nginx_ingress)
57
+ environment.kubernetes.add_plugin(:rails_assets) do
58
+ asset_url context.asset_url
59
+ packs_url context.packs_url
60
+ asset_path context.asset_path
61
+ end
60
62
 
61
- if @tls_enabled
62
- context = self
63
+ if @tls_enabled
64
+ context = self
63
65
 
64
- environment.kubernetes.add_plugin(:cert_manager) do
65
- email context.environment.docker.credentials.email
66
+ environment.kubernetes.add_plugin(:cert_manager) do
67
+ email context.environment.docker.credentials.email
68
+ end
66
69
  end
67
70
  end
68
71
  end
@@ -80,6 +83,8 @@ module Kuby
80
83
  assets.configure_deployment(deployment, image_with_tag)
81
84
  end
82
85
 
86
+ spec = self
87
+
83
88
  deployment do
84
89
  spec do
85
90
  template do
@@ -88,12 +93,14 @@ module Kuby
88
93
  image image_with_tag
89
94
  end
90
95
 
91
- init_container(:create_db) do
92
- image image_with_tag
93
- end
96
+ unless spec.environment.development?
97
+ init_container(:create_db) do
98
+ image image_with_tag
99
+ end
94
100
 
95
- init_container(:migrate_db) do
96
- image image_with_tag
101
+ init_container(:migrate_db) do
102
+ image image_with_tag
103
+ end
97
104
  end
98
105
  end
99
106
  end
@@ -130,7 +137,7 @@ module Kuby
130
137
 
131
138
  port do
132
139
  name 'http'
133
- port 8080
140
+ port spec.docker.webserver_phase.port
134
141
  protocol 'TCP'
135
142
  target_port 'http'
136
143
  end
@@ -226,6 +233,13 @@ module Kuby
226
233
  add :app, kube_spec.selector_app
227
234
  add :role, kube_spec.role
228
235
  end
236
+
237
+ annotations do
238
+ add(
239
+ 'getkuby.io/dockerfile-checksum',
240
+ kube_spec.environment.docker.to_dockerfile.checksum
241
+ )
242
+ end
229
243
  end
230
244
 
231
245
  spec do
@@ -278,33 +292,93 @@ module Kuby
278
292
  end
279
293
  end
280
294
 
281
- readiness_probe do
282
- success_threshold 1
283
- failure_threshold 2
284
- initial_delay_seconds 15
285
- period_seconds 3
286
- timeout_seconds 1
287
-
288
- http_get do
289
- path '/healthz'
290
- port kube_spec.docker.webserver_phase.port
291
- scheme 'HTTP'
295
+ unless kube_spec.environment.development?
296
+ readiness_probe do
297
+ success_threshold 1
298
+ failure_threshold 2
299
+ initial_delay_seconds 15
300
+ period_seconds 3
301
+ timeout_seconds 1
302
+
303
+ http_get do
304
+ path '/healthz'
305
+ port kube_spec.docker.webserver_phase.port
306
+ scheme 'HTTP'
307
+ end
292
308
  end
293
309
  end
294
- end
295
310
 
296
- init_container(:create_db) do
297
- name "#{kube_spec.selector_app}-create-db"
298
- command %w(bundle exec rake kuby:rails_app:db:create_unless_exists)
299
- end
311
+ if kube_spec.environment.development?
312
+ env do
313
+ name 'BUNDLE_PATH'
314
+ value '/bundle'
315
+ end
316
+
317
+ env do
318
+ name 'GEM_HOME'
319
+ value '/bundle'
320
+ end
321
+
322
+ env do
323
+ name 'BOOTSNAP_CACHE_DIR'
324
+ value '/usr/src/bootsnap'
325
+ end
326
+
327
+ volume_mount do
328
+ name "#{kube_spec.selector_app}-code"
329
+ mount_path '/usr/src/app'
330
+ end
331
+
332
+ volume_mount do
333
+ name "#{kube_spec.selector_app}-bundle"
334
+ mount_path '/bundle'
335
+ end
300
336
 
301
- init_container(:migrate_db) do
302
- name "#{kube_spec.selector_app}-migrate-db"
303
- command %w(bundle exec rake db:migrate)
337
+ volume_mount do
338
+ name "#{kube_spec.selector_app}-bootsnap"
339
+ mount_path '/usr/src/bootsnap'
340
+ end
341
+ end
304
342
  end
305
343
 
306
- image_pull_secret do
307
- name kube_spec.environment.kubernetes.registry_secret.metadata.name
344
+ if kube_spec.environment.development?
345
+ volume do
346
+ name "#{kube_spec.selector_app}-code"
347
+
348
+ persistent_volume_claim do
349
+ claim_name kube_spec.code_volume_claim.metadata.name
350
+ end
351
+ end
352
+
353
+ volume do
354
+ name "#{kube_spec.selector_app}-bundle"
355
+
356
+ persistent_volume_claim do
357
+ claim_name kube_spec.bundle_volume_claim.metadata.name
358
+ end
359
+ end
360
+
361
+ volume do
362
+ name "#{kube_spec.selector_app}-bootsnap"
363
+
364
+ persistent_volume_claim do
365
+ claim_name kube_spec.bootsnap_volume_claim.metadata.name
366
+ end
367
+ end
368
+ else
369
+ init_container(:create_db) do
370
+ name "#{kube_spec.selector_app}-create-db"
371
+ command %w(bundle exec rake kuby:rails_app:db:create_unless_exists)
372
+ end
373
+
374
+ init_container(:migrate_db) do
375
+ name "#{kube_spec.selector_app}-migrate-db"
376
+ command %w(bundle exec rake db:migrate)
377
+ end
378
+
379
+ image_pull_secret do
380
+ name kube_spec.environment.kubernetes.registry_secret.metadata.name
381
+ end
308
382
  end
309
383
 
310
384
  restart_policy 'Always'
@@ -361,6 +435,118 @@ module Kuby
361
435
  @ingress
362
436
  end
363
437
 
438
+ def code_volume(&block)
439
+ spec = self
440
+
441
+ if environment.development?
442
+ @code_volume ||= KubeDSL.persistent_volume do
443
+ metadata do
444
+ name "#{spec.selector_app}-code"
445
+ end
446
+
447
+ spec do
448
+ access_modes ['ReadWriteMany']
449
+
450
+ capacity do
451
+ add :storage, '1Mi'
452
+ end
453
+
454
+ host_path do
455
+ path File.expand_path(spec.root)
456
+ end
457
+
458
+ storage_class_name 'hostpath'
459
+ end
460
+ end
461
+
462
+ @code_volume.instance_eval(&block) if block
463
+ @code_volume
464
+ end
465
+ end
466
+
467
+ def code_volume_claim(&block)
468
+ spec = self
469
+
470
+ if environment.development?
471
+ @code_volume_claim ||= KubeDSL.persistent_volume_claim do
472
+ metadata do
473
+ name "#{spec.selector_app}-code"
474
+ namespace spec.namespace.metadata.name
475
+ end
476
+
477
+ spec do
478
+ access_modes ['ReadWriteMany']
479
+
480
+ resources do
481
+ requests do
482
+ add :storage, '1Mi'
483
+ end
484
+ end
485
+
486
+ storage_class_name 'hostpath'
487
+ volume_name spec.code_volume.metadata.name
488
+ end
489
+ end
490
+
491
+ @code_volume_claim.instance_eval(&block) if block
492
+ @code_volume_claim
493
+ end
494
+ end
495
+
496
+ def bundle_volume_claim(&block)
497
+ spec = self
498
+
499
+ if environment.development?
500
+ @bundle_volume_claim ||= KubeDSL.persistent_volume_claim do
501
+ metadata do
502
+ name "#{spec.selector_app}-bundle"
503
+ namespace spec.namespace.metadata.name
504
+ end
505
+
506
+ spec do
507
+ access_modes ['ReadWriteMany']
508
+ storage_class_name 'hostpath'
509
+
510
+ resources do
511
+ requests do
512
+ add :storage, '2Gi'
513
+ end
514
+ end
515
+ end
516
+ end
517
+
518
+ @bundle_volume_claim.instance_eval(&block) if block
519
+ @bundle_volume_claim
520
+ end
521
+ end
522
+
523
+ def bootsnap_volume_claim(&block)
524
+ spec = self
525
+
526
+ if environment.development?
527
+ @bootsnap_volume_claim ||= KubeDSL.persistent_volume_claim do
528
+ metadata do
529
+ name "#{spec.selector_app}-bootsnap"
530
+ namespace spec.namespace.metadata.name
531
+ end
532
+
533
+ spec do
534
+ access_modes ['ReadWriteMany']
535
+ storage_class_name 'hostpath'
536
+
537
+ resources do
538
+ requests do
539
+ add :storage, '2Gi'
540
+ end
541
+ end
542
+ end
543
+ end
544
+
545
+ @bootsnap_volume_claim.instance_eval(&block) if block
546
+ @bootsnap_volume_claim
547
+ end
548
+ end
549
+
364
550
  def resources
365
551
  @resources ||= [
366
552
  service,
@@ -369,6 +555,10 @@ module Kuby
369
555
  app_secrets,
370
556
  deployment,
371
557
  ingress,
558
+ code_volume,
559
+ code_volume_claim,
560
+ bundle_volume_claim,
561
+ bootsnap_volume_claim,
372
562
  *database&.plugin&.resources
373
563
  ]
374
564
  end
@@ -0,0 +1,89 @@
1
+ module Kuby
2
+ class Args
3
+ attr_reader :args, :flag_aliases
4
+
5
+ def initialize(args, flag_aliases = [])
6
+ @args = args
7
+ @flag_aliases = flag_aliases
8
+ end
9
+
10
+ def [](flag)
11
+ idx = find_arg_index(flag)
12
+ idx ? args[idx] : nil
13
+ end
14
+
15
+ def []=(flag, new_value)
16
+ idx = find_arg_index(flag)
17
+
18
+ if idx
19
+ args[idx] = new_value
20
+ else
21
+ @args += [flag, new_value]
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def find_arg_index(flag)
28
+ idx = args.find_index do |arg|
29
+ flag_aliases.any? { |fas| fas.include?(arg) && fas.include?(flag) }
30
+ end
31
+
32
+ idx ? idx + 1 : nil
33
+ end
34
+ end
35
+
36
+ class RailsCommands
37
+ PREFIX = %w(bundle exec rails).freeze
38
+ SERVER_ARG_ALIASES = [['--binding', '-b'], ['-p', '--port']].freeze
39
+
40
+ class << self
41
+ def run(args = ARGV)
42
+ subcommand = args[0]
43
+ arglist = nil
44
+
45
+ case subcommand
46
+ when 'server', 's'
47
+ arglist = Args.new([*PREFIX, *args], SERVER_ARG_ALIASES)
48
+ arglist['-b'] ||= '0.0.0.0'
49
+ arglist['-p'] ||= '3000'
50
+ when 'runner', 'r'
51
+ when 'console', 'c'
52
+ else
53
+ return
54
+ end
55
+
56
+ setup
57
+
58
+ arglist ||= Args.new([*PREFIX, *args])
59
+ tasks = Kuby::Tasks.new(environment)
60
+ tasks.remote_exec(arglist.args)
61
+ end
62
+
63
+ private
64
+
65
+ def setup
66
+ require 'rubygems'
67
+ require 'bundler'
68
+
69
+ Bundler.setup
70
+
71
+ require 'kuby'
72
+
73
+ Kuby.load!
74
+ end
75
+
76
+ def kubernetes_cli
77
+ kubernetes.provider.kubernetes.cli
78
+ end
79
+
80
+ def kubernetes
81
+ environment.kubernetes
82
+ end
83
+
84
+ def environment
85
+ Kuby.definition.environment
86
+ end
87
+ end
88
+ end
89
+ end