kuby-core 0.5.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +49 -0
  3. data/kuby-core.gemspec +4 -1
  4. data/lib/kuby.rb +23 -17
  5. data/lib/kuby/definition.rb +20 -14
  6. data/lib/kuby/docker.rb +2 -1
  7. data/lib/kuby/docker/alpine.rb +0 -1
  8. data/lib/kuby/docker/assets_phase.rb +1 -1
  9. data/lib/kuby/docker/bundler_phase.rb +4 -2
  10. data/lib/kuby/docker/cli.rb +32 -0
  11. data/lib/kuby/docker/copy_phase.rb +1 -1
  12. data/lib/kuby/docker/errors.rb +1 -0
  13. data/lib/kuby/docker/inline_layer.rb +15 -0
  14. data/lib/kuby/docker/{phase.rb → layer.rb} +6 -5
  15. data/lib/kuby/docker/layer_stack.rb +30 -4
  16. data/lib/kuby/docker/metadata.rb +10 -2
  17. data/lib/kuby/docker/package_phase.rb +1 -1
  18. data/lib/kuby/docker/setup_phase.rb +1 -1
  19. data/lib/kuby/docker/spec.rb +4 -4
  20. data/lib/kuby/docker/timestamp_tag.rb +6 -3
  21. data/lib/kuby/docker/webserver_phase.rb +1 -1
  22. data/lib/kuby/docker/yarn_phase.rb +1 -1
  23. data/lib/kuby/environment.rb +22 -0
  24. data/lib/kuby/kubernetes/minikube_provider.rb +5 -5
  25. data/lib/kuby/kubernetes/plugins/nginx_ingress.rb +12 -0
  26. data/lib/kuby/kubernetes/plugins/rails_app/database.rb +30 -9
  27. data/lib/kuby/kubernetes/plugins/rails_app/generators/kuby.rb +43 -4
  28. data/lib/kuby/kubernetes/plugins/rails_app/mysql.rb +14 -4
  29. data/lib/kuby/kubernetes/plugins/rails_app/plugin.rb +15 -14
  30. data/lib/kuby/kubernetes/plugins/rails_app/postgres.rb +132 -0
  31. data/lib/kuby/kubernetes/plugins/rails_app/sqlite.rb +20 -0
  32. data/lib/kuby/kubernetes/plugins/rails_app/tasks.rake +9 -4
  33. data/lib/kuby/kubernetes/spec.rb +1 -1
  34. data/lib/kuby/railtie.rb +0 -4
  35. data/lib/kuby/tasks.rb +31 -0
  36. data/lib/kuby/tasks/kuby.rake +19 -18
  37. data/lib/kuby/version.rb +1 -1
  38. data/spec/docker/timestamp_tag_spec.rb +11 -0
  39. data/spec/spec_helper.rb +102 -0
  40. metadata +23 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6cec43b807b241115b8aaede84a93510a8fdbc45ebf82a79c334073b9d3cc634
4
- data.tar.gz: f0beeedfdd8f48430c9715a4f5c2235037afbd99a7562475d40a7c0ff004e825
3
+ metadata.gz: a163afc89c23d091afce04277e9308252ae8335f8137f2136b68355dccb74bc2
4
+ data.tar.gz: 617007a29710ec9359dc4f3d5b31d850e22a0eacdc70175c444cf6d94a53462f
5
5
  SHA512:
