kuby-core 0.16.0 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -0
  3. data/Gemfile +6 -2
  4. data/Rakefile +5 -3
  5. data/bin/tapioca +29 -0
  6. data/kuby-core.gemspec +9 -11
  7. data/lib/kuby/basic_logger.rb +34 -34
  8. data/lib/kuby/cli_base.rb +43 -43
  9. data/lib/kuby/commands.rb +135 -21
  10. data/lib/kuby/definition.rb +12 -12
  11. data/lib/kuby/dependable.rb +20 -0
  12. data/lib/kuby/dependency.rb +14 -0
  13. data/lib/kuby/docker/alpine.rb +10 -10
  14. data/lib/kuby/docker/app_image.rb +11 -20
  15. data/lib/kuby/docker/app_phase.rb +36 -0
  16. data/lib/kuby/docker/assets_phase.rb +2 -2
  17. data/lib/kuby/docker/bundler_phase.rb +42 -40
  18. data/lib/kuby/docker/cli.rb +72 -37
  19. data/lib/kuby/docker/copy_phase.rb +7 -7
  20. data/lib/kuby/docker/credentials.rb +1 -0
  21. data/lib/kuby/docker/debian.rb +10 -10
  22. data/lib/kuby/docker/distro.rb +13 -13
  23. data/lib/kuby/docker/docker_uri.rb +20 -20
  24. data/lib/kuby/docker/dockerfile.rb +48 -39
  25. data/lib/kuby/docker/image.rb +66 -54
  26. data/lib/kuby/docker/inline_layer.rb +4 -4
  27. data/lib/kuby/docker/layer.rb +6 -6
  28. data/lib/kuby/docker/layer_stack.rb +35 -35
  29. data/lib/kuby/docker/local_tags.rb +16 -16
  30. data/lib/kuby/docker/package_list.rb +16 -16
  31. data/lib/kuby/docker/package_phase.rb +16 -16
  32. data/lib/kuby/docker/packages/managed_package.rb +13 -13
  33. data/lib/kuby/docker/packages/nodejs.rb +5 -5
  34. data/lib/kuby/docker/packages/package.rb +8 -8
  35. data/lib/kuby/docker/packages/simple_managed_package.rb +7 -7
  36. data/lib/kuby/docker/packages/yarn.rb +6 -6
  37. data/lib/kuby/docker/remote_tags.rb +16 -16
  38. data/lib/kuby/docker/setup_phase.rb +18 -20
  39. data/lib/kuby/docker/spec.rb +93 -72
  40. data/lib/kuby/docker/timestamp_tag.rb +16 -11
  41. data/lib/kuby/docker/timestamped_image.rb +64 -38
  42. data/lib/kuby/docker/webserver_phase.rb +20 -20
  43. data/lib/kuby/docker/yarn_phase.rb +29 -5
  44. data/lib/kuby/docker.rb +2 -1
  45. data/lib/kuby/kubernetes/bare_metal_provider.rb +12 -10
  46. data/lib/kuby/kubernetes/deployer.rb +22 -10
  47. data/lib/kuby/kubernetes/docker_config.rb +1 -0
  48. data/lib/kuby/kubernetes/provider.rb +1 -0
  49. data/lib/kuby/kubernetes/spec.rb +63 -13
  50. data/lib/kuby/plugin.rb +23 -1
  51. data/lib/kuby/plugin_registry.rb +15 -0
  52. data/lib/kuby/plugins/nginx_ingress.rb +8 -6
  53. data/lib/kuby/plugins/rails_app/assets.rb +16 -4
  54. data/lib/kuby/plugins/rails_app/assets_image.rb +18 -11
  55. data/lib/kuby/plugins/rails_app/crdb/plugin.rb +473 -0
  56. data/lib/kuby/plugins/rails_app/crdb.rb +9 -0
  57. data/lib/kuby/plugins/rails_app/database.rb +12 -8
  58. data/lib/kuby/plugins/rails_app/generators/kuby.rb +17 -16
  59. data/lib/kuby/plugins/rails_app/plugin.rb +37 -27
  60. data/lib/kuby/plugins/rails_app/sqlite.rb +7 -3
  61. data/lib/kuby/plugins/rails_app/tasks.rake +25 -12
  62. data/lib/kuby/plugins/rails_app.rb +1 -0
  63. data/lib/kuby/plugins/system.rb +16 -0
  64. data/lib/kuby/plugins.rb +1 -0
  65. data/lib/kuby/railtie.rb +31 -1
  66. data/lib/kuby/tasks.rb +91 -10
  67. data/lib/kuby/trailing_hash.rb +2 -2
  68. data/lib/kuby/utils/sem_ver/constraint.rb +68 -0
  69. data/lib/kuby/utils/sem_ver/constraint_set.rb +25 -0
  70. data/lib/kuby/utils/sem_ver/version.rb +49 -0
  71. data/lib/kuby/utils/sem_ver.rb +17 -0
  72. data/lib/kuby/utils/table.rb +35 -0
  73. data/lib/kuby/utils/which.rb +65 -0
  74. data/lib/kuby/utils.rb +11 -0
  75. data/lib/kuby/version.rb +1 -1
  76. data/lib/kuby.rb +37 -2
  77. data/rbi/kuby-core.rbi +2128 -0
  78. data/spec/docker/spec_spec.rb +50 -26
  79. data/spec/docker/timestamped_image_spec.rb +2 -2
  80. data/spec/dummy/app/channels/application_cable/channel.rb +2 -1
  81. data/spec/dummy/app/channels/application_cable/connection.rb +2 -1
  82. data/spec/dummy/app/controllers/application_controller.rb +2 -1
  83. data/spec/dummy/app/jobs/application_job.rb +2 -1
  84. data/spec/dummy/app/mailers/application_mailer.rb +2 -1
  85. data/spec/dummy/app/models/application_record.rb +2 -1
  86. data/spec/dummy/config/application.rb +2 -1
  87. data/spec/dummy/config/initializers/wrap_parameters.rb +2 -1
  88. data/spec/dummy/config/routes.rb +2 -1
  89. data/spec/dummy/test/application_system_test_case.rb +2 -1
  90. data/spec/dummy/test/channels/application_cable/connection_test.rb +2 -1
  91. data/spec/spec_helper.rb +13 -1
  92. metadata +46 -39
  93. data/lib/kuby/plugins/rails_app/mysql.rb +0 -158
  94. data/lib/kuby/plugins/rails_app/postgres.rb +0 -163
