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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/Gemfile +1 -2
- data/kuby-core.gemspec +1 -1
- data/lib/kuby.rb +2 -20
- data/lib/kuby/commands.rb +13 -67
- data/lib/kuby/docker.rb +27 -25
- data/lib/kuby/docker/alpine.rb +2 -1
- data/lib/kuby/docker/app_image.rb +19 -0
- data/lib/kuby/docker/cli.rb +4 -12
- data/lib/kuby/docker/docker_uri.rb +18 -7
- data/lib/kuby/docker/errors.rb +1 -19
- data/lib/kuby/docker/image.rb +115 -0
- data/lib/kuby/docker/layer.rb +0 -7
- data/lib/kuby/docker/local_tags.rb +9 -10
- data/lib/kuby/docker/package_phase.rb +0 -5
- data/lib/kuby/docker/packages.rb +1 -0
- data/lib/kuby/docker/remote_tags.rb +10 -5
- data/lib/kuby/docker/setup_phase.rb +17 -9
- data/lib/kuby/docker/spec.rb +29 -62
- data/lib/kuby/docker/timestamp_tag.rb +8 -1
- data/lib/kuby/docker/timestamped_image.rb +113 -0
- data/lib/kuby/environment.rb +1 -10
- data/lib/kuby/kubernetes/bare_metal_provider.rb +16 -4
- data/lib/kuby/kubernetes/deployer.rb +1 -1
- data/lib/kuby/kubernetes/docker_desktop_provider.rb +0 -15
- data/lib/kuby/kubernetes/spec.rb +21 -17
- data/lib/kuby/plugin.rb +2 -2
- data/lib/kuby/plugins/rails_app.rb +1 -0
- data/lib/kuby/plugins/rails_app/assets.rb +60 -70
- data/lib/kuby/plugins/rails_app/assets_image.rb +55 -0
- data/lib/kuby/plugins/rails_app/plugin.rb +53 -236
- data/lib/kuby/tasks.rb +30 -69
- data/lib/kuby/version.rb +1 -1
- data/spec/docker/spec_spec.rb +9 -118
- data/spec/docker/timestamped_image_spec.rb +123 -0
- data/spec/spec_helper.rb +10 -11
- metadata +9 -10
- data/lib/kuby/dev_setup.rb +0 -346
- data/lib/kuby/docker/dev_spec.rb +0 -202
- data/lib/kuby/docker/metadata.rb +0 -90
- data/lib/kuby/docker/tags.rb +0 -92
- data/lib/kuby/rails_commands.rb +0 -84
- 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
|
data/lib/kuby/docker/layer.rb
CHANGED
@@ -8,37 +8,36 @@ module Kuby
|
|
8
8
|
sig { returns CLI }
|
9
9
|
attr_reader :cli
|
10
10
|
|
11
|
-
sig { returns(
|
12
|
-
attr_reader :
|
11
|
+
sig { returns(String) }
|
12
|
+
attr_reader :image_url
|
13
13
|
|
14
14
|
sig {
|
15
15
|
params(
|
16
16
|
cli: CLI,
|
17
|
-
|
17
|
+
image_url: String
|
18
18
|
)
|
19
19
|
.void
|
20
20
|
}
|
21
|
-
def initialize(cli,
|
21
|
+
def initialize(cli, image_url)
|
22
22
|
@cli = cli
|
23
|
-
@
|
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(
|
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(
|
38
|
-
latest = images.find { |image| image[:tag] ==
|
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
|
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'
|
data/lib/kuby/docker/packages.rb
CHANGED
@@ -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(
|
12
|
-
attr_reader :
|
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
|
-
|
19
|
+
image_url: String
|
18
20
|
)
|
19
21
|
.void
|
20
22
|
}
|
21
|
-
def initialize(remote_client,
|
23
|
+
def initialize(remote_client, image_url)
|
22
24
|
@remote_client = remote_client
|
23
|
-
@
|
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 {
|
29
|
-
|
30
|
-
|
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
|
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
|
-
|
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 '#{
|
64
|
+
raise MissingDistroError, "distro '#{spec.distro_name}' hasn't been registered"
|
57
65
|
end
|
58
66
|
end
|
59
67
|
end
|
data/lib/kuby/docker/spec.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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(
|
136
|
-
def
|
137
|
-
|
138
|
-
|
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[
|
193
|
+
@distro_spec ||= if distro_klass = Kuby.distros[distro_name]
|
222
194
|
distro_klass.new(self)
|
223
195
|
else
|
224
|
-
raise MissingDistroError, "distro '#{
|
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) }
|