kuby-core 0.11.12 → 0.12.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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/Gemfile +2 -2
  4. data/bin/kuby +2 -0
  5. data/kuby-core.gemspec +2 -2
  6. data/lib/kuby.rb +3 -20
  7. data/lib/kuby/basic_logger.rb +1 -1
  8. data/lib/kuby/cli_base.rb +9 -4
  9. data/lib/kuby/commands.rb +13 -67
  10. data/lib/kuby/docker.rb +27 -25
  11. data/lib/kuby/docker/alpine.rb +2 -1
  12. data/lib/kuby/docker/app_image.rb +19 -0
  13. data/lib/kuby/docker/bundler_phase.rb +0 -4
  14. data/lib/kuby/docker/cli.rb +4 -12
  15. data/lib/kuby/docker/docker_uri.rb +18 -7
  16. data/lib/kuby/docker/errors.rb +1 -19
  17. data/lib/kuby/docker/image.rb +115 -0
  18. data/lib/kuby/docker/layer.rb +0 -7
  19. data/lib/kuby/docker/local_tags.rb +9 -10
  20. data/lib/kuby/docker/package_phase.rb +0 -5
  21. data/lib/kuby/docker/packages.rb +1 -0
  22. data/lib/kuby/docker/remote_tags.rb +10 -5
  23. data/lib/kuby/docker/setup_phase.rb +17 -9
  24. data/lib/kuby/docker/spec.rb +29 -62
  25. data/lib/kuby/docker/timestamp_tag.rb +8 -1
  26. data/lib/kuby/docker/timestamped_image.rb +113 -0
  27. data/lib/kuby/docker/yarn_phase.rb +2 -2
  28. data/lib/kuby/environment.rb +1 -10
  29. data/lib/kuby/kubernetes.rb +1 -0
  30. data/lib/kuby/kubernetes/bare_metal_provider.rb +53 -0
  31. data/lib/kuby/kubernetes/deployer.rb +2 -1
  32. data/lib/kuby/kubernetes/docker_desktop_provider.rb +0 -15
  33. data/lib/kuby/kubernetes/spec.rb +21 -17
  34. data/lib/kuby/plugin.rb +2 -2
  35. data/lib/kuby/plugins/rails_app.rb +1 -0
  36. data/lib/kuby/plugins/rails_app/assets.rb +60 -70
  37. data/lib/kuby/plugins/rails_app/assets_image.rb +55 -0
  38. data/lib/kuby/plugins/rails_app/plugin.rb +54 -213
  39. data/lib/kuby/tasks.rb +30 -69
  40. data/lib/kuby/version.rb +1 -1
  41. data/spec/docker/spec_spec.rb +9 -118
  42. data/spec/docker/timestamped_image_spec.rb +123 -0
  43. data/spec/spec_helper.rb +10 -11
  44. metadata +10 -10
  45. data/lib/kuby/dev_setup.rb +0 -346
  46. data/lib/kuby/docker/dev_spec.rb +0 -202
  47. data/lib/kuby/docker/metadata.rb +0 -85
  48. data/lib/kuby/docker/tags.rb +0 -92
  49. data/lib/kuby/rails_commands.rb +0 -84
  50. data/spec/docker/metadata_spec.rb +0 -73
@@ -0,0 +1,55 @@
1
+ module Kuby
2
+ module Plugins
3
+ module RailsApp
4
+ class AssetsImage < ::Kuby::Docker::Image
5
+ attr_reader :base_image
6
+
7
+ def initialize(base_image, dockerfile, main_tag = nil, alias_tags = [])
8
+ super(dockerfile, base_image.image_url, base_image.credentials, main_tag, alias_tags)
9
+ @base_image = base_image
10
+ end
11
+
12
+ def new_version
13
+ # Asset images track the base image, so return the current version
14
+ # here. There can be no asset image without a base image.
15
+ current_version
16
+ end
17
+
18
+ def current_version
19
+ @current_version ||= duplicate_with_annotated_tags(
20
+ base_image.current_version
21
+ )
22
+ end
23
+
24
+ def previous_version
25
+ @previous_version ||= duplicate_with_annotated_tags(
26
+ base_image.previous_version
27
+ )
28
+ end
29
+
30
+ def build(build_args = {})
31
+ docker_cli.build(current_version, build_args: build_args)
32
+ end
33
+
34
+ def push(tag)
35
+ docker_cli.push(image_url, tag)
36
+ end
37
+
38
+ private
39
+
40
+ def duplicate_with_annotated_tags(image)
41
+ self.class.new(
42
+ base_image,
43
+ dockerfile,
44
+ annotate_tag(image.main_tag),
45
+ image.alias_tags.map { |at| annotate_tag(at) }
46
+ )
47
+ end
48
+
49
+ def annotate_tag(tag)
50
+ "#{tag}-assets"
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -47,27 +47,23 @@ module Kuby
47
47
  environment.kubernetes.plugins[@database.plugin_name] = @database.plugin