@@ -1,4 +1,5 @@
1
- # typed: true
1
+ # typed: false
2
+
2
3
  require 'rails/generators'
3
4
  require 'rails/generators/base'
4
5
 
@@ -14,14 +15,6 @@ class KubyGenerator < Rails::Generators::Base
14
15
  end
15
16
 
16
17
  def create_config_file
17
- app_class = Rails.application.class
18
-
19
- app_name = if app_class.respond_to?(:module_parent_name)
20
- app_class.module_parent_name
21
- else
22
- app_class.parent_name
23
- end
24
-
25
18
  create_file(
26
19
  'kuby.rb',
27
20
  <<~END
@@ -59,13 +52,7 @@ class KubyGenerator < Rails::Generators::Base
59
52
 
60
53
  kubernetes do
61
54
  # Add a plugin that facilitates deploying a Rails app.
62
- add_plugin :rails_app do
63
- # configure database credentials
64
- database do
65
- user app_creds[:KUBY_DB_USER]
66
- password app_creds[:KUBY_DB_PASSWORD]
67
- end
68
- end
55
+ add_plugin :rails_app
69
56
 
70
57
  # Use Docker Desktop as the provider.
71
58
  # See: https://www.docker.com/products/docker-desktop
@@ -121,4 +108,18 @@ class KubyGenerator < Rails::Generators::Base
121
108
  END
122
109
  )