6
- metadata.gz: a9b9d00b93f1734ebdb9459284b7d211bfe4610ac2ad964da9b2e57fd3618de5fce4b698d4b994d00383944a3d798925cf1ee07d884209e192f7146f1d33d369
7
- data.tar.gz: 5725aa6ca478958cd94d53fc77a5523b38392447084e642849130fb61375f99b287da7a13989ddb2c5505f6992b83515564e7615017c212971f5306df6557d40
6
+ metadata.gz: 8fd6302f94ffa2a740928bd324ca5e2bbe251f49356ce582e581c4d91740b66425b6c1a3b392e40fe7c52e649781fe939fa377d6894e435e00386ddaed0f4ee3
7
+ data.tar.gz: 4ae55a8fc6c4aeceac417b54e41f679a988c34788e5740e268ddeb728223f949915c17e8bc738ffcd744a257a1d5ba74cf431d1df90c8b5b496eaa51861229d6
@@ -1,3 +1,52 @@
1
+ ## 0.7.2
2
+ * Fix issue causing `Kuby.environment(...)` to raise an `UndefinedEnvironmentError` for existing environments.
3
+
4
+ ## 0.7.1
5
+ * Fix timestamp tag parsing regression caused by adding anchor tags to the regex.
6
+ - Instead, let's rely on `strptime` and return `nil` if it throws an `ArgumentError`.
7
+
8
+ ## 0.7.0
9
+ * Automatically perform `docker login` if not already logged into the Docker registry.
10
+ * Fix timestamp tag parsing issue causing deploy to fail with no available tags.
11
+ - Issue turned out to be ignoring the month of October in the validation regex.
12
+
13
+ ## 0.6.1
14
+ * Fix issue causing database.yml to not be rewritten to point at correct database host.
15
+
16
+ ## 0.6.0
17
+ * Don't load the Rails environment when running Kuby's rake tasks.
18
+ - Kuby's gems are still part of the bundle, but config has been moved out of the initializer and into kuby.rb in the Rails root directory.
19
+ - Internal classes no longer retain a reference to `Rails.application`.
20
+ - Kuby config now requires `environment` blocks:
21
+ ```ruby
22
+ Kuby.define('my-app') do
23
+ environment(:production) do
24
+ ...
25
+ end
26
+
27
+ environment(:staging) do
28
+ ...
29
+ end
30
+ end
31
+ ```
32
+ * Fix `MissingDistroError` caused by not setting a default distro.
33
+ * Create a .dockerignore file when running the Rails generator.
34
+ * Add ability to insert inline Docker layers without having to create a separate class, eg:
35
+ ```ruby
36
+ insert :hello, before: :bundler_phase do |dockerfile|
37
+ dockerfile.run('echo "hello, world"')
38
+ end
39
+ ```
40
+ * Add Postgres database support.
41
+ * Don't install sqlite libs by default.
42
+ * Modify Rails generator
43
+ - Require kuby and load config safely.
44
+ - Provide manual access to credentials via `ActiveSupport::EncryptedConfiguration`, which is necessary now that our rake tasks don't load the Rails environment.
45
+ * Add a convenience method for requesting the amount of block storage for the database.
46
+ * Add the ability to entirely disable database management via `manage_database false`.
47
+ * Avoid deploying nginx-ingress if it's already deployed.
48
+ * Add rake task for running arbitrary `kubectl` commands.
49
+
1
50
  ## 0.5.0
2
51
  * Fix Rails generators issue causing crash at startup.
3
52
  * Add rake task to run arbitrary kubectl commands.
@@ -15,13 +15,16 @@ Gem::Specification.new do |s|
15
15
  s.add_dependency 'colorize', '~> 0.8'
16
16
  s.add_dependency 'docker-remote', '~> 0.1'
17
17
  s.add_dependency 'krane', '~> 1.0'
18
- s.add_dependency 'kuby-cert-manager', '~> 0.1'
18
+ s.add_dependency 'kuby-cert-manager', '~> 0.2'
19
19
  s.add_dependency 'kube-dsl', '~> 0.3'
20
20
  s.add_dependency 'kuby-kube-db', '~> 0.4'
21
21
  s.add_dependency 'kubernetes-cli', '~> 0.2'
22
22
  s.add_dependency 'railties', '>= 5.1'
23
23
  s.add_dependency 'rouge', '~> 3.0'
24
24
 
25
+ s.add_development_dependency 'rspec'
26
+
25
27
  s.require_path = 'lib'
28
+
26
29
  s.files = Dir['{lib,spec}/**/*', 'Gemfile', 'LICENSE', 'CHANGELOG.md', 'README.md', 'Rakefile', 'kuby-core.gemspec']
27
30
  end
@@ -10,6 +10,7 @@ module Kuby
10
10
  autoload :CLIBase, 'kuby/cli_base'
11
11
  autoload :Definition, 'kuby/definition'
12
12
  autoload :Docker, 'kuby/docker'
