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
@@ -1,346 +0,0 @@
1
- # typed: strict
2
-
3
- module Kuby
4
- class Spinner
5
- extend T::Sig
6
-
7
- PIECES = T.let(%w(- \\ | /).freeze, T::Array[String])
8
- INTERVAL = T.let(0.2, Float) # seconds
9
-
10
- sig {
11
- params(
12
- message: String,
13
- block: T.proc.params(spinner: Spinner).void
14
- )
15
- .void
16
- }
17
- def self.spin(message, &block)
18
- yield new(message)
19
- end
20
-
21
- sig { returns(String) }
22
- attr_reader :message
23
-
24
- sig { returns(Symbol) }
25
- attr_reader :status
26
-
27
- sig { params(message: String).void }
28
- def initialize(message)
29
- @message = T.let(message, String)
30
- @status = T.let(:running, Symbol)
31
- @thread = T.let(Thread.new do
32
- counter = 0
33
-
34
- while true
35
- case status
36
- when :running
37
- piece = PIECES[counter % PIECES.size]
38
- STDOUT.write "\r[#{piece}] #{message}"
39
- sleep INTERVAL
40
- counter += 1
41
- when :success
42
- STDOUT.write("\r[+] #{message}")
43
- break
44
- when :failure
45
- STDOUT.write("\r[×] #{message}")
46
- break
47
- end
48
- end
49
-
50
- puts
51
- end, Thread)
52
- end
53
-
54
- sig { void }
55
- def success
56
- @status = :success
57
- @thread.join
58
- end
59
-
60
- sig { void }
61
- def failure
62
- @status = :failure
63
- @thread.join
64
- end
65
- end
66
-
67
- class SetupTask
68
- extend T::Sig
69
-
70
- sig { returns(String) }
71
- attr_reader :message
72
-
73
- sig { returns(T.proc.void) }
74
- attr_reader :callable
75
-
76
- sig { params(message: String, callable: T.proc.void).void }
77
- def initialize(message, callable)
78
- @message = message
79
- @callable = callable
80
- end
81
-
82
- sig { void }
83
- def run
84
- callable.call
85
- end
86
- end
87
-
88
- class Pipe
89
- extend T::Sig
90
-
91
- sig { returns(Symbol) }
92
- attr_reader :name
93
-
94
- sig { returns(T.untyped) }
95
- attr_reader :cli
96
-
97
- sig { returns(StringIO) }
98
- attr_reader :out
99
-
100
- sig { returns(StringIO) }
101
- attr_reader :err
102
-
103
- sig { params(name: Symbol, cli: T.untyped).void }
104
- def initialize(name, cli)
105
- @name = T.let(name, Symbol)
106
- @cli = T.let(cli, T.untyped)
107
- @out = T.let(StringIO.new, StringIO)
108
- @err = T.let(StringIO.new, StringIO)
109
- end
110
-
111
- sig { params(block: T.proc.void).void }
112
- def wrap(&block)
113
- cli.with_pipes(out, err) do
114
- block.call
115
- end
116
- end
117
-
118
- sig { returns(T::Boolean) }
119
- def success?
120
- cli.last_status.nil? || cli.last_status.success?
121
- end
122
- end
123
-
124
- class Pipes
125
- extend T::Sig
126
- extend T::Generic
127
-
128
- include Enumerable
129
-
130
- Elem = type_member(fixed: Pipe)
131
-
132
- sig { returns(T::Array[Pipe]) }
133
- attr_reader :pipes
134
-
135
- sig { returns(T.nilable(StandardError)) }
136
- attr_reader :ex
137
-
138
- sig { params(clis: T::Hash[Symbol, T.untyped]).returns(Pipes) }
139
- def self.build(clis)
140
- new(clis.map { |name, cli| Pipe.new(name, cli) })
141
- end
142
-
143
- sig { params(pipes: T::Array[Pipe]).void }
144
- def initialize(pipes)
145
- @ex = T.let(@ex, T.nilable(StandardError))
146
- @pipes = T.let(pipes, T::Array[Pipe])
147
- end
148
-
149
- sig {
150
- override.params(
151
- block: T.proc.params(package: Pipe).void
152
- )
153
- .void
154
- }
155
- def each(&block)
156
- pipes.each(&block)
157
- end
158
-
159
- sig { params(block: T.proc.void).void }
160
- def wrap(&block)
161
- do_wrap(pipes, &block)
162
- end
163
-
164
- sig { returns(T::Boolean) }
165
- def success?
166
- pipes.all?(&:success?) && !ex
167
- end
168
-
169
- private
170
-
171
- sig {
172
- params(
173
- remaining_pipes: T::Array[Pipe],
174
- block: T.proc.void
175
- )
176
- .void
177
- }
178
- def do_wrap(remaining_pipes, &block)
179
- if remaining_pipes.empty?
180
- begin
181
- yield
182
- rescue => e
183
- @ex = e
184
- end
185
-
186
- return
187
- end
188
-
189
- T.must(remaining_pipes[0]).wrap do
190
- do_wrap(T.must(remaining_pipes[1..-1]), &block)
191
- end
192
- end
193
- end
194
-
195
- class SetupTaskList
196
- extend T::Sig
197
-
198
- sig { returns(T::Array[SetupTask]) }
199
- attr_reader :tasks
200
-
201
- sig { returns T::Hash[Symbol, T.untyped] }
202
- attr_reader :clis
203
-
204
- sig { params(tasks: T::Array[SetupTask], clis: T::Hash[Symbol, T.untyped]).void }
205
- def initialize(tasks, clis)
206
- @tasks = tasks
207
- @clis = clis
208
- end
209
-
210
- sig { void }
211
- def run
212
- tasks.each do |task|
213
- pipes = Pipes.build(clis)
214
-
215
- Spinner.spin(task.message) do |spinner|
216
- pipes.wrap { task.run }
217
-
218
- if pipes.success?
219
- spinner.success
220
- else
221
- spinner.failure
222
- print_error(pipes.ex)
223
-
224
- pipes.each do |pipe|
225
- print_streams(pipe)
226
- end
227
-
228
- return false
229
- end
230
- end
231
- end
232
-
233
- true
234
- end
235
-
236
- private
237
-
238
- sig { params(pipe: Pipe).void }
239
- def print_streams(pipe)
240
- unless pipe.out.string.strip.empty?
241
- puts("========= #{pipe.name.upcase} STDOUT ========")
242
- puts pipe.out.string
243
- end
244
-
245
- unless pipe.err.string.strip.empty?
246
- puts("========= #{pipe.name.upcase} STDERR ========")
247
- puts pipe.err.string
248
- end
249
- end
250
-
251
- sig { params(ex: T.nilable(StandardError)).void }
252
- def print_error(ex)
253
- return unless ex
254
- puts("========= RUBY ERROR ========")
255
- puts(ex.message)
256
- puts(T.must(ex.backtrace).join("\n"))
257
- end
258
- end
259
-
260
- class DevSetup
261
- extend T::Sig
262
-
263
- sig { returns(Environment) }
264
- attr_reader :environment
265
-
266
- sig { params(environment: Environment).void }
267
- def initialize(environment)
268
- @environment = T.let(environment, Environment)
269
- @setup_tasks = T.let(@setup_tasks, T.nilable(T::Array[SetupTask]))
270
- @clis = T.let(@clis, T.nilable(T::Hash[Symbol, T.untyped]))
271
- @tasks = T.let(@tasks, T.nilable(Tasks))
272
- end
273
-
274
- sig { void }
275
- def run
276
- SetupTaskList.new(setup_tasks, clis).run
277
- end
278
-
279
- private
280
-
281
- sig { returns(T::Array[SetupTask]) }
282
- def setup_tasks
283
- @setup_tasks ||= [
284
- SetupTask.new(
285
- 'Building dev Docker image', -> { tasks.build }
286
- ),
287
-
288
- SetupTask.new(
289
- 'Setting up local Kubernetes cluster', -> { tasks.setup }
290
- ),
291
-
292
- SetupTask.new(
293
- 'Deploying dev environment', -> { tasks.deploy }
294
- ),
295
-
296
- SetupTask.new(
297
- 'Installing bundler', -> {
298
- tasks.remote_system("gem install bundler -v #{Bundler::VERSION}")
299
- }
300
- ),
301
-
302
- SetupTask.new(
303
- 'Installing bundled gems', -> { tasks.remote_system('bundle install') }
304
- ),
305
-
306
- SetupTask.new(
307
- 'Installing Javascript packages', -> { tasks.remote_system('yarn install') }
308
- ),
309
-
310
- SetupTask.new(
311
- 'Creating database', -> { tasks.remote_system('bundle exec rake db:create') }
312
- ),
313
-
314
- SetupTask.new(
315
- 'Migrating database', -> { tasks.remote_system('bundle exec rake db:migrate') }
316
- )
317
- ]
318
- end
319
-
320
- sig { returns(Kubernetes::Spec) }
321
- def kubernetes
322
- environment.kubernetes
323
- end
324
-
325
- sig { returns(Docker::Spec) }
326
- def docker
327
- environment.docker
328
- end
329
-
330
- sig { returns T::Hash[Symbol, T.untyped] }
331
- def clis
332
- @clis ||= {
333
- kubectl: kubernetes.provider.kubernetes_cli,
334
- helm: kubernetes.provider.helm_cli,
335
- krane: kubernetes.provider.deployer,
336
- docker: docker.cli,
337
- kuby: Kuby.logger
338
- }
339
- end
340
-
341
- sig { returns(Tasks) }
342
- def tasks
343
- @tasks ||= Kuby::Tasks.new(environment)
344
- end
345
- end
346
- end
@@ -1,202 +0,0 @@
1
- # typed: strict
2
-
3
- module Kuby
4
- module Docker
5
- class WebserverDevPhase < Layer
6
- extend T::Sig
7
-
8
- DEFAULT_PORT = T.let(3000, Integer)
9
-
10
- sig { params(port: Integer).void }
11
- attr_writer :port
12
-
13
- sig { params(environment: Environment).void }
14
- def initialize(environment)
15
- super
16
-
17
- @port = T.let(@port, T.nilable(Integer))
18
- end
19
-
20
- sig { override.params(dockerfile: Dockerfile).void }
21
- def apply_to(dockerfile)
22
- dockerfile.expose(port)
23
- end
24
-
25
- sig { returns(Integer) }
26
- def port
27
- @port || DEFAULT_PORT
28
- end
29
- end
30
-
31
- class DevSpec
32
- extend T::Sig
33
-
34
- sig { returns(Environment) }
35
- attr_reader :environment
36
-
37
- sig { params(environment: Environment).void }
38
- def initialize(environment)
39
- @environment = environment
40
-
41
- @setup_phase = T.let(@setup_phase, T.nilable(SetupPhase))
42
- @package_phase = T.let(@package_phase, T.nilable(PackagePhase))
43
- @webserver_phase = T.let(@webserver_phase, T.nilable(WebserverDevPhase))
44
- @metadata = T.let(@metadata, T.nilable(Metadata))
45
- @distro_spec = T.let(@distro_spec, T.nilable(Distro))
46
- @cli = T.let(@cli, T.nilable(CLI))
47
- @tags = T.let(@tags, T.nilable(LocalTags))
48
- @layer_stack = T.let(@layer_stack, T.nilable(Docker::LayerStack))
49
- end
50
-
51
- sig { params(dir: String).void }
52
- def working_dir(dir)
53
- setup_phase.working_dir = dir
54
- end
55
-
56
- sig { params(env: String).void }
57
- def rails_env(env)
58
- setup_phase.rails_env = env
59
- end
60
-
61
- sig {
62
- params(
63
- package_name: Symbol,
64
- version: T.nilable(String)
65
- )
66
- .void
67
- }
68
- def package(package_name, version = nil)
69
- package_phase.add(package_name, version)
70
- end
71
-
72
- sig { params(distro_name: Symbol).void }
73
- def distro(distro_name)
74
- metadata.distro = distro_name
75
- @distro_spec = nil
76
- end
77
-
78
- sig { params(port: Integer).void }
79
- def port(port)
80
- webserver_phase.port = port
81
- end
82
-
83
- sig {
84
- params(
85
- name: Symbol,
86
- layer: T.nilable(Layer),
87
- block: T.nilable(T.proc.params(df: Dockerfile).void)
88
- )
89
- .void
90
- }
91
- def use(name, layer = nil, &block)
92
- layer_stack.use(name, layer, &block)
93
- end
94
-
95
- sig {
96
- params(
97
- name: Symbol,
98
- layer: T.nilable(T.any(Layer, T::Hash[Symbol, T.untyped])),
99
- options: T::Hash[Symbol, T.untyped],
100
- block: T.nilable(T.proc.params(df: Dockerfile).void)
101
- )
102
- .void
103
- }
104
- def insert(name, layer = nil, options = {}, &block)
105
- layer_stack.insert(name, layer, options, &block)
106
- end
107
-
108
- sig { params(name: Symbol).void }
109
- def delete(name)
110
- layer_stack.delete(name)
111
- end
112
-
113
- sig { params(name: Symbol).returns(T::Boolean) }
114
- def exists?(name)
115
- layer_stack.includes?(name)
116
- end
117
-
118
- sig {
119
- params(block: T.nilable(T.proc.void)).returns(Credentials)
120
- }
121
- def credentials(&block)
122
- raise 'Docker credentials are not supported in the development environment'
123
- end
124
-
125
- sig { returns(Dockerfile) }
126
- def to_dockerfile
127
- Dockerfile.new.tap do |df|
128
- layer_stack.each { |layer| layer.apply_to(df) }
129
- df.cmd("#{distro_spec.shell_exe} -c 'while test 1; do sleep 5; done'")
130
- end
131
- end
132
-
133
- sig { returns(SetupPhase) }
134
- def setup_phase
135
- @setup_phase ||= SetupPhase.new(environment)
136
- end
137
-
138
- sig { returns(PackagePhase) }
139
- def package_phase
140
- @package_phase ||= PackagePhase.new(environment)
141
- end
142
-
143
- sig { returns(WebserverDevPhase) }
144
- def webserver_phase
145
- @webserver_phase ||= WebserverDevPhase.new(environment)
146
- end
147
-
148
- sig { returns(Metadata) }
149
- def metadata
150
- @metadata ||= Metadata.new(environment)
151
- end
152
-
153
- sig { returns(Distro) }
154
- def distro_spec
155
- @distro_spec ||= if distro_klass = Kuby.distros[metadata.distro]
156
- distro_klass.new(self)
157
- else
158
- raise MissingDistroError, "distro '#{metadata.distro}' hasn't been registered"
159
- end
160
- end
161
-
162
- sig { returns(String) }
163
- def tag
164
- t = ENV.fetch('KUBY_DOCKER_TAG') do
165
- tags.latest_timestamp_tag
166
- end
167
-
168
- unless t
169
- raise MissingTagError, 'could not find latest timestamped tag'
170
- end
171
-
172
- t.to_s
173
- end
174
-
175
- sig { params(current_tag: String).returns(String) }
176
- def previous_tag(current_tag)
177
- raise MissingTagError, 'cannot roll back in the development environment'
178
- end
179
-
180
- sig { returns(CLI) }
181
- def cli
182
- @cli ||= Docker::CLI.new
183
- end
184
-
185
- private
186
-
187
- sig { returns(LocalTags) }
188
- def tags
189
- @tags ||= LocalTags.new(cli, metadata)
190
- end
191
-
192
- sig { returns(Docker::LayerStack) }
193
- def layer_stack
194
- @layer_stack ||= Docker::LayerStack.new.tap do |stack|
195
- stack.use(:setup_phase, setup_phase)
196
- stack.use(:package_phase, package_phase)
197
- stack.use(:webserver_phase, webserver_phase)
198
- end
199
- end
200
- end
201
- end
202
- end