123
110
  end
111
+
112
+ private
113
+
114
+ def app_name
115
+ @app_name ||= begin
116
+ app_class = Rails.application.class
117
+
118
+ if app_class.respond_to?(:module_parent_name)
119
+ app_class.module_parent_name
120
+ else
121
+ app_class.parent_name
122
+ end
123
+ end
124
+ end
124
125
  end
@@ -1,4 +1,5 @@
1
- # typed: false
1
+ # typed: ignore
2
+
2
3
  require 'kube-dsl'
3
4
  require 'kuby/cert-manager'
4
5
 
@@ -6,6 +7,8 @@ module Kuby
6
7
  module Plugins
7
8
  module RailsApp
8
9
  class Plugin < ::Kuby::Plugin
10
+ depends_on :kubernetes, '>= 1.20'
11
+
9
12
  extend ::KubeDSL::ValueFields
10
13
 
11
14
  WEB_ROLE = 'web'.freeze
@@ -18,8 +21,7 @@ module Kuby
18
21
  DEFAULT_ASSET_PATH = './public'.freeze
19
22
 
20
23
  value_field :root, default: '.'
21
- value_fields :hostname, :tls_enabled
22
- value_fields :manage_database, :database, :replicas
24
+ value_fields :hostname, :tls_enabled, :manage_database, :replicas
23
25
  value_fields :asset_url, :packs_url, :asset_path
24
26
 
25
27
  alias_method :manage_database?, :manage_database
@@ -45,11 +47,12 @@ module Kuby
45
47
  if manage_database? && @database = Database.get(self)
46
48
  @database.plugin.instance_eval(&@database_block) if @database_block
47
49
  environment.kubernetes.plugins[@database.plugin_name] = @database.plugin
48
- environment.kubernetes.add_plugin(:kube_db)
49
50
 
50
51
  environment.docker do
51
52
  insert :rewrite_db_config, RewriteDbConfig.new, after: :copy_phase
52
53
  end
54
+
55
+ @database.plugin.configure_pod_spec(deployment.spec.template.spec)
53
56
  end
54
57
 
55
58
  environment.kubernetes.add_plugin(:nginx_ingress)
@@ -87,6 +90,15 @@ module Kuby
87
90
  spec = self
88
91
 
89
92
  deployment do
93
+ metadata do
94
+ annotations do
95
+ add(
96
+ 'getkuby.io/dockerfile-checksum',
97
+ spec.environment.docker.image.dockerfile.checksum
98
+ )
99
+ end
100
+ end
101
+
90
102
  spec do
91
103
  template do
92
104
  spec do
@@ -209,15 +221,7 @@ module Kuby
209
221
  type 'Opaque'
210
222
 
211
223
  data do
212
- if master_key = ENV[MASTER_KEY_VAR]
213
- add MASTER_KEY_VAR.to_sym, master_key
214
- else
215
- master_key_path = File.join(spec.root, 'config', 'master.key')
216
-
217
- if File.exist?(master_key_path)
218
- add MASTER_KEY_VAR.to_sym, File.read(master_key_path).strip
219
- end
220
- end
224
+ add MASTER_KEY_VAR.to_sym, spec.master_key
221
225
  end
222
226
  end
223
227
 
@@ -237,13 +241,6 @@ module Kuby
237
241
  add :app, kube_spec.selector_app
238
242
  add :role, kube_spec.role
239
243
  end
240
-
241
- annotations do
242
- add(
243
- 'getkuby.io/dockerfile-checksum',
244
- kube_spec.environment.docker.image.dockerfile.checksum
245
- )
246
- end
247
244
  end
248
245
 
249
246
  spec do
@@ -313,7 +310,7 @@ module Kuby
313
310
 
314
311
  init_container(:create_db) do
