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
@@ -5,52 +5,56 @@ require 'docker/remote'
5
5
  module Kuby
6
6
  module Docker
7
7
  class TimestampedImage < Image
8
- extend T::Sig
9
-
10
- sig {
11
- params(
12
- dockerfile: T.any(Dockerfile, T.proc.returns(Dockerfile)),
13
- image_url: String,
14
- credentials: Credentials,
15
- registry_index_url_str: T.nilable(String),
16
- main_tag: T.nilable(String),
17
- alias_tags: T::Array[String]
18
- ).void
19
- }
8
+ # extend T::Sig
9
+
10
+ # T::Sig::WithoutRuntime.sig {
11
+ # params(
12
+ # dockerfile: T.any(Dockerfile, T.proc.returns(Kuby::Docker::Dockerfile)),
13
+ # image_url: String,
14
+ # credentials: Kuby::Docker::Credentials,
15
+ # registry_index_url_str: T.nilable(String),
16
+ # main_tag: T.nilable(String),
17
+ # alias_tags: T::Array[String]
18
+ # ).void
19
+ # }
20
20
  def initialize(dockerfile, image_url, credentials, registry_index_url_str = nil, main_tag = nil, alias_tags = [])
21
- @new_version = T.let(@new_version, T.nilable(Image))
22
- @current_version = T.let(@current_version, T.nilable(Image))
23
- @previous_version = T.let(@previous_version, T.nilable(Image))
21
+ # @new_version = T.let(@new_version, T.nilable(Image))
22
+ # @current_version = T.let(@current_version, T.nilable(Image))
23
+ # @previous_version = T.let(@previous_version, T.nilable(Image))
24
24
 
25
- @remote_client = T.let(@remote_client, T.nilable(::Docker::Remote::Client))
26
- @local = T.let(@local, T.nilable(LocalTags))
27
- @remote = T.let(@remote, T.nilable(RemoteTags))
25
+ # @remote_client = T.let(@remote_client, T.nilable(::Docker::Remote::Client))
26
+ # @local = T.let(@local, T.nilable(LocalTags))
27
+ # @remote = T.let(@remote, T.nilable(RemoteTags))
28
28
 
29
29
  super
30
30
  end
31
31
 
32
- sig { returns(Image) }
32
+ # T::Sig::WithoutRuntime.sig { returns(Kuby::Docker::Image) }
33
33
  def new_version
34
34
  @new_version ||= duplicate_with_tags(
35
- TimestampTag.new(Time.now).to_s, [Kuby::Docker::LATEST_TAG]
35
+ TimestampTag.now.to_s, [Kuby::Docker::LATEST_TAG]
36
36
  )
37
37
  end
38
38
 
39
- sig { returns(Image) }
39
+ # T::Sig::WithoutRuntime.sig { returns(Kuby::Docker::Image) }
40
40
  def current_version
41
- @current_version ||= duplicate_with_tags(
42
- latest_timestamp_tag.to_s, [Kuby::Docker::LATEST_TAG]
43
- )
41
+ @current_version ||= begin
42
+ duplicate_with_tags(
43
+ latest_timestamp_tag.to_s, [Kuby::Docker::LATEST_TAG]
44
+ )
45
+ rescue MissingTagError
46
+ new_version
47
+ end
44
48
  end
45
49
 
46
- sig { params(current_tag: T.nilable(String)).returns(Image) }
50
+ # T::Sig::WithoutRuntime.sig { params(current_tag: T.nilable(String)).returns(Kuby::Docker::Image) }
47
51
  def previous_version(current_tag = nil)
48
52
  @previous_version ||= duplicate_with_tags(
49
53
  previous_timestamp_tag(current_tag).to_s, []
50
54
  )
51
55
  end
52
56
 
53
- sig { params(current_tag: T.nilable(String)).returns(TimestampTag) }
57
+ # T::Sig::WithoutRuntime.sig { params(current_tag: T.nilable(String)).returns(Kuby::Docker::TimestampTag) }
54
58
  def previous_timestamp_tag(current_tag = nil)
55
59
  current_tag = TimestampTag.try_parse(current_tag || latest_timestamp_tag.to_s)
56
60
  raise MissingTagError, 'could not find current timestamp tag' unless current_tag
@@ -64,48 +68,70 @@ module Kuby
64
68
  idx ||= 0
65
69
  raise MissingTagError, 'could not find previous timestamp tag' unless idx > 0