48
48
  environment.kubernetes.add_plugin(:kube_db)
49
49
 
50
- unless environment.development?
51
- environment.docker do
52
- insert :rewrite_db_config, RewriteDbConfig.new, after: :copy_phase
53
- end
50
+ environment.docker do
51
+ insert :rewrite_db_config, RewriteDbConfig.new, after: :copy_phase
54
52
  end
55
53
  end
56
54
 
57
- unless environment.development?
58
- environment.kubernetes.add_plugin(:nginx_ingress)
59
- environment.kubernetes.add_plugin(:rails_assets) do
60
- asset_url context.asset_url
61
- packs_url context.packs_url
62
- asset_path context.asset_path
63
- end
55
+ environment.kubernetes.add_plugin(:nginx_ingress)
56
+ environment.kubernetes.add_plugin(:rails_assets) do
57
+ asset_url context.asset_url
58
+ packs_url context.packs_url
59
+ asset_path context.asset_path
60
+ end
64
61
 
65
- if @tls_enabled
66
- context = self
62
+ if @tls_enabled
63
+ context = self
67
64
 
68
- environment.kubernetes.add_plugin(:cert_manager) do
69
- email context.environment.docker.credentials.email
70
- end
65
+ environment.kubernetes.add_plugin(:cert_manager) do
66
+ email context.environment.docker.credentials.email
71
67
  end
72
68
  end
73
69
  end
@@ -79,14 +75,13 @@ module Kuby
79
75
  def before_deploy(manifest)
80
76
  # Make sure plugin has been configured. If not, do nothing.
81
77
  if cert_manager = environment.kubernetes.plugin(:cert_manager)
82
- cert_manager.annotate_ingress(ingress)
78
+ cert_manager.annotate_ingress(ingress) if tls_enabled
83
79
  end
84
80
 
85
- image_with_tag = "#{docker.metadata.image_url}:#{kubernetes.tag}"
81
+ image_with_tag = "#{docker.image.image_url}:#{kubernetes.tag || Kuby::Docker::LATEST_TAG}"
86
82
 
87
83
  if assets = environment.kubernetes.plugin(:rails_assets)
88
84
  assets.configure_ingress(ingress, hostname)
89
- assets.configure_deployment(deployment, image_with_tag)
90
85
  end
91
86
 
92
87
  spec = self
@@ -99,14 +94,12 @@ module Kuby
99
94
  image image_with_tag
100
95
  end
101
96
 
102
- unless spec.environment.development?
103
- init_container(:create_db) do
104
- image image_with_tag
105
- end
97
+ init_container(:create_db) do
98
+ image image_with_tag
99
+ end
106
100
 
107
- init_container(:migrate_db) do
108
- image image_with_tag
109
- end
101
+ init_container(:migrate_db) do
102
+ image image_with_tag
110
103
  end
111
104
  end
112
105
  end
@@ -248,7 +241,7 @@ module Kuby
248
241
  annotations do
249
242
  add(
250
243
  'getkuby.io/dockerfile-checksum',
251
- kube_spec.environment.docker.to_dockerfile.checksum
244
+ kube_spec.environment.docker.image.dockerfile.checksum
252
245
  )
253
246
  end
254
247
  end
@@ -303,93 +296,57 @@ module Kuby
303
296
  end
304
297
  end
305
298
 