315
312
  name "#{kube_spec.selector_app}-create-db"
316
- command %w(bundle exec rake kuby:rails_app:db:create_unless_exists)
313
+ command %w(bundle exec rake kuby:rails_app:db:bootstrap)
317
314
 
318
315
  env_from do
319
316
  config_map_ref do
@@ -330,7 +327,7 @@ module Kuby
330
327
 
331
328
  init_container(:migrate_db) do
332
329
  name "#{kube_spec.selector_app}-migrate-db"
333
- command %w(bundle exec rake db:migrate)
330
+ command %w(bundle exec rake kuby:rails_app:db:migrate)
334
331
 
335
332
  env_from do
336
333
  config_map_ref do
@@ -364,7 +361,7 @@ module Kuby
364
361
  spec = self
365
362
  tls_enabled = @tls_enabled
366
363
 
367
- @ingress ||= KubeDSL::DSL::Extensions::V1beta1::Ingress.new do
364
+ @ingress ||= KubeDSL::DSL::Networking::V1::Ingress.new do
368
365
  metadata do
369
366
  name "#{spec.selector_app}-ingress"
370
367
  namespace spec.namespace.metadata.name
@@ -381,10 +378,16 @@ module Kuby
381
378
  http do
382
379
  path do
383
380
  path '/'
381
+ path_type 'Prefix'
384
382
 
385
383
  backend do
386
- service_name spec.service.metadata.name
387
- service_port spec.service.spec.ports.first.port
384
+ service do
385
+ name spec.service.metadata.name
386
+
387
+ port do
388
+ name spec.service.spec.ports.first.name
389
+ end
390
+ end
388
391
  end
389
392
  end
390
393
  end
@@ -410,8 +413,8 @@ module Kuby
410
413
  config_map,
411
414
  app_secrets,
412
415
  deployment,
413
- ingress,
414
- *database&.plugin&.resources
416
+ *database&.plugin&.resources,
417
+ ingress
415
418
  ]
416
419
  end
417
420
 
@@ -434,6 +437,13 @@ module Kuby
434
437
  def namespace
435
438
  environment.kubernetes.namespace
436
439
  end
440
+
441
+ def master_key
442
+ @master_key ||= ENV[MASTER_KEY_VAR] || begin
443
+ master_key_path = File.join(root, 'config', 'master.key')
444
+ File.read(master_key_path).strip if File.exist?(master_key_path)
445
+ end
446
+ end
437
447
  end
438
448
  end
439
449
  end
@@ -6,7 +6,7 @@ module Kuby
6
6
  class Sqlite < ::Kuby::Plugin
7
7
  attr_reader :environment
8
8
 
9
- def initialize(environment, *)
9
+ def initialize(environment, *_)
10
10
  @environment = environment
11
11
  end
12
12
 
@@ -15,8 +15,12 @@ module Kuby
15
15
  environment.docker.package_phase.add(:sqlite_client)
16
16
  end
17
17
 
18
- def requires_credentials?
19
- false
18
+ def configure_pod_spec(_pod_spec)
19
+ # no configuration steps necessary
20
+ end
21
+
22
+ def bootstrap
23
+ # no boostrap steps necessary
20
24
  end
21
25
 
22
26
  def user(_user)
@@ -7,29 +7,42 @@ namespace :kuby do
7
7
  Kuby.load!
8
8
 
9
9
  config_file = File.join(Kuby.environment.kubernetes.plugin(:rails_app).root, 'config', 'database.yml')
10
- database = Kuby.environment.kubernetes.plugin(:rails_app).database
11
10
 
12
- if database.plugin.respond_to?(:rewritten_configs)
13
- File.write(config_file, YAML.dump(database.plugin.rewritten_configs))
14
- Kuby.logger.info("Wrote #{config_file}")
11
+ if rails_app = Kuby.environment.kubernetes.plugin(:rails_app)
12
+ database = rails_app.database
13
+
14
+ if database.plugin.respond_to?(:rewritten_configs)
15
+ File.write(config_file, YAML.dump(database.plugin.rewritten_configs))
16
+ Kuby.logger.info("Wrote #{config_file}")
17
+ end
15
18
  end