66
70
 
67
- T.must(all_tags[idx - 1])
71
+ all_tags[idx - 1]
68
72
  end
69
73
 
70
- sig { returns(TimestampTag) }
74
+ # T::Sig::WithoutRuntime.sig { returns(Kuby::Docker::TimestampTag) }
71
75
  def latest_timestamp_tag
72
76
  tag = timestamp_tags.sort.last
73
77
  raise MissingTagError, 'could not find latest timestamp tag' unless tag
74
78
  tag
75
79
  end
76
80
 
77
- sig { params(build_args: T::Hash[String, String], docker_args: T::Array[String]).void }
78
- def build(build_args = {}, docker_args = [])
79
- docker_cli.build(new_version, build_args: build_args, docker_args: docker_args)
80
- @current_version = new_version
81
- @new_version = nil
81
+ # T::Sig::WithoutRuntime.sig {
82
+ # params(
83
+ # build_args: T::Hash[String, String],
84
+ # docker_args: T::Array[String],
85
+ # context: T.nilable(String),
86
+ # cache_from: T.nilable(String)
87
+ # ).void
88
+ # }
89
+ def build(build_args = {}, docker_args = [], context: nil, cache_from: nil)
90
+ docker_cli.build(
91
+ self,
92
+ build_args: build_args,
93
+ docker_args: docker_args,
94
+ context: context,
95
+ cache_from: cache_from
96
+ )
82
97
  end
83
98
 
84
- sig { params(tag: String).void }
99
+ # T::Sig::WithoutRuntime.sig { params(tag: String).void }
85
100
  def push(tag)
86
101
  docker_cli.push(image_url, tag)
87
102
  end
88
103
 
104
+ # T::Sig::WithoutRuntime.sig { params(tag: String).void }
105
+ def pull(tag)
106
+ docker_cli.pull(image_url, tag)
107
+ end
108
+
109
+ # T::Sig::WithoutRuntime.sig { returns(T::Boolean) }
110
+ def exists?
111
+ return false unless main_tag
112
+ timestamp_tags.include?(TimestampTag.try_parse(main_tag))
113
+ end
114
+
89
115
  private
90
116
 
91
- sig { returns(::Docker::Remote::Client) }
117
+ # T::Sig::WithoutRuntime.sig { returns(::Docker::Remote::Client) }
92
118
  def remote_client
93
119
  @remote_client ||= ::Docker::Remote::Client.new(
94
120
  registry_index_host, image_repo, credentials.username, credentials.password,
95
121
  )
96
122
  end
97
123
 
98
- sig { returns(T::Array[TimestampTag]) }
124
+ # T::Sig::WithoutRuntime.sig { returns(T::Array[Kuby::Docker::TimestampTag]) }
99
125
  def timestamp_tags
100
126
  (local.timestamp_tags + remote.timestamp_tags).uniq
101
127
  end
102
128
 
103
- sig { returns(LocalTags) }
129
+ # T::Sig::WithoutRuntime.sig { returns(Kuby::Docker::LocalTags) }
104
130
  def local
105
131
  @local ||= LocalTags.new(docker_cli, image_url)
106
132
  end
107
133
 
108
- sig { returns(RemoteTags) }
134
+ # T::Sig::WithoutRuntime.sig { returns(Kuby::Docker::RemoteTags) }
109
135
  def remote
110
136
  @remote ||= RemoteTags.new(remote_client, registry_index_host)
111
137
  end
@@ -3,28 +3,28 @@
3
3
  module Kuby
4
4
  module Docker
5
5
  class WebserverPhase < Layer
6
- extend T::Sig
6
+ # extend T::Sig
7
7
 
8
8
  class Webserver
9
- extend T::Sig
10
- extend T::Helpers
9
+ # extend T::Sig
10
+ # extend T::Helpers
11
11
 
12
- abstract!
12
+ # abstract!
13
13
 
14
- sig { returns(WebserverPhase) }
14
+ # T::Sig::WithoutRuntime.sig { returns(WebserverPhase) }
15
15
  attr_reader :phase
16
16
 
17
- sig { params(phase: WebserverPhase).void }
17
+ # T::Sig::WithoutRuntime.sig { params(phase: WebserverPhase).void }
18
18
  def initialize(phase)
19
19
  @phase = phase
20
20
  end
21
21
 
