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,115 @@
1
+ # typed: strict
2
+
3
+ module Kuby
4
+ module Docker
5
+ class Image
6
+ extend T::Sig
7
+
8
+ sig { returns(String) }
9
+ attr_reader :image_url
10
+
11
+ sig { returns(Credentials) }
12
+ attr_reader :credentials
13
+
14
+ sig { returns(T.nilable(String)) }
15
+ attr_reader :main_tag
16
+
17
+ sig { returns T::Array[String] }
18
+ attr_reader :alias_tags
19
+
20
+ sig {
21
+ params(
22
+ dockerfile: T.any(Dockerfile, T.proc.returns(Dockerfile)),
23
+ image_url: String,
24
+ credentials: Credentials,
25
+ main_tag: T.nilable(String),
26
+ alias_tags: T::Array[String]
27
+ ).void
28
+ }
29
+ def initialize(dockerfile, image_url, credentials, main_tag = nil, alias_tags = [])
30
+ @dockerfile = T.let(dockerfile, T.any(Dockerfile, T.proc.returns(Dockerfile)))
31
+ @image_url = T.let(image_url, String)
32
+ @credentials = T.let(credentials, Credentials)
33
+ @main_tag = T.let(main_tag, T.nilable(String))
34
+ @alias_tags = T.let(alias_tags, T::Array[String])
35
+
36
+ @image_host = T.let(@image_host, T.nilable(String))
37
+ @image_hostname = T.let(@image_hostname, T.nilable(String))
38
+ @image_repo = T.let(@image_repo, T.nilable(String))
39
+ @full_image_uri = T.let(@full_image_uri, T.nilable(DockerURI))
40
+ @docker_cli = T.let(@docker_cli, T.nilable(Docker::CLI))
41
+ end
42
+
43
+ sig { returns(Image) }
44
+ def new_version
45
+ raise NotImplementedError, 'please use a Docker::Image subclass'
46
+ end
47
+
48
+ sig { returns(Image) }
49
+ def current_version
50
+ raise NotImplementedError, 'please use a Docker::Image subclass'
51
+ end
52
+
53
+ sig { params(current_tag: T.nilable(String)).returns(Image) }
54
+ def previous_version(current_tag = nil)
55
+ raise NotImplementedError, 'please use a Docker::Image subclass'
56
+ end
57
+
58
+ sig { returns(Dockerfile) }
59
+ def dockerfile
60
+ if @dockerfile.respond_to?(:call)
61
+ T.cast(@dockerfile, T.proc.returns(Dockerfile)).call
62
+ else
63
+ T.cast(@dockerfile, Dockerfile)
64
+ end
65
+ end
66
+
67
+ sig { returns(String) }
68
+ def image_host
69
+ @image_host ||= "#{image_uri.host}:#{image_uri.port}"
70
+ end
71
+
72
+ sig { returns(String) }
73
+ def image_hostname
74
+ @image_hostname ||= image_uri.host
75
+ end
76
+
77
+ sig { returns(String) }
78
+ def image_repo
79
+ @image_repo ||= image_uri.path
80
+ end
81
+
82
+ sig { returns(DockerURI) }
83
+ def image_uri
84
+ @full_image_uri ||= DockerURI.parse(image_url)
85
+ end
86
+
87
+ sig { returns(T::Array[String]) }
88
+ def tags
89
+ [main_tag, *alias_tags].compact
90
+ end
91
+
92
+ sig { params(build_args: T::Hash[String, String]).void }
93
+ def build(build_args = {})
94
+ raise NotImplementedError, 'please use a Docker::Image subclass'
95
+ end
96
+
97
+ sig { params(tag: String).void }
98
+ def push(tag)
99
+ raise NotImplementedError, 'please use a Docker::Image subclass'
100
+ end
101
+
102
+ sig { returns(Docker::CLI) }
103
+ def docker_cli
104
+ @docker_cli ||= Docker::CLI.new
105
+ end
106
+
107
+ private
108
+
109
+ sig { params(main_tag: String, alias_tags: T::Array[String]).returns(Image) }
110
+ def duplicate_with_tags(main_tag, alias_tags)
111
+ self.class.new(dockerfile, image_url, credentials, main_tag, alias_tags)
112
+ end
113
+ end
114
+ end
115
+ end
@@ -21,13 +21,6 @@ module Kuby
21
21
  raise NotImplementedError,
