kuby-core 0.14.0 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b6d5377f043be7d0aa02cddf817a9dd7fdbaa500c2ed94a2fdb92fa7ef0c1cc
4
- data.tar.gz: 57c6cb1a15c4385a7675a05637dbfd085e2b2f438f5429104ec66203e854dd68
3
+ metadata.gz: 0a5f265cee423b7487de7e4c209b26cc9fef63cf584455bc4164ecd7e6384377
4
+ data.tar.gz: e48304bf9397b30ceed7025039d074e55423ced190f734a416a311e412bd609d
5
5
  SHA512:
6
- metadata.gz: 9c903b0768473d63df1a3ddb9ee3146193abad9c809614f7ccd2e7b7c711a2a3effd3810a3b67780ee68ea3198b8770099fcb3fd4d2a334b39b664f59b8e9e8f
7
- data.tar.gz: 69fc8934c14181930c03d3d3ffe050e2da0a85edbbd2f5bba38bf0509131149c643ea42577afcec3a4625057eab52f57bab1e00c3271bedcf491d2e11f1ae696
6
+ metadata.gz: d4cbd6c2fbf6de8f621d4b2412bb4c1921eff7e5ba3c90b5afefac3c0d216aff676772b0582dab70a688c3c1dc7ce55a48f9c42f3b178ad25f31d489a3f33421
7
+ data.tar.gz: b9b3f808ed6184c73edb8652a958cd53b12337c8a48bf2f19d188b07c1c4d56283f7bfbfffd2a9338dc522a2376f8665e9c792571b4fdb609bf14b0181efcc16
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 0.15.0
2
+ * Add an extra parameter to the Docker spec for specifying the URL of the Docker registry index.
3
+ - In most cases, the registry and registry index URLs will be the same. However Docker Hub, the default registry, uses index.docker.io for API requests (catalog, tags, etc) but only allows pushes to docker.io.
4
+ * Add config/master.key to the .dockerignore created by the Rails generator.
5
+ - This was an unfortunate oversight, but such are the perils of using pre-1.0 software.
6
+ * Print an error message if building images on non-x86 hardware.
7
+ - People with M1 Macs are running into mismatches between their laptop's arch and the arch of their hosting provider's hardware. The error message asks you to explicitly pass the `--platform` flag to Docker.
8
+ - We may need to revisit this in the future if ARM processors become more popular.
9
+
1
10
  ## 0.14.0
2
11
  * Make RAILS_MASTER_KEY available in the assets image (@palkan, #63)
3
12
  * Allow specifying custom docker build options via CLI (@palkan, #65)
data/lib/kuby/commands.rb CHANGED
@@ -1,4 +1,4 @@
1
- # typed: strict
1
+ # typed: false
2
2
 
3
3
  require 'kuby/version'
4
4
  require 'gli'
@@ -170,7 +170,7 @@ module Kuby
170
170
  rc.desc 'Runs an arbitrary command inside a running Rails pod.'
171
171
  rc.command :exec do |c|
172
172
  c.action do |global_options, options, args|
173
- tasks.remote_exec([*args, *@rails_options])
173
+ tasks.remote_exec([*args, *T.unsafe(@rails_options)])
174
174
  end
175
175
  end
176
176
 
@@ -11,11 +11,12 @@ module Kuby
11
11
  dockerfile: T.any(Dockerfile, T.proc.returns(Dockerfile)),
12
12
  image_url: String,
13
13
  credentials: Credentials,
14
+ registry_index_url: T.nilable(String),
14
15
  main_tag: T.nilable(String),
15
16
  alias_tags: T::Array[String]
16
17
  ).void
17
18
  }
18
- def initialize(dockerfile, image_url, credentials, main_tag = nil, alias_tags = [])
19
+ def initialize(dockerfile, image_url, credentials, registry_index_url = nil, main_tag = nil, alias_tags = [])
19
20
  super
20
21
  @identifier = "app"
21
22
  end
@@ -5,11 +5,36 @@ module Kuby
5
5
  class DockerURI
6
6
  extend T::Sig
7
7
 