22
- sig { abstract.params(dockerfile: Dockerfile).void }
22
+ # T::Sig::WithoutRuntime.sig { abstract.params(dockerfile: Dockerfile).void }
23
23
  def apply_to(dockerfile); end
24
24
  end
25
25
 
26
26
  class Puma < Webserver
27
- sig { override.params(dockerfile: Dockerfile).void }
27
+ # T::Sig::WithoutRuntime.sig { override.params(dockerfile: Dockerfile).void }
28
28
  def apply_to(dockerfile)
29
29
  dockerfile.cmd(
30
30
  'puma',
@@ -39,43 +39,43 @@ module Kuby
39
39
  end
40
40
  end
41
41
 
42
- DEFAULT_PORT = T.let(8080, Integer)
43
- WEBSERVER_MAP = T.let({ puma: Puma }.freeze, T::Hash[Symbol, T.class_of(Webserver)])
42
+ DEFAULT_PORT = 8080
43
+ WEBSERVER_MAP = { puma: Puma }.freeze
44
44
 
45
- sig { params(port: Integer).void }
45
+ # T::Sig::WithoutRuntime.sig { params(port: Integer).returns(Integer) }
46
46
  attr_writer :port
47
47
 
48
- sig { returns(T.nilable(Symbol)) }
48
+ # T::Sig::WithoutRuntime.sig { returns(T.nilable(Symbol)) }
49
49
  attr_reader :webserver
50
50
 
51
- sig { params(webserver: Symbol).void }
51
+ # T::Sig::WithoutRuntime.sig { params(webserver: Symbol).returns(Symbol) }
52
52
  attr_writer :webserver
53
53
 
54
- sig { override.params(environment: Environment).void }
54
+ # T::Sig::WithoutRuntime.sig { override.params(environment: Environment).void }
55
55
  def initialize(environment)
56
56
  super
57
57
 
58
- @port = T.let(@port, T.nilable(Integer))
59
- @webserver = T.let(@webserver, T.nilable(Symbol))
58
+ # @port = T.let(@port, T.nilable(Integer))
59
+ # @webserver = T.let(@webserver, T.nilable(Symbol))
60
60
  end
61
61
 
62
- sig { override.params(dockerfile: Dockerfile).void }
62
+ # T::Sig::WithoutRuntime.sig { override.params(dockerfile: Dockerfile).void }
63
63
  def apply_to(dockerfile)
64
64
  ws = webserver || default_webserver
65
- ws_class = WEBSERVER_MAP[T.must(ws)]
65
+ ws_class = WEBSERVER_MAP[ws]
66
66
  raise "No webserver named #{ws}" unless ws_class
67
67
 
68
68
  ws_class.new(self).apply_to(dockerfile)
69
69
  end
70
70
 
71
- sig { returns(Integer) }
71
+ # T::Sig::WithoutRuntime.sig { returns(Integer) }
72
72
  def port
73
73
  @port || DEFAULT_PORT
74
74
  end
75
75
 
76
76
  private
77
77
 
78
- sig { returns(T.nilable(Symbol)) }
78
+ # T::Sig::WithoutRuntime.sig { returns(T.nilable(Symbol)) }
79
79
  def default_webserver
80
80
  if Gem.loaded_specs.include?('puma')
81
81
  :puma
@@ -3,13 +3,37 @@
3
3
  module Kuby
4
4
  module Docker
5
5
  class YarnPhase < Layer
6
- extend T::Sig
6
+ # extend T::Sig
7
7
 
8
- sig { params(dockerfile: Dockerfile).void }
8
+ # T::Sig::WithoutRuntime.sig { params(dockerfile: Dockerfile).void }
9
9
  def apply_to(dockerfile)
10
- # use character classes as a hack to only copy the files if they exist
11
- dockerfile.copy('package.json yarn.loc[k] .npmr[c] .yarnr[c]', './')
12
- dockerfile.run('yarn', 'install')
10
+ host_path = environment.docker.app_root_path
11
+
12
+ # if more than one file is passed to the COPY directive, the destination must be
13
+ # a directory and must end with a slash
14
+ container_path = ensure_trailing_delimiter(
15
+ File.join(
16
+ dockerfile.current_workdir,
17
+ environment.docker.app_root_path
18
+ )
19
+ )
20
+
21
+ if File.exist?(File.join(host_path, 'package.json'))
22
+ # use brackets as a hack to only copy the files if they exist
23
+ dockerfile.copy(
24
+ "#{host_path}/package.json #{host_path}/yarn.loc[k] #{host_path}/.npmr[c] #{host_path}/.yarnr[c]",
25
+ container_path
26
+ )
27
+
28
+ dockerfile.run('yarn', 'install', '--cwd', container_path)
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ # T::Sig::WithoutRuntime.sig { params(path: String).returns(String) }
35
+ def ensure_trailing_delimiter(path)
36
+ path.end_with?(File::SEPARATOR) ? path : File.join(path, '')
13
37
  end
14
38
  end
15
39
  end
data/lib/kuby/docker.rb CHANGED
@@ -4,10 +4,11 @@ require 'kuby/docker/errors'
4
4
 
5
5
  module Kuby
6
6
  module Docker
7
- LATEST_TAG = T.let('latest'.freeze, String)
7
+ LATEST_TAG = 'latest'.freeze
8
8
 
9
9
  autoload :Alpine, 'kuby/docker/alpine'
10
10
  autoload :AppImage, 'kuby/docker/app_image'
11
+ autoload :AppPhase, 'kuby/docker/app_phase'
11
12
  autoload :AssetsPhase, 'kuby/docker/assets_phase'
12
13
  autoload :BundlerPhase, 'kuby/docker/bundler_phase'
13
14
  autoload :CLI, 'kuby/docker/cli'
@@ -5,47 +5,49 @@ require 'kube-dsl'
5
5
  module Kuby
6
6
  module Kubernetes
7
7
  class BareMetalProvider < Provider
8
- extend T::Sig
8
+ # extend T::Sig
9
9
 
10
- STORAGE_CLASS_NAME = T.let('hostpath'.freeze, String)
10
+ DEFAULT_STORAGE_CLASS = 'hostpath'.freeze
11
11
 
12
12
  class Config
13
13
  extend ::KubeDSL::ValueFields
14
14
 
15
15
  value_fields :kubeconfig
16
+ value_fields :storage_class
16
17
  end
17
18
 
18
- sig { returns(Config) }
19
+ # T::Sig::WithoutRuntime.sig { returns(Config) }
19
20
  attr_reader :config
20
21
 
21
- sig { params(environment: Environment).void }
22
+ # T::Sig::WithoutRuntime.sig { params(environment: Environment).void }
22
23
  def initialize(environment)
23
- @config = T.let(Config.new, Config)
24
+ @config = Config.new
24
25
  super
25
26
  end
26
27
 
27
- sig { params(block: T.proc.void).void }
28
+ # T::Sig::WithoutRuntime.sig { params(block: T.proc.void).void }
28
29
  def configure(&block)
29
30
  config.instance_eval(&block) if block
30
31
  end
31
32
 
32
- sig { returns(String) }
33
+ # T::Sig::WithoutRuntime.sig { returns(String) }
33
34
  def kubeconfig_path
34
35
  config.kubeconfig
35
36
  end
36
37
 
37
- sig { returns(String) }
38
+ # T::Sig::WithoutRuntime.sig { returns(String) }
38
39
  def storage_class_name
39
- STORAGE_CLASS_NAME
40
+ config.storage_class
40
41
  end
41
42
 
42
43
  private
43
44
 
44
- sig { void }
45
+ # T::Sig::WithoutRuntime.sig { void }
45
46
  def after_initialize
46
47
  configure do
47
48
  # default kubeconfig path
48
49
  kubeconfig File.join(ENV['HOME'], '.kube', 'config')
50
+ storage_class DEFAULT_STORAGE_CLASS
49
51
  end
50
52
  end
51
53
  end
@@ -8,10 +8,8 @@ require 'yaml'
8
8
  module Kuby
9
9
  module Kubernetes
10
10
  class Deployer
11
- extend T::Sig
12
-
13
11
  attr_reader :environment
14
- attr_accessor :logdev
12
+ attr_writer :logdev
15
13
 
16
14
  def initialize(environment)
17
15
  @environment = environment
@@ -19,7 +17,7 @@ module Kuby
19
17
 
20
18
  def deploy
21
19
  restart_rails_deployment_if_necessary do
22
- namespaced, global = all_resources.partition do |resource|
20
+ resource_groups = all_resources.group_by do |resource|
23
21
  # Unfortunately we can't use respond_to here because all KubeDSL
24
22
  # objects use ObjectMeta, which has a namespace field. Not sure
25
23
  # why, since it makes no sense for a namespace to have a namespace.
@@ -27,8 +25,21 @@ module Kuby
27
25
  resource.metadata.namespace
28
26
  end
29
27
 
30
- deploy_global_resources(global)
31
- deploy_namespaced_resources(namespaced)
28
+ deploy_global_resources(resource_groups[nil])
29
+
30
+ resource_groups.each_pair do |ns, resources|
31
+ next if !ns
32
+
33
+ begin
34
+ deploy_namespaced_resources(resources, ns)
35
+ rescue => e
36
+ puts e.message
37
+
38
+ if bt = e.backtrace
39
+ puts bt.join("\n")
40
+ end
41
+ end
42
+ end
32
43
  end
33
44
  end
34
45
 
@@ -69,10 +80,11 @@ module Kuby
69
80
  end
70
81
  rescue KubernetesCLI::InvalidResourceError => e
71
82
  Kuby.logger.fatal(e.message)
72
- Kuby.logger.fatal(e.resource.to_resource.to_yaml)
83
+ resource = e.resource&.to_resource
84
+ Kuby.logger.fatal(resource) if resource
73
85
  end
74
86
 
75
- def deploy_namespaced_resources(resources)
87
+ def deploy_namespaced_resources(resources, ns)
76
88
  old_kubeconfig = ENV['KUBECONFIG']
77
89
  ENV['KUBECONFIG'] = provider.kubeconfig_path
78
90
 
@@ -87,7 +99,7 @@ module Kuby
87
99
  end
88
100
 
89
101
  task = ::Kuby::Kubernetes::DeployTask.new(
90
- namespace: namespace.metadata.name,
102
+ namespace: ns,
91
103
  context: cli.current_context,
92
104
  filenames: [tmpdir]
93
105
  )
@@ -97,7 +109,7 @@ module Kuby
97
109
  task.run!(verify_result: true, prune: false)
98
110
  ensure
99
111
  ENV['KUBECONFIG'] = old_kubeconfig
100
- FileUtils.rm_rf(T.must(tmpdir))
112
+ FileUtils.rm_rf(tmpdir)
101
113
  end
102
114
 
103
115
  def restart_rails_deployment_if_necessary
@@ -1,4 +1,5 @@
1
1
  # typed: true
2
+
2
3
  require 'base64'
3
4
  require 'kube-dsl'
4
5
 
@@ -1,4 +1,5 @@
1
1
  # typed: true
2
+
2
3
  require 'kubernetes-cli'
3
4
 
4
5
  module Kuby
@@ -1,5 +1,7 @@
1
1
  # typed: false
2
+
2
3
  require 'kube-dsl'
4
+ require 'rake'
3
5
 
4
6
  module Kuby
5
7
  module Kubernetes
@@ -13,6 +15,7 @@ module Kuby
13
15
  @plugins = TrailingHash.new
14
16
 
15
17
  # default plugins
18
+ add_plugin(:system)
16
19
  add_plugin(:rails_app)
17
20
  end
18
21
 
@@ -69,17 +72,59 @@ module Kuby
69
72
  def after_configuration
70
73
  @plugins.each { |_, plg| plg.after_configuration }
71
74
  provider.after_configuration
75
+ environment.docker.after_configuration
76
+
77
+ spec = self
78
+
79
+ # this must be done _after_ docker has been configured
80
+ registry_secret do
81
+ docker_config do
82
+ registry_host spec.docker.image.image_hostname
83
+ username spec.docker.image.credentials.username
84
+ password spec.docker.image.credentials.password
85
+ email spec.docker.image.credentials.email
86
+ end
87
+ end
72
88
  end
73
89
 
74
90
  def before_deploy
75
91
  @tag ||= docker.image.current_version.main_tag
76
92
 
93
+ check_dependencies!
94
+
77
95
  provider.before_deploy(resources)
78
96
  @plugins.each { |_, plg| plg.before_deploy(resources) }
79
97
  ensure
80
98
  @tag = nil
81
99
  end
82
100
 
101
+ def check_dependencies!(plugins = @plugins)
102
+ error_messages = []
103
+
104
+ plugins.each do |plg_name, plg|
105
+ plg.class.dependencies.each do |dependency|
106
+ dependable = Kuby.dependables[dependency.name]
107
+
108
+ unless dependable
109
+ error_messages << "The #{plg_name} plugin depends on #{dependency.name}, "\
110
+ "but that dependency has not been registered."
111
+
112
+ next
113
+ end
114
+
115
+ unless dependency.satisfied_by?(dependable)
116
+ error_messages << "The #{plg_name} plugin depends on #{dependency.name} "\
117
+ "#{dependency.constraints}, but the available version is #{dependable.version}."
118
+ end
119
+ end
120
+ end
121
+
122
+ unless error_messages.empty?
123
+ error_messages.each { |msg| Kuby.logger.fatal(msg) }
124
+ exit 1
125
+ end
126
+ end
127
+
83
128
  def after_deploy
84
129
  @tag ||= docker.image.current_version.main_tag
85
130
 
@@ -89,13 +134,25 @@ module Kuby
89
134
  @tag = nil
90
135
  end
91
136
 
92
- def setup
93
- provider.before_setup
94
- provider.setup
137
+ def setup(only: [])
138
+ plugins = if only.empty?
139
+ @plugins
140
+ else
141
+ @plugins.each_with_object({}) do |(name, plg), memo|
142
+ memo[name] = plg if only.include?(name)
143
+ end
144
+ end
145
+
146
+ check_dependencies!(plugins)
95
147
 
96
- @plugins.each { |_, plg| plg.before_setup }
97
- @plugins.each { |_, plg| plg.setup }
98
- @plugins.each { |_, plg| plg.after_setup }
148
+ if only.empty?
149
+ provider.before_setup
150
+ provider.setup
151
+ end
152
+
153
+ plugins.each { |_, plg| plg.before_setup }
154
+ plugins.each { |_, plg| plg.setup }
155
+ plugins.each { |_, plg| plg.after_setup }
99
156
 
100
157
  provider.after_setup
101
158
  end
@@ -149,13 +206,6 @@ module Kuby
149
206
  name "#{spec.selector_app}-registry-secret"
150
207
  namespace spec.namespace.metadata.name
151
208
  end
152
-
153
- docker_config do
154
- registry_host spec.docker.image.image_hostname
155
- username spec.docker.image.credentials.username
156
- password spec.docker.image.credentials.password
157
- email spec.docker.image.credentials.email
158
- end
159
209
  end
160
210
 
161
211
  @registry_secret.instance_eval(&block) if block
data/lib/kuby/plugin.rb CHANGED
@@ -1,4 +1,5 @@
1
- # typed: true
1
+ # typed: false
2
+
2
3
  module Kuby
3
4
  class Plugin
4
5
  attr_reader :environment
@@ -8,14 +9,35 @@ module Kuby
8
9
  after_initialize
9
10
  end
10
11
 
12
+ class << self
13
+ # returns an array of directories containing .rake files
14
+ def task_dirs
15
+ []
16
+ end
17
+
18
+ def depends_on(dependable_name, *constraints)
19
+ dependencies << Kuby::Dependency.new(dependable_name, *constraints)
20
+ end
21
+
22
+ def dependencies
23
+ @dependencies ||= []
24
+ end
25
+ end
26
+
11
27
  def configure(&block)
12
28
  # do nothing by default
13
29
  end
14
30
 
31
+ # install any global resources like operators, etc
15
32
  def setup
16
33
  # do nothing by default
17
34
  end
18
35
 
36
+ # remove all global resources installed by #setup
37
+ def remove
38
+ # do nothing by default
39
+ end
40
+
19
41
  # additional kubernetes resources that should be deployed
20
42
  def resources
21
43
  []
@@ -1,6 +1,8 @@
1
1
  # typed: false
2
2
  module Kuby
3
3
  class PluginRegistry
4
+ include Enumerable
5
+
4
6
  ANY = 'any'.freeze
5
7
 
6
8
  def register(plugin_name, plugin_klass, environment: ANY)
@@ -19,6 +21,19 @@ module Kuby
19
21
  plugins_by_env[environment] || plugins_by_env[ANY]
20
22
  end
21
23
 
24
+ def each(&block)
25
+ return to_enum(__method__) unless block
26
+
27
+ @plugins.each_pair do |plugin_name, plugins_by_env|
28
+ plugins_by_env.each_pair do |env, plugin_klass|
29
+ case env
30
+ when ANY, Kuby.env
31
+ yield plugin_name, plugin_klass
32
+ end
33
+ end
34
+ end
35
+ end
36
+
22
37
  private
23
38
 
24
39
  def plugins