kuby-core 0.8.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/Gemfile +1 -0
  4. data/lib/kuby.rb +2 -0
  5. data/lib/kuby/docker/metadata.rb +14 -12
  6. data/lib/kuby/docker/timestamp_tag.rb +6 -0
  7. data/lib/kuby/plugins/rails_app/tasks.rake +2 -2
  8. data/lib/kuby/version.rb +1 -1
  9. data/spec/docker/metadata_spec.rb +192 -0
  10. data/spec/docker/timestamp_tag_spec.rb +54 -4
  11. data/spec/dummy/Gemfile +54 -0
  12. data/spec/dummy/Gemfile.lock +223 -0
  13. data/spec/dummy/README.md +24 -0
  14. data/spec/dummy/Rakefile +6 -0
  15. data/spec/dummy/app/assets/config/manifest.js +2 -0
  16. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  17. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  18. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  19. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  20. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  21. data/spec/dummy/app/javascript/channels/consumer.js +6 -0
  22. data/spec/dummy/app/javascript/channels/index.js +5 -0
  23. data/spec/dummy/app/javascript/packs/application.js +17 -0
  24. data/spec/dummy/app/jobs/application_job.rb +7 -0
  25. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  26. data/spec/dummy/app/models/application_record.rb +3 -0
  27. data/spec/dummy/app/views/layouts/application.html.erb +15 -0
  28. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  29. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  30. data/spec/dummy/bin/bundle +114 -0
  31. data/spec/dummy/bin/rails +9 -0
  32. data/spec/dummy/bin/rake +9 -0
  33. data/spec/dummy/bin/setup +36 -0
  34. data/spec/dummy/bin/spring +17 -0
  35. data/spec/dummy/bin/yarn +11 -0
  36. data/spec/dummy/config.ru +5 -0
  37. data/spec/dummy/config/application.rb +19 -0
  38. data/spec/dummy/config/boot.rb +4 -0
  39. data/spec/dummy/config/cable.yml +10 -0
  40. data/spec/dummy/config/credentials.yml.enc +1 -0
  41. data/spec/dummy/config/database.yml +25 -0
  42. data/spec/dummy/config/environment.rb +5 -0
  43. data/spec/dummy/config/environments/development.rb +62 -0
  44. data/spec/dummy/config/environments/production.rb +112 -0
  45. data/spec/dummy/config/environments/test.rb +49 -0
  46. data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
  47. data/spec/dummy/config/initializers/assets.rb +14 -0
  48. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  49. data/spec/dummy/config/initializers/content_security_policy.rb +30 -0
  50. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  51. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  52. data/spec/dummy/config/initializers/inflections.rb +16 -0
  53. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  54. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  55. data/spec/dummy/config/locales/en.yml +33 -0
  56. data/spec/dummy/config/master.key +1 -0
  57. data/spec/dummy/config/puma.rb +38 -0
  58. data/spec/dummy/config/routes.rb +3 -0
  59. data/spec/dummy/config/spring.rb +6 -0
  60. data/spec/dummy/config/storage.yml +34 -0
  61. data/spec/dummy/db/seeds.rb +7 -0
  62. data/spec/dummy/package.json +11 -0
  63. data/spec/dummy/public/404.html +67 -0
  64. data/spec/dummy/public/422.html +67 -0
  65. data/spec/dummy/public/500.html +66 -0
  66. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  67. data/spec/dummy/public/apple-touch-icon.png +0 -0
  68. data/spec/dummy/public/favicon.ico +0 -0
  69. data/spec/dummy/public/robots.txt +1 -0
  70. data/spec/dummy/test/application_system_test_case.rb +5 -0
  71. data/spec/dummy/test/channels/application_cable/connection_test.rb +11 -0
  72. data/spec/dummy/test/test_helper.rb +13 -0
  73. data/spec/dummy/tmp/cache/bootsnap-load-path-cache +0 -0
  74. data/spec/spec_helper.rb +70 -2
  75. data/spec/support/docker/fake_cli.rb +54 -0
  76. data/spec/support/docker/remote/fake_client.rb +16 -0
  77. data/spec/trailing_hash_spec.rb +23 -0
  78. metadata +69 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ac73b245f1fb75244ed166e23a6441cc56dabaec66f532f67517b4dd8d412091