8
- DEFAULT_REGISTRY_HOST = T.let('index.docker.io'.freeze, String)
9
- DEFAULT_REGISTRY_PORT = T.let(443, Integer)
8
+ DEFAULT_REGISTRY_HOST = T.let('docker.io'.freeze, String)
9
+ DEFAULT_REGISTRY_INDEX_HOST = T.let('index.docker.io'.freeze, String)
10
+ DEFAULT_PORT = T.let(443, Integer)
10
11
 
11
12
  sig { params(url: String).returns(DockerURI) }
12
- def self.parse(url)
13
+ def self.parse_uri(url)
14
+ parse(
15
+ url,
16
+ default_host: DEFAULT_REGISTRY_HOST,
17
+ default_port: DEFAULT_PORT
18
+ )
19
+ end
20
+
21
+ sig { params(url: String).returns(DockerURI) }
22
+ def self.parse_index_uri(url)
23
+ parse(
24
+ url,
25
+ default_host: DEFAULT_REGISTRY_INDEX_HOST,
26
+ default_port: DEFAULT_PORT
27
+ )
28
+ end
29
+
30
+ sig {
31
+ params(
32
+ url: String,
33
+ default_host: T.nilable(String),
34
+ default_port: T.nilable(Integer)
35
+ ).returns(DockerURI)
36
+ }
37
+ def self.parse(url, default_host:, default_port:)
13
38
  if idx = url.index('://')
14
39
  url = url[(idx + 3)..-1] || ''
15
40
  end
@@ -17,9 +42,9 @@ module Kuby
17
42
  host_port, *path = url.split('/')
18
43
  host, port, *path = if host_port =~ /[.:]/
19
44
  hst, prt = T.must(host_port).split(':')
20
- [T.must(hst), prt || DEFAULT_REGISTRY_PORT, *path]
45
+ [T.must(hst), prt || default_port, *path]
21
46
  else
22
- [DEFAULT_REGISTRY_HOST, DEFAULT_REGISTRY_PORT, host_port, *path]
47
+ [default_host, default_port, host_port, *path]
23
48
  end
24
49
 
25
50
  new(host.to_s, port.to_i, (path || []).join('/'))
@@ -40,6 +65,11 @@ module Kuby
40
65
  @port = port
41
66
  @path = path
42
67
  end
68
+
69
+ sig { returns(T::Boolean) }
70
+ def has_default_port?
71
+ port == DEFAULT_PORT
72
+ end
43
73
  end
44
74
  end
45
75
  end
@@ -11,6 +11,9 @@ module Kuby
11
11
  sig { returns(String) }
12
12
  attr_reader :image_url
13
13
 
14
+ sig { returns(T.nilable(String)) }
15
+ attr_reader :registry_index_url
16
+
14
17
  sig { returns(Credentials) }
15
18
  attr_reader :credentials
16
19
 
@@ -25,13 +28,15 @@ module Kuby
25
28
  dockerfile: T.any(Dockerfile, T.proc.returns(Dockerfile)),
26
29
  image_url: String,
27
30
  credentials: Credentials,
31
+ registry_index_url: T.nilable(String),
28
32
  main_tag: T.nilable(String),
29
33
  alias_tags: T::Array[String]
30
34
  ).void
31
35
  }
32
- def initialize(dockerfile, image_url, credentials, main_tag = nil, alias_tags = [])
36
+ def initialize(dockerfile, image_url, credentials, registry_index_url = nil, main_tag = nil, alias_tags = [])
33
37
  @dockerfile = T.let(dockerfile, T.any(Dockerfile, T.proc.returns(Dockerfile)))
34
38
  @image_url = T.let(image_url, String)
39
+ @registry_index_url = T.let(registry_index_url, T.nilable(String))
35
40
  @credentials = T.let(credentials, Credentials)
36
41
  @main_tag = T.let(main_tag, T.nilable(String))
37
42
  @alias_tags = T.let(alias_tags, T::Array[String])
@@ -39,6 +44,9 @@ module Kuby
39
44
 
40
45
  @image_host = T.let(@image_host, T.nilable(String))
41
46
  @image_hostname = T.let(@image_hostname, T.nilable(String))
47
+ @registry_index_host = T.let(@registry_index_host, T.nilable(String))
48
+ @registry_index_hostname = T.let(@registry_index_hostname, T.nilable(String))
49
+ @registry_index_uri = T.let(@registry_index_uri, T.nilable(DockerURI))
42
50
  @image_repo = T.let(@image_repo, T.nilable(String))