22
22
  "#{__method__} must be defined in derived classes"
23
23
  end
24
-
25
- private
26
-
27
- sig { returns(Metadata) }
28
- def metadata
29
- environment.docker.metadata
30
- end
31
24
  end
32
25
  end
33
26
  end
@@ -8,37 +8,36 @@ module Kuby
8
8
  sig { returns CLI }
9
9
  attr_reader :cli
10
10
 
11
- sig { returns(Metadata) }
12
- attr_reader :metadata
11
+ sig { returns(String) }
12
+ attr_reader :image_url
13
13
 
14
14
  sig {
15
15
  params(
16
16
  cli: CLI,
17
- metadata: Metadata
17
+ image_url: String
18
18
  )
19
19
  .void
20
20
  }
21
- def initialize(cli, metadata)
21
+ def initialize(cli, image_url)
22
22
  @cli = cli
23
- @metadata = metadata
24
-
23
+ @image_url = image_url
25
24
  @latest_timestamp_tag = T.let(@latest_timestamp_tag, T.nilable(TimestampTag))
26
25
  end
27
26
 
28
27
  sig { returns(T::Array[String]) }
29
28
  def tags
30
- images = cli.images(metadata.image_url)
29
+ images = cli.images(image_url)
31
30
  images.map { |image| T.must(image[:tag]) }
32
31
  end
33
32
 
34
33
  sig { returns(T::Array[String]) }
35
34
  def latest_tags
36
35
  # find "latest" tag
37
- images = cli.images(metadata.image_url)
38
- latest = images.find { |image| image[:tag] == Tags::LATEST }
36
+ images = cli.images(image_url)
37
+ latest = images.find { |image| image[:tag] == Kuby::Docker::LATEST_TAG }
39
38
 
40
39
  unless latest
41
- raise MissingTagError.new(Tags::LATEST)
40
+ raise MissingTagError, "could not find tag #{Kuby::Docker::LATEST_TAG}"
42
41
  end
43
42
 
44
43
  # find all tags that point to the same image as 'latest'
@@ -69,11 +69,6 @@ module Kuby
69
69
  raise MissingPackageError, "package '#{package_name}' hasn't been registered"
70
70
  end
71
71
  end
72
-
73
- sig { returns(Metadata) }
74
- def metadata
75
- environment.docker.metadata
76
- end
77
72
  end
78
73
  end
79
74
  end
@@ -1,4 +1,5 @@
1
1
  # typed: strict
2
+
2
3
  module Kuby
3
4
  module Docker
4
5
  module Packages
@@ -1,5 +1,7 @@
1
1
  # typed: strict
2
2
 
3
+ require 'docker/remote'
4
+
3
5
  module Kuby
4
6
  module Docker
5
7
  class RemoteTags
@@ -8,24 +10,27 @@ module Kuby
8
10
  sig { returns(::Docker::Remote::Client) }
9
11
  attr_reader :remote_client
10
12
 
11
- sig { returns(Metadata) }
12
- attr_reader :metadata
13
+ sig { returns(String) }
14
+ attr_reader :image_url
13
15
 
14
16
  sig {
15
17
  params(
16
18
  remote_client: ::Docker::Remote::Client,
17
- metadata: Metadata
19
+ image_url: String
18
20
  )
19
21
  .void
20
22
  }
21
- def initialize(remote_client, metadata)
23
+ def initialize(remote_client, image_url)
22
24
  @remote_client = remote_client
23
- @metadata = metadata
25
+ @image_url = image_url
24
26
  end
25
27
 
26
28
  sig { returns(T::Array[String]) }
27
29
  def tags
28
30
  remote_client.tags
31
+ rescue ::Docker::Remote::UnknownRepoError
32
+ # this can happen if we've never pushed to the repo before
33
+ []
29
34
  end
30
35
 
31
36
  sig { returns(T::Array[String]) }
@@ -7,9 +7,6 @@ module Kuby
7
7
 