13
+ autoload :Environment, 'kuby/environment'
13
14
  autoload :Kubernetes, 'kuby/kubernetes'
14
15
  autoload :Middleware, 'kuby/middleware'
15
16
  autoload :Tasks, 'kuby/tasks'
@@ -19,24 +20,30 @@ module Kuby
19
20
 
20
21
  class << self
21
22
  attr_reader :definition
22
- attr_accessor :logger
23
+ attr_writer :logger
23
24
 
24
- def define(environment, app = Rails.application, &block)
25
- environment = environment.to_s
26
- definitions[environment] ||= Definition.new(environment, app, &block)
25
+ def load!
26
+ require ENV['KUBY_CONFIG'] || File.join('.', 'kuby.rb')
27
27
  end
28
28
 
29
- def definitions
30
- @definitions ||= {}
31
- end
29
+ def define(name, &block)
30
+ raise 'Kuby is already configured' if @definition
31
+
32
+ @definition = Definition.new(name.to_s)
33
+ @definition.instance_eval(&block)
32
34
 
33
- def definition(environment = env)
34
- definitions.fetch(environment.to_s) do
35
- raise UndefinedEnvironmentError, "couldn't find a Kuby environment named "\
36
- "'#{environment}'"
35
+ @definition.environments.each do |_, env|
36
+ env.kubernetes.after_configuration
37
37
  end
38
38
  end
39
39
 
40
+ def environment(name = env)
41
+ definition.environment(name.to_s) || raise(
42
+ UndefinedEnvironmentError, "couldn't find a Kuby environment named "\
43
+ "'#{name}'"
44
+ )
45
+ end
46
+
40
47
  def register_provider(provider_name, provider_klass)
41
48
  providers[provider_name] = provider_klass
42
49
  end
@@ -61,6 +68,10 @@ module Kuby
61
68
  @plugins ||= {}
62
69
  end
63
70
 
71
+ def logger
72
+ @logger ||= BasicLogger.new(STDERR)
73
+ end
74
+
64
75
  def register_package(package_name, package_def = nil)
65
76
  packages[package_name] = case package_def
66
77
  when NilClass
@@ -86,7 +97,7 @@ module Kuby
86
97
 
87
98
  def env
88
99
  ENV.fetch('KUBY_ENV') do
89
- (definitions.keys.first || Rails.env).to_s
100
+ (definition.environments.keys.first || Rails.env).to_s
90
101
  end
91
102
  end
92
103
  end
@@ -114,8 +125,3 @@ Kuby.register_package(:c_toolchain,
114
125
  debian: 'build-essential',
115
126
  alpine: 'build-base'
116
127
  )
117
-
118
- Kuby.register_package(:sqlite_dev,
119
- debian: 'libsqlite3-dev',
120
- alpine: 'sqlite-dev'
121
- )
@@ -1,29 +1,35 @@
1
1
  module Kuby
2
2
  class Definition
3
- attr_reader :environment, :app
3
+ attr_reader :app_name
4
4
 
5
- def initialize(environment, app, &block)
6
- @environment = environment
7
- @app = app
5
+ def initialize(app_name, &block)
6
+ @app_name = app_name
7
+ end
8
+
9
+ def environment(name = Kuby.env, &block)
10
+ name = name.to_s
11
+
12
+ if name
13
+ environments[name] ||= Environment.new(name, self)
14
+ end
15
+
16
+ if block_given?
17
+ environments[name].instance_eval(&block)
18
+ end
8
19
 
9
- instance_eval(&block) if block
10
- kubernetes.after_configuration
20
+ environments[name]
11
21
  end
12
22
 
13
23
  def docker(&block)
14
- @docker ||= Docker::Spec.new(self)
15
- @docker.instance_eval(&block) if block
16
- @docker
24
+ environment.docker(&block)
17
25
  end
18
26
 
19
27
  def kubernetes(&block)
20
- @kubernetes ||= Kubernetes::Spec.new(self)
21
- @kubernetes.instance_eval(&block) if block
22
- @kubernetes
28
+ environment.kubernetes(&block)
23
29
  end
24
30
 