43
51
  @full_image_uri = T.let(@full_image_uri, T.nilable(DockerURI))
44
52
  @docker_cli = T.let(@docker_cli, T.nilable(Docker::CLI))
@@ -73,6 +81,16 @@ module Kuby
73
81
  @image_host ||= "#{image_uri.host}:#{image_uri.port}"
74
82
  end
75
83
 
84
+ sig { returns(String) }
85
+ def registry_index_host
86
+ @registry_index_host ||= "#{registry_index_uri.host}:#{registry_index_uri.port}"
87
+ end
88
+
89
+ sig { returns(String) }
90
+ def registry_index_hostname
91
+ @registry_index_hostname ||= registry_index_uri.host
92
+ end
93
+
76
94
  sig { returns(String) }
77
95
  def image_hostname
78
96
  @image_hostname ||= image_uri.host
@@ -85,7 +103,12 @@ module Kuby
85
103
 
86
104
  sig { returns(DockerURI) }
87
105
  def image_uri
88
- @full_image_uri ||= DockerURI.parse(image_url)
106
+ @full_image_uri ||= DockerURI.parse_uri(image_url)
107
+ end
108
+
109
+ sig { returns(DockerURI) }
110
+ def registry_index_uri
111
+ @registry_index_uri ||= DockerURI.parse_index_uri(registry_index_url || image_url)
89
112
  end
90
113
 
91
114
  sig { returns(T::Array[String]) }
@@ -112,7 +135,7 @@ module Kuby
112
135
 
113
136
  sig { params(main_tag: String, alias_tags: T::Array[String]).returns(Image) }
114
137
  def duplicate_with_tags(main_tag, alias_tags)
115
- self.class.new(dockerfile, image_url, credentials, main_tag, alias_tags)
138
+ self.class.new(dockerfile, image_url, credentials, registry_index_url, main_tag, alias_tags)
116
139
  end
117
140
  end
118
141
  end
@@ -13,6 +13,9 @@ module Kuby
13
13
  sig { returns(T.nilable(String)) }
14
14
  attr_reader :image_url_str
15
15
 
16
+ sig { returns(T.nilable(String)) }
17
+ attr_reader :registry_index_url_str
18
+
16
19
  sig { params(environment: Environment).void }
17
20
  def initialize(environment)
18
21
  @environment = environment
@@ -31,6 +34,7 @@ module Kuby
31
34
  @layer_stack = T.let(@layer_stack, T.nilable(Kuby::Docker::LayerStack))
32
35
 
33
36
  @image_url_str = T.let(@image_url_str, T.nilable(String))
37
+ @registry_index_url_str = T.let(@registry_index_url_str, T.nilable(String))
34
38
  @image = T.let(@image, T.nilable(Docker::AppImage))
35
39
  end
36
40
 
@@ -96,6 +100,11 @@ module Kuby
96
100
  @image_url_str = url
97
101
  end
98
102
 
103
+ sig { params(url: String).void }
104
+ def registry_index_url(url)
105
+ @registry_index_url_str = url
106
+ end
107
+
99
108
  sig {
100
109
  params(
101
110
  name: Symbol,
@@ -148,7 +157,7 @@ module Kuby
148
157
  end
149
158
 
150
159
  Docker::AppImage.new(
151
- dockerfile, T.must(image_url_str), credentials
160
+ dockerfile, T.must(image_url_str), credentials, registry_index_url_str
152
161
  )
153
162
  end
154
163
  end
@@ -12,11 +12,12 @@ module Kuby
12
12
  dockerfile: T.any(Dockerfile, T.proc.returns(Dockerfile)),
13
13
  image_url: String,
14
14
  credentials: Credentials,
15
+ registry_index_url_str: T.nilable(String),
15
16
  main_tag: T.nilable(String),
16
17
  alias_tags: T::Array[String]
17
18
  ).void
18
19
  }
19
- def initialize(dockerfile, image_url, credentials, main_tag = nil, alias_tags = [])
20
+ def initialize(dockerfile, image_url, credentials, registry_index_url_str = nil, main_tag = nil, alias_tags = [])
20
21
  @new_version = T.let(@new_version, T.nilable(Image))
21
22
  @current_version = T.let(@current_version, T.nilable(Image))
22
23
  @previous_version = T.let(@previous_version, T.nilable(Image))
