kuby-core 0.7.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -0
  3. data/Gemfile +1 -0
  4. data/README.md +11 -1
  5. data/bin/kuby +4 -0
  6. data/kuby-core.gemspec +11 -4
  7. data/lib/kuby.rb +55 -23
  8. data/lib/kuby/basic_logger.rb +13 -0
  9. data/lib/kuby/cli_base.rb +81 -8
  10. data/lib/kuby/commands.rb +220 -0
  11. data/lib/kuby/definition.rb +1 -11
  12. data/lib/kuby/dev_setup.rb +255 -0
  13. data/lib/kuby/docker.rb +1 -0
  14. data/lib/kuby/docker/bundler_phase.rb +3 -3
  15. data/lib/kuby/docker/cli.rb +13 -1
  16. data/lib/kuby/docker/dev_spec.rb +131 -0
  17. data/lib/kuby/docker/dockerfile.rb +16 -1
  18. data/lib/kuby/docker/layer.rb +4 -4
  19. data/lib/kuby/docker/layer_stack.rb +4 -0
  20. data/lib/kuby/docker/local_tags.rb +4 -0
  21. data/lib/kuby/docker/metadata.rb +19 -39
  22. data/lib/kuby/docker/package_phase.rb +2 -2
  23. data/lib/kuby/docker/setup_phase.rb +3 -2
  24. data/lib/kuby/docker/spec.rb +42 -16
  25. data/lib/kuby/docker/timestamp_tag.rb +12 -3
  26. data/lib/kuby/environment.rb +15 -2
  27. data/lib/kuby/kubernetes.rb +9 -11
  28. data/lib/kuby/kubernetes/deploy_task.rb +4 -1
  29. data/lib/kuby/kubernetes/deployer.rb +67 -15
  30. data/lib/kuby/kubernetes/{minikube_provider.rb → docker_desktop_provider.rb} +8 -4
  31. data/lib/kuby/kubernetes/provider.rb +12 -8
  32. data/lib/kuby/kubernetes/spec.rb +30 -29
  33. data/lib/kuby/plugin.rb +59 -0
  34. data/lib/kuby/plugin_registry.rb +27 -0
  35. data/lib/kuby/plugins.rb +6 -0
  36. data/lib/kuby/plugins/nginx_ingress.rb +71 -0
  37. data/lib/kuby/plugins/rails_app.rb +18 -0
  38. data/lib/kuby/plugins/rails_app/asset_copy_task.rb +117 -0
  39. data/lib/kuby/plugins/rails_app/assets.rb +347 -0
  40. data/lib/kuby/plugins/rails_app/database.rb +75 -0
  41. data/lib/kuby/{kubernetes/plugins → plugins}/rails_app/generators/kuby.rb +3 -15
  42. data/lib/kuby/plugins/rails_app/mysql.rb +155 -0
  43. data/lib/kuby/plugins/rails_app/plugin.rb +588 -0
  44. data/lib/kuby/plugins/rails_app/postgres.rb +143 -0
  45. data/lib/kuby/plugins/rails_app/rewrite_db_config.rb +11 -0
  46. data/lib/kuby/plugins/rails_app/sqlite.rb +32 -0
  47. data/lib/kuby/plugins/rails_app/tasks.rake +36 -0
  48. data/lib/kuby/rails_commands.rb +89 -0
  49. data/lib/kuby/railtie.rb +0 -4
  50. data/lib/kuby/tasks.rb +84 -31
  51. data/lib/kuby/version.rb +1 -1
  52. data/spec/docker/metadata_spec.rb +84 -0
  53. data/spec/docker/spec_spec.rb +266 -0
  54. data/spec/docker/timestamp_tag_spec.rb +61 -0
  55. data/spec/dummy/Gemfile +54 -0
  56. data/spec/dummy/Gemfile.lock +223 -0
  57. data/spec/dummy/README.md +24 -0
  58. data/spec/dummy/Rakefile +6 -0
  59. data/spec/dummy/app/assets/config/manifest.js +2 -0
  60. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  61. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  62. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  63. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  64. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  65. data/spec/dummy/app/javascript/channels/consumer.js +6 -0
  66. data/spec/dummy/app/javascript/channels/index.js +5 -0
  67. data/spec/dummy/app/javascript/packs/application.js +17 -0
  68. data/spec/dummy/app/jobs/application_job.rb +7 -0
  69. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  70. data/spec/dummy/app/models/application_record.rb +3 -0
  71. data/spec/dummy/app/views/layouts/application.html.erb +15 -0
  72. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  73. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  74. data/spec/dummy/bin/bundle +114 -0
  75. data/spec/dummy/bin/rails +9 -0
  76. data/spec/dummy/bin/rake +9 -0
  77. data/spec/dummy/bin/setup +36 -0
  78. data/spec/dummy/bin/spring +17 -0
  79. data/spec/dummy/bin/yarn +11 -0
  80. data/spec/dummy/config.ru +5 -0
  81. data/spec/dummy/config/application.rb +19 -0
  82. data/spec/dummy/config/boot.rb +4 -0
  83. data/spec/dummy/config/cable.yml +10 -0
  84. data/spec/dummy/config/credentials.yml.enc +1 -0
  85. data/spec/dummy/config/database.yml +25 -0
  86. data/spec/dummy/config/environment.rb +5 -0
  87. data/spec/dummy/config/environments/development.rb +62 -0
  88. data/spec/dummy/config/environments/production.rb +112 -0
  89. data/spec/dummy/config/environments/test.rb +49 -0
  90. data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
  91. data/spec/dummy/config/initializers/assets.rb +14 -0
  92. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  93. data/spec/dummy/config/initializers/content_security_policy.rb +30 -0
  94. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  95. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  96. data/spec/dummy/config/initializers/inflections.rb +16 -0
  97. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  98. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  99. data/spec/dummy/config/locales/en.yml +33 -0
  100. data/spec/dummy/config/master.key +1 -0
  101. data/spec/dummy/config/puma.rb +38 -0
  102. data/spec/dummy/config/routes.rb +3 -0
  103. data/spec/dummy/config/spring.rb +6 -0
  104. data/spec/dummy/config/storage.yml +34 -0
  105. data/spec/dummy/db/seeds.rb +7 -0
  106. data/spec/dummy/package.json +11 -0
  107. data/spec/dummy/public/404.html +67 -0
  108. data/spec/dummy/public/422.html +67 -0
  109. data/spec/dummy/public/500.html +66 -0
  110. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  111. data/spec/dummy/public/apple-touch-icon.png +0 -0
  112. data/spec/dummy/public/favicon.ico +0 -0
  113. data/spec/dummy/public/robots.txt +1 -0
  114. data/spec/dummy/test/application_system_test_case.rb +5 -0
  115. data/spec/dummy/test/channels/application_cable/connection_test.rb +11 -0
  116. data/spec/dummy/test/test_helper.rb +13 -0
  117. data/spec/dummy/tmp/cache/bootsnap-load-path-cache +0 -0
  118. data/spec/spec_helper.rb +177 -0
  119. data/spec/support/docker/fake_cli.rb +54 -0
  120. data/spec/support/docker/remote/fake_client.rb +16 -0
  121. data/spec/trailing_hash_spec.rb +23 -0
  122. metadata +155 -30
  123. data/lib/ext/krane/kubernetes_resource.rb +0 -16
  124. data/lib/kuby/kubernetes/plugin.rb +0 -55
  125. data/lib/kuby/kubernetes/plugins.rb +0 -8
  126. data/lib/kuby/kubernetes/plugins/nginx_ingress.rb +0 -73
  127. data/lib/kuby/kubernetes/plugins/rails_app.rb +0 -16
  128. data/lib/kuby/kubernetes/plugins/rails_app/database.rb +0 -79
  129. data/lib/kuby/kubernetes/plugins/rails_app/mysql.rb +0 -154
  130. data/lib/kuby/kubernetes/plugins/rails_app/plugin.rb +0 -379
  131. data/lib/kuby/kubernetes/plugins/rails_app/postgres.rb +0 -142
  132. data/lib/kuby/kubernetes/plugins/rails_app/rewrite_db_config.rb +0 -13
  133. data/lib/kuby/kubernetes/plugins/rails_app/sqlite.rb +0 -30
  134. data/lib/kuby/kubernetes/plugins/rails_app/tasks.rake +0 -28
  135. data/lib/kuby/tasks/kuby.rake +0 -70