16
19
  end
17
20
 
18
- task :create_unless_exists do
19
- begin
20
- Rake::Task['environment'].invoke
21
- ActiveRecord::Base.connection
22
- rescue ActiveRecord::NoDatabaseError => e
23
- Rake::Task['db:create'].invoke
21
+ task :bootstrap do
22
+ Kuby.load!
23
+
24
+ if rails_app = Kuby.environment.kubernetes.plugin(:rails_app)
25
+ if database = rails_app.database
26
+ database.plugin.bootstrap
27
+ end
24
28
  end
25
29
  end
30
+
31
+ task :migrate do
32
+ next unless Kernel.const_defined?('::ActiveRecord')
33
+
34
+ Rake::Task['environment'].invoke
35
+ Rake::Task['db:migrate'].invoke
36
+ end
26
37
  end
27
38
 
28
39
  namespace :assets do
29
40
  task :copy do
30
41
  Kuby.load!
31
- assets = Kuby.environment.kubernetes.plugin(:rails_assets)
32
- assets.copy_task.run
42
+
43
+ if assets = Kuby.environment.kubernetes.plugin(:rails_assets)
44
+ assets.copy_task.run
45
+ end
33
46
  end
34
47
  end
35
48
  end
@@ -5,6 +5,7 @@ module Kuby
5
5
  autoload :AssetCopyTask, 'kuby/plugins/rails_app/asset_copy_task'
6
6
  autoload :AssetsImage, 'kuby/plugins/rails_app/assets_image'
7
7
  autoload :Assets, 'kuby/plugins/rails_app/assets'
8
+ autoload :CRDB, 'kuby/plugins/rails_app/crdb'
8
9
  autoload :Database, 'kuby/plugins/rails_app/database'
9
10
  autoload :MySQL, 'kuby/plugins/rails_app/mysql'
10
11
  autoload :Plugin, 'kuby/plugins/rails_app/plugin'
@@ -0,0 +1,16 @@
1
+ # typed: true
2
+
3
+ require 'kube-dsl'
4
+
5
+ module Kuby
6
+ module Plugins
7
+ class System < ::Kuby::Plugin
8
+ # Kubernetes maintains backwards compatibility for the three most recent
9
+ # minor versions. In other words, everything that works in v1.23 also works
10
+ # in v1.22 and v1.21. Kuby tries to do the same. As of the time of this
11
+ # writing, the most recent k8s release is v1.23, so Kuby supports v1.21 and up.
12
+ depends_on :kubernetes, '~> 1.21'
13
+ depends_on :kubectl, '~> 1.21'
14
+ end
15
+ end
16
+ end
data/lib/kuby/plugins.rb CHANGED
@@ -3,5 +3,6 @@ module Kuby
3
3
  module Plugins
4
4
  autoload :NginxIngress, 'kuby/plugins/nginx_ingress'
5
5
  autoload :RailsApp, 'kuby/plugins/rails_app'
6
+ autoload :System, 'kuby/plugins/system'
6
7
  end
7
8
  end
data/lib/kuby/railtie.rb CHANGED
@@ -1,11 +1,41 @@
1
- # typed: strict
1
+ # typed: ignore
2
+
2
3
  require 'logger'
3
4
  require 'rails/railtie'
5
+ require 'rails/command'
6
+ require 'rails/commands/dbconsole/dbconsole_command'
4
7
 
5
8
  module Kuby