@@ -90,7 +91,7 @@ module Kuby
90
91
  sig { returns(::Docker::Remote::Client) }
91
92
  def remote_client
92
93
  @remote_client ||= ::Docker::Remote::Client.new(
93
- image_host, image_repo, credentials.username, credentials.password,
94
+ registry_index_host, image_repo, credentials.username, credentials.password,
94
95
  )
95
96
  end
96
97
 
@@ -106,7 +107,7 @@ module Kuby
106
107
 
107
108
  sig { returns(RemoteTags) }
108
109
  def remote
109
- @remote ||= RemoteTags.new(remote_client, image_url)
110
+ @remote ||= RemoteTags.new(remote_client, registry_index_host)
110
111
  end
111
112
  end
112
113
  end
@@ -296,7 +296,7 @@ module Kuby
296
296
  end
297
297
 
298
298
  def image
299
- @image ||= RailsApp::AssetsImage.new(docker.image, -> { dockerfile })
299
+ @image ||= RailsApp::AssetsImage.new(docker.image, -> { dockerfile }, docker.image.registry_index_url)
300
300
  end
301
301
 
302
302
  private
@@ -324,6 +324,7 @@ module Kuby
324
324
  if tag
325
325
  image_name = "#{app_name}-#{tag}"
326
326
  df.from("#{base_image.image_url}:#{tag}", as: image_name)
327
+ df.arg('RAILS_MASTER_KEY')
327
328
  df.copy("--from=#{prev_image_name} #{RAILS_MOUNT_PATH}", RAILS_MOUNT_PATH)
328
329
  df.run("env RAILS_MASTER_KEY=$RAILS_MASTER_KEY bundle exec rake kuby:rails_app:assets:copy")
329
330
  end
@@ -4,8 +4,8 @@ module Kuby
4
4
  class AssetsImage < ::Kuby::Docker::Image
5
5
  attr_reader :base_image
6
6
 
7
- def initialize(base_image, dockerfile, main_tag = nil, alias_tags = [])
8
- super(dockerfile, base_image.image_url, base_image.credentials, main_tag, alias_tags)
7
+ def initialize(base_image, dockerfile, registry_index_url = nil, main_tag = nil, alias_tags = [])
8
+ super(dockerfile, base_image.image_url, base_image.credentials, registry_index_url, main_tag, alias_tags)
9
9
  @base_image = base_image
10
10
  @identifier = "assets"
11
11
  end
@@ -42,6 +42,7 @@ module Kuby
42
42
  self.class.new(
43
43
  base_image,
44
44
  dockerfile,
45
+ registry_index_url,
45
46
  annotate_tag(image.main_tag),
46
47
  image.alias_tags.map { |at| annotate_tag(at) }
47
48
  )
@@ -86,16 +86,38 @@ class KubyGenerator < Rails::Generators::Base
86
86
  create_file(
87
87
  '.dockerignore',
88
88
  <<~END
89
- .bundle/
90
- vendor/bundle
91
- node_modules/
92
- .node_modules/
93
- **/.git*
94
- tmp/
95
- log/
96
- engines/**/log/
97
- engines/**/tmp/
89
+ .git/
90
+
91
+ # Ignore bundler config.
92
+ .bundle
93
+
94
+ # Ignore all logfiles and tempfiles.
95
+ log/*
96
+ tmp/*
97
+ !log/.keep
98
+ !tmp/.keep
99
+
100
+ # Ignore pidfiles, but keep the directory.
101
+ tmp/pids/*
102
+ !tmp/pids/
103
+ !tmp/pids/.keep
104
+
105
+ # Ignore uploaded files in development.
106
+ storage/*
107
+ !storage/.keep
108
+
98
109
  public/assets
110
+ **/.byebug_history
111
+
112
+ # Ignore master key for decrypting credentials and more.
113
+ config/master.key
114
+
115
+ public/packs
116
+ public/packs-test
117
+ node_modules
118
+ yarn-error.log
119
+ **/yarn-debug.log*
120
+ **/.yarn-integrity
99
121
  END
100
122
  )
101
123
  end
data/lib/kuby/tasks.rb CHANGED
@@ -29,9 +29,13 @@ module Kuby
29
29
  end
30
30
 
31
31
  def build(build_args = {}, docker_args = [], only = nil)