25
- def app_name
26
- @app_name ||= app.class.module_parent.name
31
+ def environments
32
+ @environments ||= {}
27
33
  end
28
34
  end
29
35
  end
@@ -10,12 +10,13 @@ module Kuby
10
10
  autoload :Credentials, 'kuby/docker/credentials'
11
11
  autoload :Debian, 'kuby/docker/debian'
12
12
  autoload :Dockerfile, 'kuby/docker/dockerfile'
13
+ autoload :InlineLayer, 'kuby/docker/inline_layer'
14
+ autoload :Layer, 'kuby/docker/layer'
13
15
  autoload :LayerStack, 'kuby/docker/layer_stack'
14
16
  autoload :LocalTags, 'kuby/docker/local_tags'
15
17
  autoload :Metadata, 'kuby/docker/metadata'
16
18
  autoload :Packages, 'kuby/docker/packages'
17
19
  autoload :PackagePhase, 'kuby/docker/package_phase'
18
- autoload :Phase, 'kuby/docker/phase'
19
20
  autoload :RemoteTags, 'kuby/docker/remote_tags'
20
21
  autoload :SetupPhase, 'kuby/docker/setup_phase'
21
22
  autoload :Spec, 'kuby/docker/spec'
@@ -8,7 +8,6 @@ module Kuby
8
8
  [:nodejs, '12.14.1'],
9
9
  [:yarn, '1.21.1'],
10
10
  [:c_toolchain],
11
- [:sqlite_dev],
12
11
  [:tzdata]
13
12
  ].freeze
14
13
 
@@ -1,6 +1,6 @@
1
1
  module Kuby
2
2
  module Docker
3
- class AssetsPhase < Phase
3
+ class AssetsPhase < Layer
4
4
  def apply_to(dockerfile)