306
- unless kube_spec.environment.development?
307
- readiness_probe do
308
- success_threshold 1
309
- failure_threshold 2
310
- initial_delay_seconds 15
311
- period_seconds 3
312
- timeout_seconds 1
313
-
314
- http_get do
315
- path '/healthz'
316
- port kube_spec.docker.webserver_phase.port
317
- scheme 'HTTP'
318
- end
319
- end
320
- end
321
-
322
- if kube_spec.environment.development?
323
- env do
324
- name 'BUNDLE_PATH'
325
- value '/bundle'
326
- end
327
-
328
- env do
329
- name 'GEM_HOME'
330
- value '/bundle'
331
- end
332
-
333
- env do
334
- name 'BOOTSNAP_CACHE_DIR'
335
- value '/usr/src/bootsnap'
336
- end
337
-
338
- volume_mount do
339
- name "#{kube_spec.selector_app}-code"
340
- mount_path '/usr/src/app'
341
- end
342
-
343
- volume_mount do
344
- name "#{kube_spec.selector_app}-bundle"
345
- mount_path '/bundle'
346
- end
347
-
348
- volume_mount do
349
- name "#{kube_spec.selector_app}-bootsnap"
350
- mount_path '/usr/src/bootsnap'
299
+ readiness_probe do
300
+ success_threshold 1
301
+ failure_threshold 2
302
+ initial_delay_seconds 15
303
+ period_seconds 3
304
+ timeout_seconds 1
305
+
306
+ http_get do
307
+ path '/healthz'
308
+ port kube_spec.docker.webserver_phase.port
309
+ scheme 'HTTP'
351
310
  end
352
311
  end
353
312
  end
354
313
 
355
- if kube_spec.environment.development?
356
- volume do
357
- name "#{kube_spec.selector_app}-code"
314
+ init_container(:create_db) do
315
+ name "#{kube_spec.selector_app}-create-db"
316
+ command %w(bundle exec rake kuby:rails_app:db:create_unless_exists)
358
317
 
359
- persistent_volume_claim do
360
- claim_name kube_spec.code_volume_claim.metadata.name
318
+ env_from do
319
+ config_map_ref do
320
+ name kube_spec.config_map.metadata.name
361
321
  end
362
322
  end
363
323
 
364
- volume do
365
- name "#{kube_spec.selector_app}-bundle"
366
-
367
- persistent_volume_claim do
368
- claim_name kube_spec.bundle_volume_claim.metadata.name
324
+ env_from do
325
+ secret_ref do
326
+ name kube_spec.app_secrets.metadata.name
369
327
  end
370
328
  end
329
+ end
371
330
 
372
- volume do
373
- name "#{kube_spec.selector_app}-bootsnap"
331
+ init_container(:migrate_db) do
332
+ name "#{kube_spec.selector_app}-migrate-db"
333
+ command %w(bundle exec rake db:migrate)
374
334
 
375
- persistent_volume_claim do
376
- claim_name kube_spec.bootsnap_volume_claim.metadata.name
335
+ env_from do
336
+ config_map_ref do
337
+ name kube_spec.config_map.metadata.name
377
338
  end
378
339
  end
379
- else
380
- init_container(:create_db) do
381
- name "#{kube_spec.selector_app}-create-db"
382
- command %w(bundle exec rake kuby:rails_app:db:create_unless_exists)
383
- end
384
340
 
385
- init_container(:migrate_db) do
386
- name "#{kube_spec.selector_app}-migrate-db"
387
- command %w(bundle exec rake db:migrate)
341
+ env_from do
342
+ secret_ref do
343
+ name kube_spec.app_secrets.metadata.name
344
+ end
388
345
  end
346
+ end
389
347
 
390
- image_pull_secret do
391
- name kube_spec.environment.kubernetes.registry_secret.metadata.name
392
- end
348
+ image_pull_secret do
349
+ name kube_spec.environment.kubernetes.registry_secret.metadata.name
393
350
  end
394
351
 
395
352
  restart_policy 'Always'
@@ -446,118 +403,6 @@ module Kuby
446
403
  @ingress
447
404
  end
448
405
 