9
+ module CockroachConsoleMonkeypatch
10
+ def start
11
+ config_hash = if respond_to?(:config)
12
+ config_hash = config
13
+ else
14
+ config_hash = db_config.configuration_hash.stringify_keys
15
+ end
16
+
17
+ return super unless config_hash['adapter'] == 'cockroachdb'
18
+
19
+ ENV['PGUSER'] = config_hash['username'] if config_hash['username']
20
+ ENV['PGHOST'] = config_hash['host'] if config_hash['host']
21
+ ENV['PGPORT'] = config_hash['port'].to_s if config_hash['port']
22
+ ENV['PGPASSWORD'] = config_hash['password'].to_s if config_hash['password'] && @options['include_password']
23
+ ENV['PGSSLMODE'] = config_hash['sslmode'].to_s if config_hash['sslmode']
24
+ ENV['PGSSLCERT'] = config_hash['sslcert'].to_s if config_hash['sslcert']
25
+ ENV['PGSSLKEY'] = config_hash['sslkey'].to_s if config_hash['sslkey']
26
+ ENV['PGSSLROOTCERT'] = config_hash['sslrootcert'].to_s if config_hash['sslrootcert']
27
+
28
+ find_cmd_and_exec('psql', config_hash['database'])
29
+ end
30
+ end
31
+
6
32
  class Railtie < ::Rails::Railtie
7
33
  initializer 'kuby.health_check_middleware' do |app|
8
34
  app.middleware.use Kuby::Middleware::HealthCheck
9
35
  end
36
+
37
+ initializer 'kuby.cockroachdb_console_support' do
38
+ Rails::DBConsole.prepend(CockroachConsoleMonkeypatch)
39
+ end
10
40
  end
11
41
  end
data/lib/kuby/tasks.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # typed: false
2
+ require 'rake'
2
3
  require 'rouge'
3
4
 
4
5
  module Kuby
@@ -9,9 +10,9 @@ module Kuby
9
10
  @environment = environment
10
11
  end
11
12
 
12
- def print_dockerfiles(only: nil)
13
+ def print_dockerfiles(only: [])
13
14
  kubernetes.docker_images.each do |image|
14
- next if only && image.identifier != only
15
+ next unless only.empty? || only.include?(image.identifier)
15
16
 
16
17
  image = image.current_version
17
18
  identifier = image.identifier ? " ##{image.identifier}" : ""
@@ -24,28 +25,96 @@ module Kuby
24
25
  end
25
26
  end
26
27
 
27
- def setup
28
- environment.kubernetes.setup
28
+ def setup(only: [])
29
+ environment.kubernetes.setup(only: only.map(&:to_sym))
29
30
  end
30
31
 
31
- def build(build_args = {}, docker_args = [], only: nil, ignore_missing_args: false)
32
+ def remove_plugin(name)
33
+ plugin = environment.kubernetes.plugin(name)
34
+
35
+ # Allow running uninstallation procedures for plugins that are no longer part
36
+ # of the environment. This is especially useful for plugins Kuby adds by default
37
+ # in one version and removes in another, like KubeDB.
38
+ plugin ||= if plugin_class = Kuby.plugins.find(name.to_sym)
39
+ plugin_class.new(environment)
40
+ end
41
+
42
+ unless plugin
43
+ Kuby.logger.fatal(
44
+ "No plugin found named '#{name}'. Run `kuby plugin list -a` to show a list of all available plugins."
45
+ )
46
+
47
+ exit 1
48
+ end
49
+
50
+ plugin.remove
51
+ end
52
+
53
+ def list_plugins(all: false)
54
+ Kuby.plugins.each do |name, _|
55
+ if environment.kubernetes.plugins.include?(name)
56
+ Kuby.logger.info("* #{name}")
57
+ elsif all
58
+ Kuby.logger.info(" #{name}")
59
+ end
60
+ end
61
+ end
62
+
63
+ def run_rake_tasks(tasks)
64
+ if tasks.empty?
65
+ Kuby.logger.fatal('Please specify at least one task to run.')
66
+ exit 1
67
+ end
68
+
69
+ Kuby.load_rake_tasks!
70
+ Rake::Task[tasks.join(' ')].invoke
71
+ end
72
+
73
+ def list_rake_tasks
74
+ Kuby.load_rake_tasks!
75
+
76
+ rows = Rake::Task.tasks.map do |task|
77
+ [task.name, task.full_comment || '']
78
+ end
79
+
80
+ puts Kuby::Utils::Table.new(%w(NAME DESCRIPTION), rows).to_s
81
+ end
82
+
83
+ def build(build_args = {}, docker_args = [], only: [], ignore_missing_args: false, context: nil, cache_from_latest: true)
32
84
  check_platform(docker_args)