8
8
  DEFAULT_WORKING_DIR = T.let('/usr/src/app'.freeze, String)
9
9
 
10
- sig { returns(T.nilable(String)) }
11
- attr_reader :base_image
12
-
13
10
  sig { params(base_image: String).void }
14
11
  attr_writer :base_image
15
12
 
@@ -25,35 +22,46 @@ module Kuby
25
22
  sig { params(rails_env: String).void }
26
23
  attr_writer :rails_env
27
24
 
28
- sig { params(environment: Environment).void }
29
- def initialize(environment)
30
- super
25
+ sig { returns(T.nilable(Docker::Spec)) }
26
+ attr_reader :docker_spec
27
+
28
+ sig { params(environment: Environment, docker_spec: Docker::Spec).void }
29
+ def initialize(environment, docker_spec)
30
+ super(environment)
31
31
 
32
32
  @base_image = T.let(@base_image, T.nilable(String))
33
33
  @working_dir = T.let(@working_dir, T.nilable(String))
34
34
  @rails_env = T.let(@rails_env, T.nilable(String))
35
+ @docker_spec = T.let(docker_spec, T.nilable(Docker::Spec))
35
36
  end
36
37
 
37
38
  sig { override.params(dockerfile: Dockerfile).void }
38
39
  def apply_to(dockerfile)
39
- dockerfile.from(base_image || default_base_image)
40
+ dockerfile.from(base_image)
40
41
  dockerfile.workdir(working_dir || DEFAULT_WORKING_DIR)
41
42
  dockerfile.env("RAILS_ENV=#{rails_env || Kuby.env}")
42
43
  dockerfile.env("KUBY_ENV=#{Kuby.env}")
43
44
  dockerfile.arg('RAILS_MASTER_KEY')
44
45
  end
45
46
 
47
+ sig { returns(String) }
48
+ def base_image
49
+ @base_image || default_base_image
50
+ end
51
+
46
52
  private
47
53
 
48
54
  sig { returns(String) }
49
55
  def default_base_image
50
- case metadata.distro
56
+ spec = T.must(docker_spec)
57
+
58
+ case spec.distro_name
51
59
  when :debian
52
60
  "ruby:#{RUBY_VERSION}"
53
61
  when :alpine
54
62
  "ruby:#{RUBY_VERSION}-alpine"
55
63
  else
56
- raise MissingDistroError, "distro '#{metadata.distro}' hasn't been registered"
64
+ raise MissingDistroError, "distro '#{spec.distro_name}' hasn't been registered"
57
65
  end
58
66
  end
59
67
  end
@@ -1,15 +1,18 @@
1
1
  # typed: strict
2
2
 
3
- require 'docker/remote'
4
-
5
3
  module Kuby
6
4
  module Docker
7
5
  class Spec
8
6
  extend T::Sig
9
7
 
8
+ DEFAULT_DISTRO = :debian
9
+
10
10
  sig { returns(Environment) }
11
11
  attr_reader :environment
12
12
 
13
+ sig { returns(T.nilable(String)) }
14
+ attr_reader :image_url_str
15
+
13
16
  sig { params(environment: Environment).void }
14
17
  def initialize(environment)
15
18
  @environment = environment
@@ -22,13 +25,18 @@ module Kuby
22
25
  @copy_phase = T.let(@copy_phase, T.nilable(CopyPhase))
23
26
  @assets_phase = T.let(@assets_phase, T.nilable(AssetsPhase))
24
27
  @webserver_phase = T.let(@webserver_phase, T.nilable(WebserverPhase))
25
- @metadata = T.let(@metadata, T.nilable(Metadata))
26
28
 
29
+ @distro_name = T.let(@distro_name, T.nilable(Symbol))
27
30
  @distro_spec = T.let(@distro_spec, T.nilable(Distro))
28
- @cli = T.let(@cli, T.nilable(CLI))
29
- @remote_client = T.let(@remote_client, T.nilable(::Docker::Remote::Client))
30
- @tags = T.let(@tags, T.nilable(Tags))
31
31
  @layer_stack = T.let(@layer_stack, T.nilable(Kuby::Docker::LayerStack))