449
- def code_volume(&block)
450
- spec = self
451
-
452
- if environment.development?
453
- @code_volume ||= KubeDSL.persistent_volume do
454
- metadata do
455
- name "#{spec.selector_app}-code"
456
- end
457
-
458
- spec do
459
- access_modes ['ReadWriteMany']
460
-
461
- capacity do
462
- add :storage, '1Mi'
463
- end
464
-
465
- host_path do
466
- path File.expand_path(spec.root)
467
- end
468
-
469
- storage_class_name 'hostpath'
470
- end
471
- end
472
-
473
- @code_volume.instance_eval(&block) if block
474
- @code_volume
475
- end
476
- end
477
-
478
- def code_volume_claim(&block)
479
- spec = self
480
-
481
- if environment.development?
482
- @code_volume_claim ||= KubeDSL.persistent_volume_claim do
483
- metadata do
484
- name "#{spec.selector_app}-code"
485
- namespace spec.namespace.metadata.name
486
- end
487
-
488
- spec do
489
- access_modes ['ReadWriteMany']
490
-
491
- resources do
492
- requests do
493
- add :storage, '1Mi'
494
- end
495
- end
496
-
497
- storage_class_name 'hostpath'
498
- volume_name spec.code_volume.metadata.name
499
- end
500
- end
501
-
502
- @code_volume_claim.instance_eval(&block) if block
503
- @code_volume_claim
504
- end
505
- end
506
-
507
- def bundle_volume_claim(&block)
508
- spec = self
509
-
510
- if environment.development?
511
- @bundle_volume_claim ||= KubeDSL.persistent_volume_claim do
512
- metadata do
513
- name "#{spec.selector_app}-bundle"
514
- namespace spec.namespace.metadata.name
515
- end
516
-
517
- spec do
518
- access_modes ['ReadWriteMany']
519
- storage_class_name 'hostpath'
520
-
521
- resources do
522
- requests do
523
- add :storage, '2Gi'
524
- end
525
- end
526
- end
527
- end
528
-
529
- @bundle_volume_claim.instance_eval(&block) if block
530
- @bundle_volume_claim
531
- end
532
- end
533
-
534
- def bootsnap_volume_claim(&block)
535
- spec = self
536
-
537
- if environment.development?
538
- @bootsnap_volume_claim ||= KubeDSL.persistent_volume_claim do
539
- metadata do
540
- name "#{spec.selector_app}-bootsnap"
541
- namespace spec.namespace.metadata.name
542
- end
543
-
544
- spec do
545
- access_modes ['ReadWriteMany']
546
- storage_class_name 'hostpath'
547
-
548
- resources do
549
- requests do
550
- add :storage, '2Gi'
551
- end
552
- end
553
- end
554
- end
555
-
556
- @bootsnap_volume_claim.instance_eval(&block) if block
557
- @bootsnap_volume_claim
558
- end
559
- end
560
-
561
406
  def resources
562
407
  @resources ||= [
563
408
  service,
@@ -566,10 +411,6 @@ module Kuby
566
411
  app_secrets,
567
412
  deployment,
568
413
  ingress,
569
- code_volume,
570
- code_volume_claim,
571
- bundle_volume_claim,
572
- bootsnap_volume_claim,
573
414
  *database&.plugin&.resources
574
415
  ]
575
416
  end
data/lib/kuby/tasks.rb CHANGED
@@ -9,65 +9,60 @@ module Kuby
9
9
  @environment = environment
10
10
  end
11
11
 
12
- def print_dockerfile
13
- theme = Rouge::Themes::Base16::Solarized.new
14
- formatter = Rouge::Formatters::Terminal256.new(theme)
15
- lexer = Rouge::Lexers::Docker.new
16
- tokens = lexer.lex(Kuby.environment.docker.to_dockerfile.to_s)
17
- puts formatter.format(tokens)
12
+ def print_dockerfiles
13
+ kubernetes.docker_images.each do |image|
14
+ image = image.current_version
15
+ Kuby.logger.info("Dockerfile for image #{image.image_url} with tags #{image.tags.join(', ')}")
16
+ theme = Rouge::Themes::Base16::Solarized.new
17
+ formatter = Rouge::Formatters::Terminal256.new(theme)
18
+ lexer = Rouge::Lexers::Docker.new
19
+ tokens = lexer.lex(image.dockerfile.to_s)
20
+ puts formatter.format(tokens)
21
+ end
18
22
  end
19
23
 
20
24
  def setup
21
25
  environment.kubernetes.setup
22
26
  end
23
27
 