85
+
86
+ if master_key = rails_app.master_key
87
+ build_args['RAILS_MASTER_KEY'] = master_key
88
+ end
89
+
33
90
  check_build_args(build_args) unless ignore_missing_args
34
91
 
35
92
  kubernetes.docker_images.each do |image|
36
- next if only && image.identifier != only
37
-
93
+ next unless only.empty? || only.include?(image.identifier)
38
94
  return unless perform_docker_login_if_necessary(image)
39
95
 
96
+ build_kwargs = {}
97
+
98
+ if cache_from_latest
99
+ latest_image_url = "#{image.image_url}:#{Kuby::Docker::LATEST_TAG}"
100
+ Kuby.logger.info("Pulling image #{latest_image_url}")
101
+
102
+ begin
103
+ image.pull(Kuby::Docker::LATEST_TAG)
104
+ build_kwargs[:cache_from] = latest_image_url
105
+ rescue Kuby::Docker::PullError
106
+ end
107
+ end
108
+
40
109
  image = image.new_version
41
110
  Kuby.logger.info("Building image #{image.image_url} with tags #{image.tags.join(', ')}")
42
- image.build(build_args, docker_args)
111
+ image.build(build_args, docker_args, context: context, **build_kwargs)
43
112
  end
44
113
  end
45
114
 
46
- def push(only: nil)
115
+ def push(only: [])
47
116
  kubernetes.docker_images.each do |image|
48
- next if only && image.identifier != only
117
+ next unless only.empty? || only.include?(image.identifier)
49
118
 
50
119
  image = image.current_version
51
120
  Kuby.logger.info("Pushing image #{image.image_url} with tags #{image.tags.join(', ')}")
@@ -95,6 +164,18 @@ module Kuby
95
164
  puts File.read(path)
96
165
  end
97
166
 
167
+ def print_images
168
+ rows = kubernetes.docker_images.flat_map do |image|
169
+ image = image.current_version
170
+
171
+ image.tags.map do |tag|
172
+ [image.identifier, "#{image.image_url}:#{tag}"]
173
+ end
174
+ end
175
+
176
+ puts Kuby::Utils::Table.new(%w(IDENTIFIER URL), rows).to_s
177
+ end
178
+
98
179
  def kubectl(*cmd)
99
180
  kubernetes_cli.run_cmd(cmd)
100
181
  end
@@ -3,14 +3,14 @@
3
3
  module Kuby
4
4
  class TrailingHash < Hash
5
5
  def each(&block)
6
- return to_enum(T.must(__method__)) unless block_given?
6
+ return to_enum(__method__) unless block_given?
7
7
 
8
8
  seen_keys = []
9
9
  keys_before = keys
10
10
 
11
11
  until keys_before.empty?
12
12
  keys_before.each do |k|
13
- yield k, T.must(self[k])
13
+ yield k, self[k]
14
14
  seen_keys << k
15
15
  end
16
16
 
