kuby-core 0.11.16 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
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) }