@@ -0,0 +1,18 @@
1
+ module Kuby
2
+ module Plugins
3
+ module RailsApp
4
+ autoload :AssetCopyTask, 'kuby/plugins/rails_app/asset_copy_task'
5
+ autoload :Assets, 'kuby/plugins/rails_app/assets'
6
+ autoload :Database, 'kuby/plugins/rails_app/database'
7
+ autoload :MySQL, 'kuby/plugins/rails_app/mysql'
8
+ autoload :Plugin, 'kuby/plugins/rails_app/plugin'
9
+ autoload :Postgres, 'kuby/plugins/rails_app/postgres'
10
+ autoload :RewriteDbConfig, 'kuby/plugins/rails_app/rewrite_db_config'
11
+ autoload :Sqlite, 'kuby/plugins/rails_app/sqlite'
12
+ end
13
+ end
14
+ end
15
+
16
+ Kuby.register_plugin(:rails_assets, Kuby::Plugins::RailsApp::Assets)
17
+
18
+ load File.expand_path(File.join('rails_app', 'tasks.rake'), __dir__)
@@ -0,0 +1,117 @@
1
+ require 'fileutils'
2
+ require 'pathname'
3
+
4
+ module Kuby
5
+ module Plugins
6
+ module RailsApp
7
+ # Works by maintaining a directory structure where each deploy creates
8
+ # a new directory. Each directory is given a timestamped name. Assets
9
+ # are symlinked into a special 'current' directory from each of the
10
+ # timestamped directories. Each invocation overrides existing symlinks
11
+ # if they already exist. This technique ensures assets from the previous
12
+ # deploy remain available while the web servers are restarting.
13
+ class AssetCopyTask
14
+ TIMESTAMP_FORMAT = '%Y%m%d%H%M%S'.freeze
15
+ KEEP = 5
16
+
17
+ attr_reader :dest_path, :source_path
18
+
19
+ def initialize(to:, from:)
20
+ @dest_path = to
21
+ @source_path = from
22
+ end
23
+
24
+ def run
25
+ FileUtils.mkdir_p(ts_dir)
26
+ FileUtils.mkdir_p(current_dir)
27
+
28
+ copy_new_assets
29
+ delete_old_assets
30
+
31
+ nil
32
+ end
33
+
34
+ private
35
+
36
+ def copy_new_assets
37
+ # Copy all assets to new timestamp directory
38
+ #
39
+ # "source_path/." is special syntax. From the Ruby docs:
40
+ # cp_r('src', 'dest') makes dest/src, but cp_r('src/.', 'dest') doesn't
41
+ FileUtils.cp_r(File.join(source_path, '.'), ts_dir)
42
+
43
+ relative_source_files = Dir.chdir(ts_dir) do
44
+ Dir.glob(File.join('**', '*'))
45
+ end
46
+
47
+ relative_source_files.each do |relative_source_file|
48
+ source_file = Pathname(File.join(current_dir, relative_source_file))
49
+ source_ts_file = Pathname(File.join(ts_dir, relative_source_file))
50
+ next unless File.file?(source_ts_file)
51
+
52
+ # create individual symlinks for each file in source dir
53
+ target_file = File.join(current_dir, relative_source_file)
54
+ FileUtils.mkdir_p(File.dirname(target_file))
55
+ source_ln_file = source_ts_file.relative_path_from(source_file.dirname)
56
+ FileUtils.ln_s(source_ln_file, target_file, force: true)
57
+ Kuby.logger.info("Linked #{source_ln_file} -> #{target_file}")
58
+ end
59
+ end
60
+
61
+ def delete_old_assets
62
+ # find all asset directories; directories have timestamp names
63
+ asset_dirs = (Dir.glob(File.join(dest_path, '*')) - [current_dir])
64
+ .select { |dir| File.directory?(dir) && try_parse_ts(File.basename(dir)) }
65
+ .sort_by { |dir| parse_ts(File.basename(dir)) }
66
+ .reverse
67
+
68
+ # only keep the n most recent directories
69
+ dirs_to_delete = asset_dirs[KEEP..-1] || []
70
+
71
+ dirs_to_delete.each do |dir_to_delete|
72
+ relative_files_to_delete = Dir.chdir(dir_to_delete) do
73
+ Dir.glob(File.join('**', '*'))
74
+ end
75
+
76
+ relative_files_to_delete.each do |relative_file_to_delete|
77
+ file_to_delete = File.join(dir_to_delete, relative_file_to_delete)
78
+ next unless File.file?(file_to_delete)
79
+
80
+ link = File.join(current_dir, relative_file_to_delete)
81
+ next unless File.symlink?(link)
82
+
83
+ # Only remove a symlink if it still points to a resource
84
+ # in the directory we're currently deleting. Othewise, leave
85
+ # it there - it was added by another deploy.
86
+ if File.readlink(link) == file_to_delete
87
+ File.unlink(link)
88
+ end
89
+ end
90
+
91
+ FileUtils.rm_r(dir_to_delete)
92
+ end
93
+ end
94
+
95
+ def try_parse_ts(ts)
96
+ parse_ts(ts)
97
+ rescue ArgumentError
98
+ return nil
99
+ end
100
+
101
+ def parse_ts(ts)
102
+ Time.strptime(ts, TIMESTAMP_FORMAT)
103
+ end
104
+
105
+ def ts_dir
106
+ @ts_dir ||= File.join(
107
+ dest_path, Time.now.strftime(TIMESTAMP_FORMAT)
108
+ )
109
+ end
110
+
111
+ def current_dir
112
+ @current_dir ||= File.join(dest_path, 'current')
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,347 @@
1
+ require 'kube-dsl'
2
+
3
+ module Kuby
4
+ module Plugins
5
+ module RailsApp
6
+ class Assets < ::Kuby::Plugin
7
+ extend ::KubeDSL::ValueFields
8
+
9
+ ROLE = 'assets'.freeze
10
+ NGINX_IMAGE = 'nginx:1.9-alpine'.freeze
11
+ NGINX_PORT = 8082
12
+ NGINX_MOUNT_PATH = '/usr/share/nginx/assets'.freeze
13
+ RAILS_MOUNT_PATH = '/usr/share/assets'.freeze
14
+
15
+ value_fields :asset_url, :packs_url, :asset_path
16
+
17
+ def configure(&block)
18
+ instance_eval(&block)
19
+ end
20
+
21
+ def configure_ingress(ingress, hostname)
22
+ spec = self
23
+
24
+ ingress.spec.rule do
25
+ host hostname
26
+
27
+ http do
28
+ path do
29
+ path spec.asset_url
30
+
31
+ backend do
32
+ service_name spec.service.metadata.name
33
+ service_port spec.service.spec.ports.first.port
34
+ end
35
+ end
36
+
37
+ path do
38
+ path spec.packs_url
39
+
40
+ backend do
41
+ service_name spec.service.metadata.name
42
+ service_port spec.service.spec.ports.first.port
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ def configure_deployment(deployment, docker_image)
50
+ spec = self
51
+
52
+ deployment.spec.template.spec do
53
+ init_container(:copy_assets) do
54
+ name "#{spec.selector_app}-copy-assets"
55
+ command %w(bundle exec rake kuby:rails_app:assets:copy)
56
+ image docker_image
57
+
58
+ volume_mount do
59
+ name 'assets'
60
+ mount_path RAILS_MOUNT_PATH
61
+ end
62
+ end
63
+
64
+ container(:web) do
65
+ volume_mount do
66
+ name 'assets'
67
+ mount_path NGINX_MOUNT_PATH
68
+ end
69
+ end
70
+
71
+ volume do
72
+ name 'assets'
73
+
74
+ persistent_volume_claim do
75
+ claim_name spec.volume_claim.metadata.name
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ def copy_task
82
+ @copy_task ||= AssetCopyTask.new(
83
+ from: asset_path, to: RAILS_MOUNT_PATH
84
+ )
85
+ end
86
+
87
+ def service(&block)
88
+ spec = self
89
+
90
+ @service ||= KubeDSL.service do
91
+ metadata do
92
+ name "#{spec.selector_app}-#{spec.role}-svc"
93
+ namespace spec.namespace.metadata.name
94
+
95
+ labels do
96
+ add :app, spec.selector_app
97
+ add :role, spec.role
98
+ end
99
+ end
100
+
101
+ spec do
102
+ type 'NodePort'
103
+
104
+ selector do
105
+ add :app, spec.selector_app
106
+ add :role, spec.role
107
+ end
108
+
109
+ port do
110
+ name 'http'
111
+ port NGINX_PORT
112
+ protocol 'TCP'
113
+ target_port 'http'
114
+ end
115
+ end
116
+ end
117
+
118
+ @service.instance_eval(&block) if block
119
+ @service
120
+ end
121
+
122
+ def service_account(&block)
123
+ spec = self
124
+
125
+ @service_account ||= KubeDSL.service_account do
126
+ metadata do
127
+ name "#{spec.selector_app}-#{spec.role}-sa"
128
+ namespace spec.namespace.metadata.name
129
+
130
+ labels do
131
+ add :app, spec.selector_app
132
+ add :role, spec.role
133
+ end
134
+ end
135
+ end
136
+
137
+ @service_account.instance_eval(&block) if block
138
+ @service_account
139
+ end
140
+
141
+ def nginx_config(&block)
142
+ spec = self
143
+
144
+ @nginx_config ||= KubeDSL.config_map do
145
+ metadata do
146
+ name "#{spec.selector_app}-#{spec.role}-nginx-config"
147
+ namespace spec.namespace.metadata.name
148
+ end
149
+
150
+ data do
151
+ add 'nginx.conf', <<~END
152
+ user nginx;
153
+ worker_processes 1;
154
+
155
+ error_log /var/log/nginx/error.log warn;
156
+ pid /var/run/nginx.pid;
157
+
158
+ events {
159
+ worker_connections 1024;
160
+ }
161
+
162
+ http {
163
+ include /etc/nginx/mime.types;
164
+ default_type application/octet-stream;
165
+
166
+ log_format main '$remote_addr - $remote_user [$time_local] "$request" '
167
+ '$status $body_bytes_sent "$http_referer" '
168
+ '"$http_user_agent" "$http_x_forwarded_for"';
169
+
170
+ access_log /var/log/nginx/access.log main;
171
+
172
+ sendfile on;
173
+ keepalive_timeout 65;
174
+ gzip on;
175
+
176
+ server {
177
+ listen #{NGINX_PORT};
178
+ server_name localhost;
179
+
180
+ location / {
181
+ root #{File.join(NGINX_MOUNT_PATH, 'current')};
182
+ }
183
+
184
+ error_page 500 502 503 504 /500.html;
185
+ }
186
+ }
187
+ END
188
+ end
189
+ end
190
+
191
+ @nginx_config.instance_eval(&block) if block
192
+ @nginx_config
193
+ end
194
+
195
+ def deployment(&block)
196
+ kube_spec = self
197
+
198
+ @deployment ||= KubeDSL.deployment do
199
+ metadata do
200
+ name "#{kube_spec.selector_app}-#{kube_spec.role}"
201
+ namespace kube_spec.namespace.metadata.name
202
+
203
+ labels do
204
+ add :app, kube_spec.selector_app
205
+ add :role, kube_spec.role
206
+ end
207
+ end
208
+
209
+ spec do
210
+ replicas 1
211
+
212
+ selector do
213
+ match_labels do
214
+ add :app, kube_spec.selector_app
215
+ add :role, kube_spec.role
216
+ end
217
+ end
218
+
219
+ strategy do
220
+ type 'RollingUpdate'
221
+
222
+ rolling_update do
223
+ max_surge '25%'
224
+ max_unavailable 0
225
+ end
226
+ end
227
+
228
+ template do
229
+ metadata do
230
+ labels do
231
+ add :app, kube_spec.selector_app
232
+ add :role, kube_spec.role
233
+ end
234
+ end
235
+
236
+ spec do
237
+ container(:nginx) do
238
+ name "#{kube_spec.selector_app}-#{kube_spec.role}"
239
+ image_pull_policy 'IfNotPresent'
240
+ image NGINX_IMAGE
241
+
242
+ port do
243
+ container_port NGINX_PORT
244
+ name 'http'
245
+ protocol 'TCP'
246
+ end
247
+
248
+ volume_mount do
249
+ name 'nginx-config'
250
+ mount_path '/etc/nginx/nginx.conf'
251
+ sub_path 'nginx.conf'
252
+ end
253
+
254
+ volume_mount do
255
+ name 'assets'
256
+ mount_path NGINX_MOUNT_PATH
257
+ end
258
+
259
+ readiness_probe do
260
+ success_threshold 1
261
+ failure_threshold 2
262
+ initial_delay_seconds 5
263
+ period_seconds 3
264
+ timeout_seconds 1
265
+
266
+ http_get do
267
+ path '/500.html'
268
+ port NGINX_PORT
269
+ scheme 'HTTP'
270
+ end
271
+ end
272
+ end
273
+
274
+ volume do
275
+ name 'nginx-config'
276
+
277
+ config_map do
278
+ name kube_spec.nginx_config.metadata.name
279
+ end
280
+ end
281
+
282
+ volume do
283
+ name 'assets'
284
+
285
+ persistent_volume_claim do
286
+ claim_name kube_spec.volume_claim.metadata.name
287
+ end
288
+ end
289
+
290
+ restart_policy 'Always'
291
+ service_account_name kube_spec.service_account.metadata.name
292
+ end
293
+ end
294
+ end
295
+ end
296
+
297
+ @deployment.instance_eval(&block) if block
298
+ @deployment
299
+ end
300
+
301
+ def volume_claim
302
+ spec = self
303
+
304
+ @volume_claim ||= KubeDSL.persistent_volume_claim do
305
+ metadata do
306
+ name "#{spec.selector_app}-#{spec.role}"
307
+ namespace spec.namespace.metadata.name
308
+ end
309
+
310
+ spec do
311
+ access_modes ['ReadWriteOnce']
312
+ storage_class_name spec.environment.kubernetes.provider.storage_class_name
313
+
314
+ resources do
315
+ requests do
316
+ add :storage, '10Gi'
317
+ end
318
+ end
319
+ end
320
+ end
321
+ end
322
+
323
+ def resources
324
+ @resources ||= [
325
+ service,
326
+ service_account,
327
+ nginx_config,
328
+ deployment,
329
+ volume_claim
330
+ ]
331
+ end
332
+
333
+ def namespace
334
+ environment.kubernetes.namespace
335
+ end
336
+
337
+ def selector_app
338
+ environment.kubernetes.selector_app
339
+ end
340
+
341
+ def role
342
+ ROLE
343
+ end
344
+ end
345
+ end
346
+ end
347
+ end