24
- def build
25
- build_args = {}
26
-
27
- unless ENV.fetch('RAILS_MASTER_KEY', '').empty?
28
- build_args['RAILS_MASTER_KEY'] = ENV['RAILS_MASTER_KEY']
28
+ def build(build_args = {})
29
+ kubernetes.docker_images.each do |image|
30
+ image = image.new_version
31
+ Kuby.logger.info("Building image #{image.image_url} with tags #{image.tags.join(', ')}")
32
+ image.build(build_args)
29
33
  end
30
-
31
- docker.cli.build(
32
- dockerfile: docker.to_dockerfile,
33
- image_url: docker.metadata.image_url,
34
- tags: docker.metadata.tags,
35
- build_args: build_args
36
- )
37
34
  end
38
35
 
39
36
  def push
40
- if environment.development?
41
- fail 'Cannot push Docker images built for the development environment'
37
+ kubernetes.docker_images.each do |image|
38
+ image = image.current_version
39
+ Kuby.logger.info("Pushing image #{image.image_url} with tags #{image.tags.join(', ')}")
40
+ push_image(image)
42
41
  end
42
+ end
43
43
 
44
- host = docker.metadata.image_host
45
-
46
- if docker.credentials.username && !docker.cli.auths.include?(host)
47
- Kuby.logger.info("Attempting to log in to registry at #{host}")
44
+ def push_image(image)
45
+ if image.credentials.username && !image.docker_cli.auths.include?(image.image_host)
46
+ Kuby.logger.info("Attempting to log in to registry at #{image.image_host}")
48
47
 
49
48
  begin
50
- docker.cli.login(
51
- url: docker.metadata.image_host,
52
- username: docker.credentials.username,
53
- password: docker.credentials.password
49
+ image.docker_cli.login(
50
+ url: image.image_host,
51
+ username: image.credentials.username,
52
+ password: image.credentials.password
54
53
  )
55
54
  rescue Kuby::Docker::LoginError => e
56
- Kuby.logger.fatal("Couldn't log in to the registry at #{host}")
55
+ Kuby.logger.fatal("Couldn't log in to the registry at #{image.image_host}")
57
56
  Kuby.logger.fatal(e.message)
58
57
  return
59
58
  end
60
59
  end
61
60
 
62
- image_url = docker.metadata.image_url
63
-
64
61
  begin
65
- docker.tags.local.latest_tags.each do |tag|
66
- docker.cli.push(image_url, tag)
67
- end
62
+ image.tags.each { |tag| image.push(tag) }
68
63
  rescue Kuby::Docker::MissingTagError => e
69
64
  msg = "#{e.message} Run kuby build to build the "\
70
- 'Docker image before running this task.'
65
+ 'Docker images before running this task.'
71
66
 
72
67
  Kuby.logger.fatal(msg)
73
68
  Kuby.logger.fatal(e.message)
@@ -135,40 +130,6 @@ module Kuby
135
130
  kubernetes_cli.restart_deployment(namespace, deployment)
136
131
  end
137
132
 
138
- def dev_deployment_ok
139
- return true unless Kuby.environment.development?
140
-
141
- deployments = kubernetes_cli.get_objects(
142
- 'deployments', namespace, match_labels.serialize
143
- )
144
-
145
- if deployments.empty?
146
- puts 'No development environment detected.'
147
- STDOUT.write('Set up development environment? (y/n): ')
148
- answer = STDIN.gets.strip.downcase
149
- return false unless answer =~ /ye?s?/
150
- return DevSetup.new(environment).run
151
- else
152
- depl = deployments.first
153
- deployed_checksum = depl.dig('metadata', 'annotations', 'getkuby.io/dockerfile-checksum')
154
- current_checksum = docker.to_dockerfile.checksum
155
-
156
- if deployed_checksum != current_checksum
157
- puts "Development environment appears to be out-of-date."
158
- puts "Environment checksum: #{deployed_checksum}"
159
- puts "Current checksum: #{current_checksum}"
160
- STDOUT.write('Update development environment? (y/n): ')
161
- answer = STDIN.gets.strip.downcase
162
- # return true here to prevent letting an out-of-date deployment
163
- # stop us from running commands
164
- return true unless answer =~ /ye?s?/
165
- return DevSetup.new(environment).run
166
- end
167
- end
168
-
169
- true
170
- end
171
-
172
133
  private
173
134
 
174
135
  def get_first_pod