kuby-core 0.5.0 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
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