kuby-core 0.11.16 → 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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/Gemfile +1 -2
  4. data/kuby-core.gemspec +1 -1
  5. data/lib/kuby.rb +2 -20
  6. data/lib/kuby/commands.rb +13 -67
  7. data/lib/kuby/docker.rb +27 -25
  8. data/lib/kuby/docker/alpine.rb +2 -1
  9. data/lib/kuby/docker/app_image.rb +19 -0
  10. data/lib/kuby/docker/cli.rb +4 -12
  11. data/lib/kuby/docker/docker_uri.rb +18 -7
  12. data/lib/kuby/docker/errors.rb +1 -19
  13. data/lib/kuby/docker/image.rb +115 -0
  14. data/lib/kuby/docker/layer.rb +0 -7
  15. data/lib/kuby/docker/local_tags.rb +9 -10
  16. data/lib/kuby/docker/package_phase.rb +0 -5
  17. data/lib/kuby/docker/packages.rb +1 -0
  18. data/lib/kuby/docker/remote_tags.rb +10 -5
  19. data/lib/kuby/docker/setup_phase.rb +17 -9
  20. data/lib/kuby/docker/spec.rb +29 -62
  21. data/lib/kuby/docker/timestamp_tag.rb +8 -1
  22. data/lib/kuby/docker/timestamped_image.rb +113 -0
  23. data/lib/kuby/environment.rb +1 -10
  24. data/lib/kuby/kubernetes/bare_metal_provider.rb +16 -4
  25. data/lib/kuby/kubernetes/deployer.rb +1 -1
  26. data/lib/kuby/kubernetes/docker_desktop_provider.rb +0 -15
  27. data/lib/kuby/kubernetes/spec.rb +21 -17
  28. data/lib/kuby/plugin.rb +2 -2
  29. data/lib/kuby/plugins/rails_app.rb +1 -0
  30. data/lib/kuby/plugins/rails_app/assets.rb +60 -70
  31. data/lib/kuby/plugins/rails_app/assets_image.rb +55 -0
  32. data/lib/kuby/plugins/rails_app/plugin.rb +53 -236
  33. data/lib/kuby/tasks.rb +30 -69
  34. data/lib/kuby/version.rb +1 -1
  35. data/spec/docker/spec_spec.rb +9 -118
  36. data/spec/docker/timestamped_image_spec.rb +123 -0
  37. data/spec/spec_helper.rb +10 -11
  38. metadata +9 -10
  39. data/lib/kuby/dev_setup.rb +0 -346
  40. data/lib/kuby/docker/dev_spec.rb +0 -202
  41. data/lib/kuby/docker/metadata.rb +0 -90
  42. data/lib/kuby/docker/tags.rb +0 -92
  43. data/lib/kuby/rails_commands.rb +0 -84
  44. 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,117 +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
377
- end
378
- 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
-
384
- env_from do
385
- config_map_ref do
386
- name kube_spec.config_map.metadata.name
387
- end
388
- end
389
-
390
- env_from do
391
- secret_ref do
392
- name kube_spec.app_secrets.metadata.name
393
- end
335
+ env_from do
336
+ config_map_ref do
337
+ name kube_spec.config_map.metadata.name
394
338
  end
395
339
  end
396
340
 
397
- init_container(:migrate_db) do
398
- name "#{kube_spec.selector_app}-migrate-db"
399
- command %w(bundle exec rake db:migrate)
400
-
401
- env_from do
402
- config_map_ref do
403
- name kube_spec.config_map.metadata.name
404
- end
405
- end
406
-
407
- env_from do
408
- secret_ref do
409
- name kube_spec.app_secrets.metadata.name
410
- end
341
+ env_from do
342
+ secret_ref do
343
+ name kube_spec.app_secrets.metadata.name
411
344
  end
412
345
  end
346
+ end
413
347
 
414
- image_pull_secret do
415
- name kube_spec.environment.kubernetes.registry_secret.metadata.name
416
- end
348
+ image_pull_secret do
349
+ name kube_spec.environment.kubernetes.registry_secret.metadata.name
417
350
  end
418
351
 
419
352
  restart_policy 'Always'
@@ -470,118 +403,6 @@ module Kuby
470
403
  @ingress
471
404
  end
472
405
 
473
- def code_volume(&block)
474
- spec = self
475
-
476
- if environment.development?
477
- @code_volume ||= KubeDSL.persistent_volume do
478
- metadata do
479
- name "#{spec.selector_app}-code"
480
- end
481
-
482
- spec do
483
- access_modes ['ReadWriteMany']
484
-
485
- capacity do
486
- add :storage, '1Mi'
487
- end
488
-
489
- host_path do
490
- path File.expand_path(spec.root)
491
- end
492
-
493
- storage_class_name 'hostpath'
494
- end
495
- end
496
-
497
- @code_volume.instance_eval(&block) if block
498
- @code_volume
499
- end
500
- end
501
-
502
- def code_volume_claim(&block)
503
- spec = self
504
-
505
- if environment.development?
506
- @code_volume_claim ||= KubeDSL.persistent_volume_claim do
507
- metadata do
508
- name "#{spec.selector_app}-code"
509
- namespace spec.namespace.metadata.name
510
- end
511
-
512
- spec do
513
- access_modes ['ReadWriteMany']
514
-
515
- resources do
516
- requests do
517
- add :storage, '1Mi'
518
- end
519
- end
520
-
521
- storage_class_name 'hostpath'
522
- volume_name spec.code_volume.metadata.name
523
- end
524
- end
525
-
526
- @code_volume_claim.instance_eval(&block) if block
527
- @code_volume_claim
528
- end
529
- end
530
-
531
- def bundle_volume_claim(&block)
532
- spec = self
533
-
534
- if environment.development?
535
- @bundle_volume_claim ||= KubeDSL.persistent_volume_claim do
536
- metadata do
537
- name "#{spec.selector_app}-bundle"
538
- namespace spec.namespace.metadata.name
539
- end
540
-
541
- spec do
542
- access_modes ['ReadWriteMany']
543
- storage_class_name 'hostpath'
544
-
545
- resources do
546
- requests do
547
- add :storage, '2Gi'
548
- end
549
- end
550
- end
551
- end
552
-
553
- @bundle_volume_claim.instance_eval(&block) if block
554
- @bundle_volume_claim
555
- end
556
- end
557
-
558
- def bootsnap_volume_claim(&block)
559
- spec = self
560
-
561
- if environment.development?
562
- @bootsnap_volume_claim ||= KubeDSL.persistent_volume_claim do
563
- metadata do
564
- name "#{spec.selector_app}-bootsnap"
565
- namespace spec.namespace.metadata.name
566
- end
567
-
568
- spec do
569
- access_modes ['ReadWriteMany']
570
- storage_class_name 'hostpath'
571
-
572
- resources do
573
- requests do
574
- add :storage, '2Gi'
575
- end
576
- end
577
- end
578
- end
579
-
580
- @bootsnap_volume_claim.instance_eval(&block) if block
581
- @bootsnap_volume_claim
582
- end
583
- end
584
-
585
406
  def resources
586
407
  @resources ||= [
587
408
  service,
@@ -590,10 +411,6 @@ module Kuby
590
411
  app_secrets,
591
412
  deployment,
592
413
  ingress,
593
- code_volume,
594
- code_volume_claim,
595
- bundle_volume_claim,
596
- bootsnap_volume_claim,
597
414
  *database&.plugin&.resources
598
415
  ]
599
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