4
- data.tar.gz: 6418a3fbca89fdc195e277aeb8309caa2b519e1d3df0fccd53f3ae8b2e9f166f
3
+ metadata.gz: a0f3610dd2dc149fb09e589c56dc477534ece66a752106b251a94ac265cb1e34
4
+ data.tar.gz: 673285ecde402c63a461ef4c4374a1353be45ff33610db840590658cc6cab5ed
5
5
  SHA512:
6
- metadata.gz: 6f7bc8616cfdf23805bf9177198f62888b17372ae67b4fb4a77c4d325adbbcf6d734385edb0614094be95bb443d84b28f6d79fe70a8acaecdfb44b547bcf86b0
7
- data.tar.gz: 815fd1ae302c35fdd0fb31f43bb1d607073a9cfb1f24d3c849578b0b8a5271bca5cbe49f2747bdf7bb22d1a4b98d1fa455c3a917e7d27f059f5a4778da215f87
6
+ metadata.gz: 6c8e407acf3a35f21b4446b0c0752a754720a7fbd03b887b64c4fe0991e61e6f107f61a2adb2394ac88d24470be5c814c6168cd96106295c777f50a35f77820a
7
+ data.tar.gz: 1871572ccb876d772eec874ed224235905ba7265ced27319f4e7abd678a65e1862584bd88f0223e2f87ba1b99158342e6c6a2c4d033d758b28338296153f5e4b
@@ -1,3 +1,11 @@
1
+ ## 0.8.1
2
+ * Fix database config rewriter task.
3
+ - Broke with refactoring of database config code.
4
+ * More correctly parse Docker image URLs.
5
+ - It can be challenging to identify the hostname in image URLs because 1) the host can be omitted, and 2) the scheme is often omitted.
6
+ - The new strategy is to look for a "." in the first segment of the URL if there is no scheme. It's not bulletproof but is better than what we had before, which was to assume the first segment was the host. Eg. for an image URL like camertron/foo, we would identify the host as "camertron."
7
+ * Added a number of tests and a Rails dummy app in spec/.
8
+
1
9
  ## 0.8.0
2
10
  * Upgrade to Krane >= 1.1.4, < 2.0.
3
11
  * Remove Krane monkeypatch in ext/.
data/Gemfile CHANGED
@@ -9,4 +9,5 @@ end
9
9
 
10
10
  group :test do
11
11
  gem 'rspec', '~> 3.0'
12
+ gem 'timecop', '~> 0.9'
12
13
  end
@@ -37,6 +37,8 @@ module Kuby
37
37
  @definition.environments.each do |_, env|
38
38
  env.kubernetes.after_configuration
39
39
  end
40
+
41
+ @definition
40
42
  end
41
43
 
42
44
  def environment(name = env)
@@ -5,6 +5,7 @@ module Kuby
5
5
  class Metadata
6
6
  DEFAULT_DISTRO = :debian
7
7
  DEFAULT_REGISTRY_HOST = 'https://www.docker.com'.freeze
8
+ DEFAULT_REGISTRY_SCHEME = 'https'
8
9
  LATEST_TAG = 'latest'
9
10
 
10
11
  attr_accessor :image_url
@@ -20,12 +21,7 @@ module Kuby
20
21
  end
21
22
 
22
23
  def image_host
23
- @image_host ||= if image_url.include?('/')
24
- uri = parse_url(image_url)
25
- "#{uri.scheme}://#{uri.host}"
26
- else
27
- DEFAULT_REGISTRY_HOST
28
- end
24
+ @image_host ||= "#{full_image_uri.scheme}://#{full_image_uri.host}"
29
25
  end
30
26
 
31
27
  def image_hostname
@@ -33,11 +29,7 @@ module Kuby
33
29
  end
34
30
 
35
31
  def image_repo
36
- @image_repo ||= if image_url.include?('/')
37
- parse_url(image_url).path.sub(/\A\//, '')
38
- else
39
- image_url
40
- end
32
+ @image_repo ||= full_image_uri.path.sub(/\A[\/]+/, '')
41
33
  end
42
34
 
43
35
  def tags
@@ -76,6 +68,16 @@ module Kuby
76
68
 
77
69
  private
78
70
 
71
+ def full_image_uri
72
+ @full_image_uri ||= if image_url.include?('://')
73
+ URI.parse(image_url)
74
+ elsif image_url =~ /\A[^.]+\.[^\/]+\//
75
+ URI.parse("#{DEFAULT_REGISTRY_SCHEME}://#{image_url}")
76
+ else
77
+ URI.parse("#{DEFAULT_REGISTRY_HOST}/#{image_url.sub(/\A[\/]+/, '')}")
78
+ end
79
+ end
80
+
79
81
  def default_image_url
80
82
  # assuming dockerhub by not specifying full url
81
83
  @default_image_url ||= environment.app_name.downcase
@@ -92,7 +94,7 @@ module Kuby
92
94
  return uri if uri.scheme
93
95
 
94
96
  # force a scheme because URI.parse won't work properly without one
95
- URI.parse("https://#{url}")
97
+ URI.parse("#{DEFAULT_REGISTRY_SCHEME}://#{url}")
96
98
  end
97
99
  end
98
100
  end
@@ -1,3 +1,5 @@
1
+ require 'time'
2
+
1
3
  module Kuby
2
4
  module Docker
3
5
  class TimestampTag
@@ -27,6 +29,10 @@ module Kuby
27
29
  time <=> other.time
28
30
  end
29
31
 
32
+ def ==(other)
33
+ time == other.time
34
+ end
35
+
30
36
  def hash
31
37
  time.hash
32
38
  end
@@ -9,8 +9,8 @@ namespace :kuby do
9
9
  config_file = File.join(Kuby.environment.kubernetes.plugin(:rails_app).root, 'config', 'database.yml')
10
10
  database = Kuby.environment.kubernetes.plugin(:rails_app).database
11
11
 
12
- if database.respond_to?(:rewritten_configs)
13
- File.write(config_file, YAML.dump(database.rewritten_configs))
12
+ if database.plugin.respond_to?(:rewritten_configs)
13
+ File.write(config_file, YAML.dump(database.plugin.rewritten_configs))
14
14
  Kuby.logger.info("Wrote #{config_file}")
15
15
  end
16
16
  end
@@ -1,3 +1,3 @@
1
1
  module Kuby
2
- VERSION = '0.8.0'
2
+ VERSION = '0.8.1'
3
3
  end
@@ -0,0 +1,192 @@
1
+ require 'spec_helper'
2
+ require 'timecop'
3
+
4
+ describe Kuby::Docker::Metadata do
5
+ let(:metadata) { definition.environment.docker.metadata }
6
+
7
+ describe '#image_url' do
8
+ subject { metadata.image_url }
9
+
10
+ it { is_expected.to eq(docker_image_url) }
11
+
12
+ context 'when no image URL is configured' do
13
+ let(:docker_image_url) { nil }
14
+ it { is_expected.to eq(definition.app_name) }
15
+ end
16
+ end
17
+
18
+ describe '#image_host' do
19
+ subject { metadata.image_host }
20
+
21
+ it { is_expected.to eq(described_class::DEFAULT_REGISTRY_HOST) }
22
+
23
+ context 'when the image URL contains an explicit host' do
24
+ let(:docker_image_url) { 'registry.foo.com/foo/testapp' }
25
+
26
+ it { is_expected.to eq('https://registry.foo.com') }
27
+ end
28
+
29
+ context 'when the image URL contains an explicit host with scheme' do
30
+ let(:docker_image_url) { 'http://registry.foo.com/foo/testapp' }
31
+
32
+ it { is_expected.to eq('http://registry.foo.com') }
33
+ end
34
+ end
35
+
36
+ describe '#image_repo' do
37
+ subject { metadata.image_repo }
38
+
39
+ it { is_expected.to eq('foo/testapp') }
40
+
41
+ context 'when the image URL contains an explicit host' do
42
+ let(:docker_image_url) { 'registry.foo.com/foo/testapp' }
43
+
44
+ it { is_expected.to eq('foo/testapp') }
45
+ end
46
+ end
47
+
48
+ describe '#image_hostname' do
49
+ subject { metadata.image_hostname }
50
+
51
+ it { is_expected.to eq('www.docker.com') }
52
+
53
+ context 'when the image URL contains an explicit host' do
54
+ let(:docker_image_url) { 'registry.foo.com/foo/testapp' }
55
+
56
+ it { is_expected.to eq('registry.foo.com') }
57
+ end
58
+ end
59
+
60
+ describe '#tags' do
61
+ subject { metadata.tags }
62
+
63
+ it 'specifies the current timestamp tag and the default tag' do
64
+ Timecop.freeze do
65
+ expect(subject).to eq([
66
+ Time.now.strftime('%Y%m%d%H%M%S'),
67
+ Kuby::Docker::Metadata::LATEST_TAG
68
+ ])
69
+ end
70
+ end
71
+ end
72
+
73
+ describe '#tag' do
74
+ let(:tag) { make_ts_tag(Time.now) }
75
+
76
+ subject { metadata.tag }
77
+
78
+ context 'with no local or remote tags' do
79
+ it 'raises an error' do
80
+ expect { subject }.to raise_error(Kuby::Docker::MissingTagError)
81
+ end
82
+ end
83
+
84
+ context 'with an available remote tag' do
85
+ before { docker_remote_client.tags << tag }
86
+
87
+ it { is_expected.to eq(tag) }
88
+ end
89
+
90
+ context 'with an available local tag' do
91
+ before do
92
+ docker_cli.build(
93
+ dockerfile: nil,
94
+ image_url: docker_image_url,
95
+ tags: [tag]
96
+ )
97
+ end
98
+
99
+ it { is_expected.to eq(tag) }
100
+ end
101
+
102
+ context 'with multiple remote tags' do
103
+ let(:time) { Time.now }
104
+
105
+ before do
106
+ docker_remote_client.tags +=
107
+ [time - 5, time + 10, time - 10, time + 15].map do |t|
108
+ make_ts_tag(t)
109
+ end
110
+ end
111
+
112
+ it { is_expected.to eq(make_ts_tag(time + 15)) }
113
+ end
114
+
115
+ context 'with multiple local and remote tags' do
116
+ let(:time) { Time.now }
117
+
118
+ before do
119
+ docker_remote_client.tags +=
120
+ [time - 5, time + 10, time - 10, time + 15].map do |t|
121
+ make_ts_tag(t)
122
+ end
123
+
124
+ docker_cli.build(
125
+ dockerfile: nil,
126
+ image_url: docker_image_url,
127
+ tags: [time - 3, time + 6, time - 6, time + 18].map do |t|
128
+ make_ts_tag(t)
129
+ end
130
+ )
131
+ end
132
+
133
+ it { is_expected.to eq(make_ts_tag(time + 18)) }
134
+ end
135
+ end
136
+
137
+ describe '#previous_tag' do
138
+ let(:time) { Time.now }
139
+ let(:current_tag) { make_ts_tag(time) }
140
+
141
+ before do
142
+ docker_remote_client.tags << current_tag
143
+ docker_cli.build(
144
+ dockerfile: nil,
145
+ image_url: docker_image_url,
146
+ tags: [current_tag]
147
+ )
148
+ end
149
+
150
+ subject { metadata.previous_tag(current_tag) }
151
+
152
+ context 'with no previous local or remote tag' do
153
+ it 'raises an error' do
154
+ expect { subject }.to raise_error(Kuby::Docker::MissingTagError)
155
+ end
156
+ end
157
+
158
+ context 'with an available previous remote tag' do
159
+ let(:previous_tag) { make_ts_tag(time - 5) }
160
+
161
+ before { docker_remote_client.tags << previous_tag }
162
+
163
+ it { is_expected.to eq(previous_tag) }
164
+ end
165
+
166
+ context 'with an available previous local tag' do
167
+ let(:previous_tag) { make_ts_tag(time - 5) }
168
+
169
+ before do
170
+ docker_cli.build(
171
+ dockerfile: nil,
172
+ image_url: docker_image_url,
173
+ tags: [previous_tag]
174
+ )
175
+ end
176
+
177
+ it { is_expected.to eq(previous_tag) }
178
+ end
179
+ end
180
+
181
+ describe '#distro' do
182
+ subject { metadata.distro }
183
+
184
+ it { is_expected.to eq(Kuby::Docker::Metadata::DEFAULT_DISTRO) }
185
+
186
+ context 'with a distro set manually' do
187
+ before { metadata.distro = :alpine }
188
+
189
+ it { is_expected.to eq(:alpine) }
190
+ end
191
+ end
192
+ end
@@ -1,11 +1,61 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Kuby::Docker::TimestampTag do
4
- context ".try_parse" do
5
- it "returns a new timestamp tag" do
6
- tag = described_class.try_parse("20200810165134")
4
+ context '.try_parse' do
5
+ let(:tag_str) { '20200810165134' }
7
6
 
8
- expect(tag).to be_kind_of(described_class)
7
+ it 'creates a new timestamp tag' do
8
+ tag = described_class.try_parse(tag_str)
9
+ expect(tag).to be_a(described_class)
10
+ end
11
+
12
+ it 'correctly parses the timestamp contained in the tag' do
13
+ time = described_class.try_parse(tag_str).time
14
+ expect([time.year, time.month, time.day, time.hour, time.min, time.sec]).to(
15
+ eq([2020, 8, 10, 16, 51, 34])
16
+ )
17
+ end
18
+
19
+ context 'with an invalid tag' do
20
+ let(:tag_str) { 'abc123' }
21
+
22
+ it 'returns nil' do
23
+ expect(described_class.try_parse(tag_str)).to eq(nil)
24
+ end
25
+ end
26
+ end
27
+
28
+ context '#to_s' do
29
+ it 'serializes the tag as a timestamp' do
30
+ tag = described_class.new(Time.new(2020, 8, 10, 16, 51, 34))
31
+ expect(tag.to_s).to eq('20200810165134')
32
+ end
33
+ end
34
+
35
+ context 'comparison' do
36
+ it 'ensures tags can be compared by their timestamp values' do
37
+ seed_time = Time.now
38
+ times = [seed_time, seed_time + 5, seed_time + 10, seed_time + 15].shuffle
39
+ tags = times.map { |t| described_class.new(t) }
40
+ expect(tags.sort.map(&:time)).to eq(times.sort)
41
+ end
42
+ end
43
+
44
+ context 'equality' do
45
+ it 'ensures tags with equal times are considered equal' do
46
+ time = Time.now
47
+ tag1 = described_class.new(time)
48
+ tag2 = described_class.new(time)
49
+ expect(tag1).to eq(tag2)
50
+ expect(tag1.hash).to eq(tag2.hash)
51
+ end
52
+
53
+ it 'ensures tags with inequal times are not considered equal' do
54
+ time = Time.now
55
+ tag1 = described_class.new(time)
56
+ tag2 = described_class.new(time + 5)
57
+ expect(tag1).to_not eq(tag2)
58
+ expect(tag1.hash).to_not eq(tag2.hash)
9
59
  end
10
60
  end
11
61
  end
@@ -0,0 +1,54 @@
1
+ source 'https://rubygems.org'
2
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
+
4
+ ruby '2.5.8'
5
+
6
+ # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
7
+ gem 'rails', '~> 6.0.3', '>= 6.0.3.2'
8
+ # Use sqlite3 as the database for Active Record
9
+ gem 'sqlite3', '~> 1.4'
10
+ # Use Puma as the app server
11
+ gem 'puma', '~> 4.1'
12
+ # Use SCSS for stylesheets
13
+ gem 'sass-rails', '>= 6'
14
+ # Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
15
+ gem 'webpacker', '~> 4.0'
16
+ # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
17
+ gem 'turbolinks', '~> 5'
18
+ # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
19
+ gem 'jbuilder', '~> 2.7'
20
+ # Use Redis adapter to run Action Cable in production
21
+ # gem 'redis', '~> 4.0'
22
+ # Use Active Model has_secure_password
23
+ # gem 'bcrypt', '~> 3.1.7'
24
+
25
+ # Use Active Storage variant
26
+ # gem 'image_processing', '~> 1.2'
27
+
28
+ # Reduces boot times through caching; required in config/boot.rb
29
+ gem 'bootsnap', '>= 1.4.2', require: false
30
+
31
+ group :development, :test do
32
+ # Call 'byebug' anywhere in the code to stop execution and get a debugger console
33
+ gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
34
+ end
35
+
36
+ group :development do
37
+ # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
38
+ gem 'web-console', '>= 3.3.0'
39
+ gem 'listen', '~> 3.2'
40
+ # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
41
+ gem 'spring'
42
+ gem 'spring-watcher-listen', '~> 2.0.0'
43
+ end
44
+
45
+ group :test do
46
+ # Adds support for Capybara system testing and selenium driver
47
+ gem 'capybara', '>= 2.15'
48
+ gem 'selenium-webdriver'
49
+ # Easy installation and use of web drivers to run system tests with browsers
50
+ gem 'webdrivers'
51
+ end
52
+
53
+ # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
54
+ gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]