32
+
33
+ @image_url_str = T.let(@image_url_str, T.nilable(String))
34
+ @image = T.let(@image, T.nilable(Docker::AppImage))
35
+ end
36
+
37
+ sig { returns(Symbol) }
38
+ def distro_name
39
+ @distro_name || DEFAULT_DISTRO
32
40
  end
33
41
 
34
42
  sig { params(image_url: String).void }
@@ -69,7 +77,7 @@ module Kuby
69
77
 
70
78
  sig { params(distro_name: Symbol).void }
71
79
  def distro(distro_name)
72
- metadata.distro = distro_name
80
+ @distro_name = distro_name
73
81
  @distro_spec = nil
74
82
  end
75
83
 
@@ -85,7 +93,7 @@ module Kuby
85
93
 
86
94
  sig { params(url: String).void }
87
95
  def image_url(url)
88
- metadata.image_url = url
96
+ @image_url_str = url
89
97
  end
90
98
 
91
99
  sig {
@@ -132,16 +140,22 @@ module Kuby
132
140
  @credentials
133
141
  end
134
142
 
135
- sig { returns(Dockerfile) }
136
- def to_dockerfile
137
- Dockerfile.new.tap do |df|
138
- layer_stack.each { |layer| layer.apply_to(df) }
143
+ sig { returns(Docker::AppImage) }
144
+ def image
145
+ @image ||= begin
146
+ dockerfile = Dockerfile.new.tap do |df|
147
+ layer_stack.each { |layer| layer.apply_to(df) }
148
+ end
149
+
150
+ Docker::AppImage.new(
151
+ dockerfile, T.must(image_url_str), credentials
152
+ )
139
153
  end
140
154
  end
141
155
 
142
156
  sig { returns(SetupPhase) }
143
157
  def setup_phase
144
- @setup_phase ||= SetupPhase.new(environment)
158
+ @setup_phase ||= SetupPhase.new(environment, self)
145
159
  end
146
160
 
147
161
  sig { returns(PackagePhase) }
@@ -174,62 +188,15 @@ module Kuby
174
188
  @webserver_phase ||= WebserverPhase.new(environment)
175
189
  end
176
190
 
177
- sig { returns(Metadata) }
178
- def metadata
179
- @metadata ||= Metadata.new(environment)
180
- end
181
-
182
- sig { returns(String) }
183
- def tag
184
- t = ENV.fetch('KUBY_DOCKER_TAG') do
185
- tags.latest_timestamp_tag
186
- end
187
-
188
- unless t
189
- raise MissingTagError, 'could not find latest timestamped tag'
190
- end
191
-
192
- t.to_s
193
- end
194
-
195
- sig { params(current_tag: String).returns(String) }
196
- def previous_tag(current_tag)
197
- t = tags.previous_timestamp_tag(current_tag)
198
-
199
- unless t
200
- raise MissingTagError, 'could not find previous timestamped tag'
201
- end
202
-
203
- t.to_s
204
- end
205
-
206
- sig { returns(CLI) }
207
- def cli
208
- @cli ||= Docker::CLI.new
209
- end
210
-
211
- sig { returns(::Docker::Remote::Client) }
212
- def remote_client
213
- @remote_client ||= ::Docker::Remote::Client.new(
214
- metadata.image_host, metadata.image_repo,
215
- credentials.username, credentials.password,
216
- )
217
- end
218
-
219
191
  sig { returns(Distro) }
220
192
  def distro_spec
221
- @distro_spec ||= if distro_klass = Kuby.distros[metadata.distro]
193
+ @distro_spec ||= if distro_klass = Kuby.distros[distro_name]
222
194
  distro_klass.new(self)
223
195
  else
224
- raise MissingDistroError, "distro '#{metadata.distro}' hasn't been registered"
196
+ raise MissingDistroError, "distro '#{distro_name}' hasn't been registered"
225
197
  end
226
198
  end
227
199
 
228
- sig { returns(Tags) }
229
- def tags
230
- @tags ||= Tags.new(cli, remote_client, metadata)
231
- end
232
-
233
200
  private
234
201
 
235
202
  sig { returns(Kuby::Docker::LayerStack) }