@@ -0,0 +1,68 @@
1
+ module Kuby
2
+ module Utils
3
+ module SemVer
4
+ class Constraint
5
+ OPERATOR_MAP = {
6
+ '=' => :eq,
7
+ '>' => :gt,
8
+ '>=' => :gteq,
9
+ '<' => :lt,
10
+ '<=' => :lteq,
11
+ '~>' => :waka
12
+ }
13
+
14
+ OPERATOR_MAP.freeze
15
+
16
+ OPERATOR_INVERSE = OPERATOR_MAP.invert.freeze
17
+
18
+ attr_reader :operator, :version
19
+
20
+ def self.parse(str)
21
+ op, ver = str.split(' ')
22
+ new(OPERATOR_MAP.fetch(op), Version.parse(ver, default: nil))
23
+ end
24
+
25
+ def initialize(operator, version)
26
+ @operator = operator
27
+ @version = version
28
+ end
29
+
30
+ def to_s
31
+ @str ||= "#{OPERATOR_INVERSE[operator]} #{version}"
32
+ end
33
+
34
+ def satisfied_by?(other_version)
35
+ case operator
36
+ when :waka
37
+ arr = version.to_a
38
+ other_arr = other_version.to_a
39
+
40
+ arr.each_with_index do |digit, idx|
41
+ break unless digit
42
+
43
+ next_digit = arr[idx + 1]
44
+
45
+ if next_digit
46
+ return false if other_arr[idx] != digit
47
+ else
48
+ return false if other_arr[idx] < digit
49
+ end
50
+ end
51
+
52
+ true
53
+ when :eq
54
+ other_version == version
55
+ when :gt
56
+ other_version > version
57
+ when :gteq
58
+ other_version >= version
59
+ when :lt
60
+ other_version < version
61
+ when :lteq
62
+ other_version <= version
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,25 @@
1
+ module Kuby
2
+ module Utils
3
+ module SemVer
4
+ class ConstraintSet
5
+ attr_reader :constraints
6
+
7
+ def self.parse(*arr)
8
+ new(arr.map { |c| Constraint.parse(c) })
9
+ end
10
+
11
+ def initialize(constraints)
12
+ @constraints = constraints
13
+ end
14
+
15
+ def satisfied_by?(version)
16
+ constraints.all? { |c| c.satisfied_by?(version) }
17
+ end
18
+
19
+ def to_s
20
+ @str ||= constraints.map(&:to_s).join(', ')
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,49 @@
1
+ module Kuby
2
+ module Utils
3
+ module SemVer
4
+ class Version
5
+ include Comparable
6
+
7
+ attr_reader :major, :minor, :patch
8
+
9
+ def self.parse(str, default: 0)
10
+ major, minor, patch = str.split('.')
11
+
12
+ new(
13
+ major ? major.to_i : default,
14
+ minor ? minor.to_i : default,
15
+ patch ? patch.to_i : default
16
+ )
17
+ end
18
+
19
+ def initialize(major, minor, patch)
20
+ @major = major
21
+ @minor = minor
22
+ @patch = patch
23
+ end
24
+
25
+ def to_s
26
+ @str ||= [major, minor, patch].compact.join('.')
27
+ end
28
+
29
+ def to_a
30
+ @arr ||= [major, minor, patch]
31
+ end
32
+
33
+ def <=>(other)
34
+ other_arr = other.to_a
35
+
36
+ to_a.each_with_index do |digit, idx|
37
+ other_digit = other_arr[idx] || 0
38
+
39
+ if digit != other_digit
40
+ return digit <=> other_digit
41
+ end
42
+ end
43
+
44
+ 0
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,17 @@
1
+ module Kuby
2
+ module Utils
3
+ module SemVer
4
+ autoload :Constraint, 'kuby/utils/sem_ver/constraint'
5
+ autoload :ConstraintSet, 'kuby/utils/sem_ver/constraint_set'
6
+ autoload :Version, 'kuby/utils/sem_ver/version'
7
+
8
+ def self.parse_version(str)
9
+ Version.parse(str)
10
+ end
11
+
12
+ def self.parse_constraints(*strs)
13
+ ConstraintSet.parse(*strs)
14
+ end
15
+ end
16
+ end
17
+ end