5
5
  dockerfile.run(
6
6
  'bundle', 'exec', 'rake', 'assets:precompile'
@@ -1,6 +1,8 @@
1
+ require 'pathname'
2
+
1
3
  module Kuby
2
4
  module Docker
3
- class BundlerPhase < Phase
5
+ class BundlerPhase < Layer
4
6
  DEFAULT_WITHOUT = ['development', 'test', 'deploy'].freeze
5
7
 
6
8
  attr_accessor :version, :gemfile, :without
@@ -48,7 +50,7 @@ module Kuby
48
50
  .definition
49
51
  .gemfiles
50
52
  .first
51
- .relative_path_from(app.root)
53
+ .relative_path_from(Pathname(Dir.getwd))
52
54
  .to_s
53
55
  end
54
56
  end
@@ -10,6 +10,38 @@ module Kuby
10
10
  @executable = executable || `which docker`.strip
11
11
  end
12
12
 
13
+ def config_file
14
+ if File.exist?(default_config_file)
15
+ default_config_file
16
+ end
17
+ end
18
+
19
+ def default_config_file
20
+ File.join(Dir.home, '.docker', 'config.json')
21
+ end
22
+
23
+ def login(url:, username:, password:)
24
+ cmd = [
25
+ executable, 'login', url, '--username', username, '--password-stdin'
26
+ ]
27
+
28
+ open3_w({}, cmd) do |stdin, _wait_threads|
29
+ stdin.puts(password)
30
+ end
31
+
32
+ unless last_status.success?
33
+ raise LoginError, 'build failed: docker command exited with '\
34
+ "status code #{last_status.exitstatus}"
35
+ end
36
+ end
37
+
38
+ def auths
39
+ return [] unless config_file
40
+
41
+ config = JSON.parse(File.read(config_file))
42
+ config.fetch('auths', {}).keys
43
+ end
44
+
13
45
  def build(dockerfile:, image_url:, tags:)
14
46
  cmd = [
15
47
  executable, 'build',
@@ -1,6 +1,6 @@
1
1
  module Kuby
2
2
  module Docker
3
- class CopyPhase < Phase
3
+ class CopyPhase < Layer
4
4
  DEFAULT_PATHS = ['./'].freeze
5
5
 
6
6
  attr_reader :paths
@@ -2,6 +2,7 @@ module Kuby
2
2
  module Docker
3
3
  class BuildError < StandardError; end
4
4
  class PushError < StandardError; end
5
+ class LoginError < StandardError; end
5
6
 
6
7
  class MissingTagError < StandardError
7
8
  attr_reader :tag
@@ -0,0 +1,15 @@
1
+ module Kuby
2
+ module Docker
3
+ class InlineLayer < Layer
4
+ attr_reader :block
5
+
6
+ def initialize(block)
7
+ @block = block
8
+ end
9
+
10
+ def apply_to(dockerfile)
11
+ block.call(dockerfile)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,18 +1,19 @@
1
1
  module Kuby
2
2
  module Docker
3
- class Phase
3
+ class Layer
4
4
  attr_reader :definition
5
5
 
6
6
  def initialize(definition)
7
7
  @definition = definition
8
8
  end
9
9
 
10
- private
11
-
12
- def app
13
- definition.app
10
+ def apply_to(dockerfile)
11
+ raise NotImplementedError,
12
+ "#{__method__} must be defined in derived classes"
14
13
  end
15
14
 
15
+ private
16
+
16
17
  def metadata
17
18
  definition.docker.metadata
18
19
  end
@@ -15,12 +15,31 @@ module Kuby
15
15
  @stack.each { |name| yield layers[name] }
16
16
  end
17
17
 
18
- def use(name, layer)
18
+ def use(name, layer = nil, &block)
19
19
  stack << name
20
- layers[name] = layer
20
+
21
+ if layer
22
+ layers[name] = layer
23
+ elsif block_given?
24
+ layers[name] = InlineLayer.new(block)
25
+ else
26
+ raise "Must either pass a layer object or a block to `#{__method__}'"
27
+ end
21
28
  end
22
29
 
23
- def insert(name, layer, options = {})
30
+ def insert(name, layer = nil, options = {}, &block)
31
+ # this is truly gross but it's the only way I can think of to be able
32
+ # to call insert these two ways:
33
+ #
34
+ # insert :foo, FooLayer.new, before: :bundler_phase
35
+ # insert :foo, before: :bundler_phase do
36
+ # ...
37
+ # end
38
+ if layer.is_a?(Hash)
39
+ insert(name, nil, options.merge(layer), &block)
40
+ return
41
+ end
42
+
24
43
  existing_name = options[:before] || options[:after]
25
44
  idx = stack.index(existing_name)
26
45
 
@@ -30,7 +49,14 @@ module Kuby
30
49
 
31
50
  idx += 1 if options[:after]
32
51
  stack.insert(idx, name)
33
- layers[name] = layer
52
+
53
+ if layer
54
+ layers[name] = layer
55
+ elsif block_given?
56
+ layers[name] = InlineLayer.new(block)
57
+ else
58
+ raise "Must either pass a layer object or a block to `#{__method__}'"
59
+ end
34
60
  end
35
61
 
36
62
  def delete(name)
@@ -4,11 +4,11 @@ module Kuby
4
4
  module Docker
5
5
  class Metadata
6
6
  DEFAULT_DISTRO = :debian
7
- DEFAULT_REGISTRY_HOST = 'https://docker.io'.freeze
7
+ DEFAULT_REGISTRY_HOST = 'https://www.docker.com'.freeze
8
8
  LATEST_TAG = 'latest'
9
9
 
10
10
  attr_accessor :image_url
11
- attr_reader :definition, :distro
11
+ attr_reader :definition
12
12
 
13
13
  def initialize(definition)
14
14
  @definition = definition
@@ -28,6 +28,10 @@ module Kuby
28
28
  end
29
29
  end
30
30
 
31
+ def image_hostname
32
+ @image_hostname ||= URI(image_host).host
33
+ end
34
+
31
35
  def image_repo
32
36
  @image_repo ||= if image_url.include?('/')
33
37
  parse_url(image_url).path.sub(/\A\//, '')
@@ -62,6 +66,10 @@ module Kuby
62
66
  t.to_s
63
67
  end
64
68
 
69
+ def distro
70
+ @distro || DEFAULT_DISTRO
71
+ end
72
+
65
73
  def distro=(distro_name)
66
74
  @distro = distro_name
67
75
  end