32
+ check_platform(docker_args)
33
+
32
34
  kubernetes.docker_images.each do |image|
33
35
  next if only && image.identifier != only
34
36
 
37
+ return unless perform_docker_login_if_necessary(image)
38
+
35
39
  image = image.new_version
36
40
  Kuby.logger.info("Building image #{image.image_url} with tags #{image.tags.join(', ')}")
37
41
  image.build(build_args, docker_args)
@@ -49,21 +53,7 @@ module Kuby
49
53
  end
50
54
 
51
55
  def push_image(image)
52
- if image.credentials.username && !image.docker_cli.auths.include?(image.image_host)
53
- Kuby.logger.info("Attempting to log in to registry at #{image.image_host}")
54
-
55
- begin
56
- image.docker_cli.login(
57
- url: image.image_host,
58
- username: image.credentials.username,
59
- password: image.credentials.password
60
- )
61
- rescue Kuby::Docker::LoginError => e
62
- Kuby.logger.fatal("Couldn't log in to the registry at #{image.image_host}")
63
- Kuby.logger.fatal(e.message)
64
- return
65
- end
66
- end
56
+ return unless perform_docker_login_if_necessary(image)
67
57
 
68
58
  begin
69
59
  image.tags.each { |tag| image.push(tag) }
@@ -145,6 +135,71 @@ module Kuby
145
135
 
146
136
  private
147
137
 
138
+ def check_platform(docker_args)
139
+ arch, * = RUBY_PLATFORM.split('-')
140
+
141
+ if arch != 'x86_64' && !docker_args.include?('--platform')
142
+ Kuby.logger.fatal(<<~END)
143
+ Hey there! It looks like your processor isn't x86-compatible.
144
+ By default, Docker will try to build images that match the
145
+ current architecture, in this case #{arch}. Most hosting
146
+ providers run x86 hardware, meaning Docker images built using
147
+ this computer's architecture might fail to run when deployed
148
+ to production. You can fix this by running the build command
149
+ with a special --platform flag, eg:
150
+
151
+ bundle exec kuby -e production build -- --platform linux/amd64
152
+
153
+ If you meant to build for the current architecture, you can
154
+ prevent this error by passing the --platform argument for the
155
+ current architecture, eg. --platform linux/arm64 for ARM, etc.
156
+ END
157
+
158
+ exit 1
159
+ end
160
+ end
161
+
162
+ def perform_docker_login_if_necessary(image)
163
+ auth_uris = image.docker_cli.auths.map do |url|
164
+ Kuby::Docker::DockerURI.parse_uri(url)
165
+ end
166
+
167
+ logged_in = image.credentials.username && (
168
+ auth_uris.any? do |uri|
169
+ image.image_hostname == uri.host ||
170
+ image.registry_index_hostname == uri.host
171
+ end
172
+ )
173
+
174
+ if !logged_in
175
+ Kuby.logger.info("Attempting to log in to registry at #{image.image_host}")
176
+
177
+ begin
178
+ # For some reason, Docker login with a port doesn't work for some
179
+ # registries (most notably Docker Hub). Since the default is 443 anyway,
180
+ # it should be fine to omit it.
181
+ url = if image.image_uri.has_default_port?
182
+ image.image_hostname # host without port
183
+ else
184
+ image.image_host # host with port
185
+ end
186
+
187
+ image.docker_cli.login(
188
+ url: url,
189
+ username: image.credentials.username,
190
+ password: image.credentials.password
191
+ )
192
+ rescue Kuby::Docker::LoginError => e
193
+ Kuby.logger.fatal("Couldn't log in to the registry at #{image.image_host}")
194
+ Kuby.logger.fatal(e.message)
195
+
196
+ return false
197
+ end
198
+ end
199
+
200
+ true
201
+ end
202
+
148
203
  def get_first_pod
149
204
  pods = kubernetes_cli.get_objects(
150
205
  'pods', namespace, match_labels.serialize
data/lib/kuby/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # typed: true
2
2
 
3
3
  module Kuby
4
- VERSION = '0.14.0'.freeze
4
+ VERSION = '0.15.0'.freeze
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kuby-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.0
4
+ version: 0.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cameron Dutro
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-28 00:00:00.000000000 Z
11
+ date: 2021-12-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize