kuby-core 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/kuby-core.gemspec +2 -1
  4. data/lib/kuby.rb +20 -14
  5. data/lib/kuby/definition.rb +22 -16
  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/copy_phase.rb +1 -1
  11. data/lib/kuby/docker/inline_layer.rb +15 -0
  12. data/lib/kuby/docker/{phase.rb → layer.rb} +6 -5
  13. data/lib/kuby/docker/layer_stack.rb +30 -4
  14. data/lib/kuby/docker/metadata.rb +5 -1
  15. data/lib/kuby/docker/package_phase.rb +1 -1
  16. data/lib/kuby/docker/setup_phase.rb +1 -1
  17. data/lib/kuby/docker/spec.rb +4 -4
  18. data/lib/kuby/docker/webserver_phase.rb +1 -1
  19. data/lib/kuby/docker/yarn_phase.rb +1 -1
  20. data/lib/kuby/environment.rb +22 -0
  21. data/lib/kuby/kubernetes/minikube_provider.rb +5 -5
  22. data/lib/kuby/kubernetes/plugins/nginx_ingress.rb +12 -0
  23. data/lib/kuby/kubernetes/plugins/rails_app/database.rb +30 -9
  24. data/lib/kuby/kubernetes/plugins/rails_app/generators/kuby.rb +43 -4
  25. data/lib/kuby/kubernetes/plugins/rails_app/mysql.rb +14 -4
  26. data/lib/kuby/kubernetes/plugins/rails_app/plugin.rb +15 -14
  27. data/lib/kuby/kubernetes/plugins/rails_app/postgres.rb +132 -0
  28. data/lib/kuby/kubernetes/plugins/rails_app/sqlite.rb +20 -0
  29. data/lib/kuby/kubernetes/plugins/rails_app/tasks.rake +9 -4
  30. data/lib/kuby/kubernetes/spec.rb +1 -1
  31. data/lib/kuby/railtie.rb +0 -4
  32. data/lib/kuby/tasks.rb +12 -0
  33. data/lib/kuby/tasks/kuby.rake +19 -18
  34. data/lib/kuby/version.rb +1 -1
  35. metadata +7 -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: cfe201dc901e8ae1b43121cb2652343f8db33893a6da69d783715cc7f320e2d2
4
+ data.tar.gz: 452b7119e76ee461f93729aa70999d51a5b1f77bc7b3af5c4935ebd863f2e526
5
5
  SHA512:
6
- metadata.gz: a9b9d00b93f1734ebdb9459284b7d211bfe4610ac2ad964da9b2e57fd3618de5fce4b698d4b994d00383944a3d798925cf1ee07d884209e192f7146f1d33d369
7
- data.tar.gz: 5725aa6ca478958cd94d53fc77a5523b38392447084e642849130fb61375f99b287da7a13989ddb2c5505f6992b83515564e7615017c212971f5306df6557d40
6
+ metadata.gz: 2944a9ea28ee31b4763d2cbbe2db04cc9009526919ef8a25b8513fbf2fdfbf72e5356ee94969b4623fc208e3b7bc527d1423e68d39447e500c0a2bd8b93867ca
7
+ data.tar.gz: 6259dd452e62337c01d1d301a0adc0fedb394c6a667e7f51a5cece1cf5464654708ba9ca56783aed610067e6b6a86131f8d7cb9371de51202e0f867610d66f15
@@ -1,3 +1,37 @@
1
+ ## 0.6.0
2
+ * Don't load the Rails environment when running Kuby's rake tasks.
3
+ - 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.
4
+ - Internal classes no longer retain a reference to `Rails.application`.
5
+ - Kuby config now requires `environment` blocks:
6
+ ```ruby
7
+ Kuby.define('my-app') do
8
+ environment(:production) do
9
+ ...
10
+ end
11
+
12
+ environment(:staging) do
13
+ ...
14
+ end
15
+ end
16
+ ```
17
+ * Fix `MissingDistroError` caused by not setting a default distro.
18
+ * Create a .dockerignore file when running the Rails generator.
19
+ * Add ability to insert inline Docker layers without having to create a separate class, eg:
20
+ ```ruby
21
+ insert :hello, before: :bundler_phase do |dockerfile|
22
+ dockerfile.run('echo "hello, world"')
23
+ end
24
+ ```
25
+ * Add Postgres database support.
26
+ * Don't install sqlite libs by default.
27
+ * Modify Rails generator
28
+ - Require kuby and load config safely.
29
+ - Provide manual access to credentials via `ActiveSupport::EncryptedConfiguration`, which is necessary now that our rake tasks don't load the Rails environment.
30
+ * Add a convenience method for requesting the amount of block storage for the database.
31
+ * Add the ability to entirely disable database management via `manage_database false`.
32
+ * Avoid deploying nginx-ingress if it's already deployed.
33
+ * Add rake task for running arbitrary `kubectl` commands.
34
+
1
35
  ## 0.5.0
2
36
  * Fix Rails generators issue causing crash at startup.
3
37
  * Add rake task to run arbitrary kubectl commands.
@@ -15,7 +15,7 @@ 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'
@@ -23,5 +23,6 @@ Gem::Specification.new do |s|
23
23
  s.add_dependency 'rouge', '~> 3.0'
24
24
 
25
25
  s.require_path = 'lib'
26
+
26
27
  s.files = Dir['{lib,spec}/**/*', 'Gemfile', 'LICENSE', 'CHANGELOG.md', 'README.md', 'Rakefile', 'kuby-core.gemspec']
27
28
  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,19 +20,25 @@ 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 ||= {}
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)
34
+
35
+ @definition.environments.each do |_, env|
36
+ env.kubernetes.after_configuration
37
+ end
31
38
  end
32
39
 
33
- def definition(environment = env)
34
- definitions.fetch(environment.to_s) do
40
+ def environment(name = env)
41
+ definition.environment(name.to_s) do
35
42
  raise UndefinedEnvironmentError, "couldn't find a Kuby environment named "\
36
43
  "'#{environment}'"
37
44
  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
- def docker(&block)
14
- @docker ||= Docker::Spec.new(self)
15
- @docker.instance_eval(&block) if block
16
- @docker
23
+ def docker
24
+ environment.docker
17
25
  end
18
26
 
19
- def kubernetes(&block)
20
- @kubernetes ||= Kubernetes::Spec.new(self)
21
- @kubernetes.instance_eval(&block) if block
22
- @kubernetes
27
+ def kubernetes
28
+ environment.kubernetes
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
@@ -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
@@ -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)
@@ -8,7 +8,7 @@ module Kuby
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
@@ -62,6 +62,10 @@ module Kuby
62
62
  t.to_s
63
63
  end
64
64
 
65
+ def distro
66
+ @distro || DEFAULT_DISTRO
67
+ end
68
+
65
69
  def distro=(distro_name)
66
70
  @distro = distro_name
67
71
  end
@@ -1,6 +1,6 @@
1
1
  module Kuby
2
2
  module Docker
3
- class PackagePhase < Phase
3
+ class PackagePhase < Layer
4
4
  attr_reader :operations
5
5
 
6
6
  def initialize(*args)
@@ -1,6 +1,6 @@
1
1
  module Kuby
2
2
  module Docker
3
- class SetupPhase < Phase
3
+ class SetupPhase < Layer
4
4
  DEFAULT_WORKING_DIR = '/usr/src/app'.freeze
5
5
 
6
6
  attr_accessor :base_image, :working_dir
@@ -50,12 +50,12 @@ module Kuby
50
50
  metadata.image_url = url
51
51
  end
52
52
 
53
- def use(*args)
54
- layer_stack.use(*args)
53
+ def use(*args, &block)
54
+ layer_stack.use(*args, &block)
55
55
  end
56
56
 
57
- def insert(*args)
58
- layer_stack.insert(*args)
57
+ def insert(*args, &block)
58
+ layer_stack.insert(*args, &block)
59
59
  end
60
60
 
61
61
  def delete(*args)
@@ -1,6 +1,6 @@
1
1
  module Kuby
2
2
  module Docker
3
- class WebserverPhase < Phase
3
+ class WebserverPhase < Layer
4
4
  class Puma
5
5
  attr_reader :phase
6
6
 
@@ -1,6 +1,6 @@
1
1
  module Kuby
2
2
  module Docker
3
- class YarnPhase < Phase
3
+ class YarnPhase < Layer
4
4
  def apply_to(dockerfile)
5
5
  dockerfile.copy('package.json', '.')
6
6
  dockerfile.copy('yarn.lock*', '.')
@@ -0,0 +1,22 @@
1
+ module Kuby
2
+ class Environment
3
+ attr_reader :name, :definition
4
+
5
+ def initialize(name, definition, &block)
6
+ @name = name
7
+ @definition = definition
8
+ end
9
+
10
+ def docker(&block)
11
+ @docker ||= Docker::Spec.new(definition)
12
+ @docker.instance_eval(&block) if block
13
+ @docker
14
+ end
15
+
16
+ def kubernetes(&block)
17
+ @kubernetes ||= Kubernetes::Spec.new(definition)
18
+ @kubernetes.instance_eval(&block) if block
19
+ @kubernetes
20
+ end
21
+ end
22
+ end
@@ -26,11 +26,6 @@ module Kuby
26
26
  rails_app.resources.delete(rails_app.ingress)
27
27
  rails_app.service.spec { type 'LoadBalancer' }
28
28
  end
29
-
30
- configure do
31
- # default kubeconfig path
32
- kubeconfig File.join(ENV['HOME'], '.kube', 'config')
33
- end
34
29
  end
35
30
 
36
31
  def kubeconfig_path
@@ -45,6 +40,11 @@ module Kuby
45
40
 
46
41
  def after_initialize
47
42
  @config = Config.new
43
+
44
+ configure do
45
+ # default kubeconfig path
46
+ kubeconfig File.join(ENV['HOME'], '.kube', 'config')
47
+ end
48
48
  end
49
49
  end
50
50
  end
@@ -27,6 +27,11 @@ module Kuby
27
27
  def setup
28
28
  Kuby.logger.info('Deploying nginx ingress resources')
29
29
 
30
+ if already_deployed?
31
+ Kuby.logger.info('Nginx ingress already deployed, skipping')
32
+ return
33
+ end
34
+
30
35
  SETUP_RESOURCES.each do |uri|
31
36
  uri = uri % { provider: @config.provider || DEFAULT_PROVIDER }
32
37
  kubernetes_cli.apply_uri(uri)
@@ -48,6 +53,13 @@ module Kuby
48
53
 
49
54
  private
50
55
 
56
+ def already_deployed?
57
+ kubernetes_cli.get_object('Service', 'ingress-nginx', 'ingress-nginx')
58
+ true
59
+ rescue KubernetesCLI::GetResourceError
60
+ return false
61
+ end
62
+
51
63
  def after_initialize
52
64
  @config = Config.new
53
65
  end
@@ -1,3 +1,6 @@
1
+ require 'erb'
2
+ require 'yaml'
3
+
1
4
  module Kuby
2
5
  module Kubernetes
3
6
  module Plugins
@@ -11,27 +14,29 @@ module Kuby
11
14
  'postgresql' => Postgres
12
15
  }.freeze
13
16
 
14
- def self.get(definition)
15
- new(definition).database
17
+ def self.get(rails_app)
18
+ if rails_app.manage_database?
19
+ new(rails_app).database
20
+ end
16
21
  end
17
22
 
18
23
  def self.get_adapter(adapter)
19
24
  ADAPTER_MAP.fetch(adapter) do
20
- raise UnsupportedDatabaseError, "Kuby does not support the '#{adapter}'"\
25
+ raise UnsupportedDatabaseError, "Kuby does not support the '#{adapter}' "\
21
26
  'database adapter'
22
27
  end
23
28
  end
24
29
 
25
- attr_reader :definition
30
+ attr_reader :rails_app
26
31
 
27
- def initialize(definition)
28
- @definition = definition
32
+ def initialize(rails_app)
33
+ @rails_app = rails_app
29
34
  end
30
35
 
31
36
  def database
32
37
  @database ||= self.class
33
38
  .get_adapter(adapter)
34
- .new(definition, environment, db_configs)
39
+ .new(rails_app, environment, db_configs)
35
40
  end
36
41
 
37
42
  private
@@ -45,11 +50,27 @@ module Kuby
45
50
  end
46
51
 
47
52
  def environment
48
- @environment ||= definition.environment
53
+ @environment ||= rails_app.definition.environment.name
49
54
  end
50
55
 
51
56
  def db_configs
52
- @db_configs ||= definition.app.config.database_configuration
57
+ @db_configs ||= YAML.load(ERB.new(File.read(db_config_path)).result)
58
+ end
59
+
60
+ def db_config_path
61
+ @db_config_path ||= begin
62
+ db_config_paths.first or
63
+ raise "Couldn't find database config at #{rails_app.root}"
64
+ end
65
+ end
66
+
67
+ def db_config_paths
68
+ @db_config_paths ||=
69
+ Dir.glob(
70
+ File.join(
71
+ rails_app.root, 'config', 'database.{yml,erb,yml.erb,yaml,yaml.erb}'
72
+ )
73
+ )
53
74
  end
54
75
  end
55
76
  end
@@ -6,15 +6,35 @@ class KubyGenerator < Rails::Generators::Base
6
6
  initializer(
7
7
  'kuby.rb',
8
8
  <<~END
9
+ require 'kuby'
10
+
11
+ Kuby.load!
12
+ END
13
+ )
14
+ end
15
+
16
+ def create_config_file
17
+ create_file(
18
+ 'kuby.rb',
19
+ <<~END
20
+ require 'active_support/encrypted_configuration'
21
+
9
22
  # Define a production Kuby deploy environment
10
23
  Kuby.define(:production) do
24
+ app_creds = ActiveSupport::EncryptedConfiguration.new(
25
+ config_path: File.join('config', 'credentials.yml.enc'),
26
+ key_path: File.join('config', 'master.key'),
27
+ env_key: 'RAILS_MASTER_KEY',
28
+ raise_if_missing_key: true
29
+ )
30
+
11
31
  docker do
12
32
  # Configure your Docker registry credentials here. Add them to your
13
33
  # Rails credentials file by running `bundle exec rake credentials:edit`.
14
34
  credentials do
15
- username Rails.application.credentials[:KUBY_DOCKER_USERNAME]
16
- password Rails.application.credentials[:KUBY_DOCKER_PASSWORD]
17
- email Rails.application.credentials[:KUBY_DOCKER_EMAIL]
35
+ username app_creds[:KUBY_DOCKER_USERNAME]
36
+ password app_creds[:KUBY_DOCKER_PASSWORD]
37
+ email app_creds[:KUBY_DOCKER_EMAIL]
18
38
  end
19
39
 
20
40
  # Configure the URL to your Docker image here, eg:
@@ -28,7 +48,8 @@ class KubyGenerator < Rails::Generators::Base
28
48
  # Add a plugin that facilitates deploying a Rails app.
29
49
  add_plugin :rails_app
30
50
 
31
- # Use minikube as the default provider.
51
+ # Use minikube as the provider, which is the default installed by
52
+ # Docker Desktop.
32
53
  # See: https://github.com/kubernetes/minikube
33
54
  #
34
55
  # Note: you will likely want to use a different provider when deploying
@@ -41,4 +62,22 @@ class KubyGenerator < Rails::Generators::Base
41
62
  END
42
63
  )
43
64
  end
65
+
66
+ def create_dockerignore
67
+ create_file(
68
+ '.dockerignore',
69
+ <<~END
70
+ .bundle/
71
+ vendor/bundle
72
+ node_modules/
73
+ .node_modules/
74
+ **/.git*
75
+ tmp/
76
+ log/
77
+ engines/**/log/
78
+ engines/**/tmp/
79
+ public/assets
80
+ END
81
+ )
82
+ end
44
83
  end
@@ -56,6 +56,20 @@ module Kuby
56
56
  end
57
57
  end
58
58
 
59
+ def storage(amount)
60
+ database do
61
+ spec do
62
+ storage do
63
+ resources do
64
+ requests do
65
+ set :storage, amount
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
59
73
  def secret(&block)
60
74
  context = self
61
75
 
@@ -118,10 +132,6 @@ module Kuby
118
132
  definition.kubernetes
119
133
  end
120
134
 
121
- def app
122
- definition.app
123
- end
124
-
125
135
  private
126
136
 
127
137
  def config
@@ -13,12 +13,17 @@ module Kuby
13
13
  ENV_SECRETS = [MASTER_KEY_VAR].freeze
14
14
  ENV_EXCLUDE = ['RAILS_ENV'].freeze
15
15
 
16
- value_fields :hostname, :tls_enabled, :database, :replicas
16
+ value_field :root, default: '.'
17
+ value_fields :hostname, :tls_enabled
18
+ value_fields :manage_database, :database, :replicas
19
+
20
+ alias_method :manage_database?, :manage_database
17
21
 
18
22
  def initialize(definition)
19
23
  @definition = definition
20
24
  @tls_enabled = true
21
25
  @replicas = 1
26
+ @manage_database = true
22
27
  end
23
28
 
24
29
  def configure(&block)
@@ -26,11 +31,7 @@ module Kuby
26
31
  end
27
32
 
28
33
  def after_configuration
29
- # currently Database.get doesn't return nil, but this if statement
30
- # is here as a placeholder to indicate we'd like to be able to
31
- # handle Rails apps that don't use a database, i.e. don't have
32
- # activerecord configured
33
- if @database = Database.get(definition)
34
+ if @database = Database.get(self)
34
35
  definition.kubernetes.plugins[database] = @database
35
36
  definition.kubernetes.add_plugin(:kube_db)
36
37
 
@@ -43,7 +44,11 @@ module Kuby
43
44
  definition.kubernetes.add_plugin(:nginx_ingress)
44
45
 
45
46
  if @tls_enabled
46
- definition.kubernetes.add_plugin(:cert_manager)
47
+ context = self
48
+
49
+ definition.kubernetes.add_plugin(:cert_manager) do
50
+ email context.definition.docker.credentials.email
51
+ end
47
52
  end
48
53
  end
49
54
 
@@ -178,9 +183,9 @@ module Kuby
178
183
  if master_key = ENV[MASTER_KEY_VAR]
179
184
  add MASTER_KEY_VAR.to_sym, master_key
180
185
  else
181
- master_key_path = spec.app.root.join('config', 'master.key')
186
+ master_key_path = File.join(spec.root, 'config', 'master.key')
182
187
 
183
- if master_key_path.exist?
188
+ if File.exist?(master_key_path)
184
189
  add MASTER_KEY_VAR.to_sym, File.read(master_key_path).strip
185
190
  end
186
191
  end
@@ -344,7 +349,7 @@ module Kuby
344
349
  app_secrets,
345
350
  deployment,
346
351
  ingress,
347
- *database.resources
352
+ *database&.resources
348
353
  ]
349
354
  end
350
355
 
@@ -364,10 +369,6 @@ module Kuby
364
369
  definition.kubernetes
365
370
  end
366
371
 
367
- def app
368
- definition.app
369
- end
370
-
371
372
  def namespace
372
373
  definition.kubernetes.namespace
373
374
  end
@@ -1,10 +1,142 @@
1
+ require 'kube-dsl'
2
+ require 'kuby/kube-db'
3
+
1
4
  module Kuby
2
5
  module Kubernetes
3
6
  module Plugins
4
7
  module RailsApp
5
8
  class Postgres < Kuby::Kubernetes::Plugin
9
+ ROLE = 'web'.freeze
10
+
11
+ attr_reader :definition, :environment, :configs
12
+
13
+ def initialize(definition, environment, configs)
14
+ @definition = definition
15
+ @environment = environment
16
+ @configs = configs
17
+
18
+ user(config['username'])
19
+ password(config['password'])
20
+ end
21
+
22
+ def resources
23
+ @resources ||= [secret, database]
24
+ end
25
+
26
+ def after_configuration
27
+ definition.docker.package_phase.add(:postgres_dev)
28
+ definition.docker.package_phase.add(:postgres_client)
29
+ end
30
+
31
+ def host
32
+ # host is the same as the name thanks to k8s DNS
33
+ @host ||= database.metadata.name
34
+ end
35
+
36
+ def rewritten_configs
37
+ # deep dup
38
+ @rewritten_configs ||= Marshal.load(Marshal.dump(configs)).tap do |new_configs|
39
+ new_configs[environment]['host'] = host
40
+ end
41
+ end
42
+
43
+ def user(user)
44
+ secret do
45
+ data do
46
+ set :POSTGRES_USER, user
47
+ end
48
+ end
49
+ end
50
+
51
+ def password(password)
52
+ secret do
53
+ data do
54
+ set :POSTGRES_PASSWORD, password
55
+ end
56
+ end
57
+ end
58
+
59
+ def secret(&block)
60
+ context = self
61
+
62
+ @secret ||= KubeDSL.secret do
63
+ metadata do
64
+ name "#{context.base_name}-postgres-secret"
65
+ namespace context.kubernetes.namespace.metadata.name
66
+ end
67
+
68
+ type 'Opaque'
69
+ end
70
+
71
+ @secret.instance_eval(&block) if block
72
+ @secret
73
+ end
74
+
75
+ def database(&block)
76
+ context = self
77
+
78
+ @database ||= Kuby::KubeDB.postgres do
79
+ api_version 'kubedb.com/v1alpha1'
80
+
81
+ metadata do
82
+ name "#{context.base_name}-postgres"
83
+ namespace context.kubernetes.namespace.metadata.name
84
+ end
85
+
86
+ spec do
87
+ database_secret do
88
+ secret_name context.secret.metadata.name
89
+ end
90
+
91
+ version '11.2'
92
+ standby_mode 'Hot'
93
+ streaming_mode 'asynchronous'
94
+ storage_type 'Durable'
95
+
96
+ storage do
97
+ storage_class_name context.kubernetes.provider.storage_class_name
98
+ access_modes ['ReadWriteOnce']
99
+
100
+ resources do
101
+ requests do
102
+ add :storage, '10Gi'
103
+ end
104
+ end
105
+ end
106
+
107
+ termination_policy 'DoNotTerminate'
108
+ end
109
+ end
110
+
111
+ @database.instance_eval(&block) if block
112
+ @database
113
+ end
114
+
115
+ def base_name
116
+ @base_name ||= "#{kubernetes.selector_app}-#{ROLE}"
117
+ end
118
+
119
+ def kubernetes
120
+ definition.kubernetes
121
+ end
122
+
123
+ private
124
+
125
+ def config
126
+ configs[environment]
127
+ end
6
128
  end
7
129
  end
8
130
  end
9
131
  end
10
132
  end
133
+
134
+ Kuby.register_package(:postgres_dev,
135
+ debian: 'postgresql-client',
136
+ alpine: 'postgresql-dev'
137
+ )
138
+
139
+ Kuby.register_package(:postgres_client,
140
+ debian: 'postgresql-client',
141
+ alpine: 'postgresql-client'
142
+ )
@@ -3,8 +3,28 @@ module Kuby
3
3
  module Plugins
4
4
  module RailsApp
5
5
  class Sqlite < Kuby::Kubernetes::Plugin
6
+ attr_reader :definition
7
+
8
+ def initialize(definition, *)
9
+ @definition = definition
10
+ end
11
+
12
+ def after_configuration
13
+ definition.docker.package_phase.add(:sqlite_dev)
14
+ definition.docker.package_phase.add(:sqlite_client)
15
+ end
6
16
  end
7
17
  end
8
18
  end
9
19
  end
10
20
  end
21
+
22
+ Kuby.register_package(:sqlite_dev,
23
+ debian: 'libsqlite3-dev',
24
+ alpine: 'sqlite-dev'
25
+ )
26
+
27
+ Kuby.register_package(:sqlite_client,
28
+ debian: 'sqlite3',
29
+ alpine: 'sqlite'
30
+ )
@@ -3,11 +3,16 @@ require 'rake'
3
3
  namespace :kuby do
4
4
  namespace :rails_app do
5
5
  namespace :db do
6
- task rewrite_config: :environment do
7
- config_file = Kuby.definition.app.root.join('config', 'database.yml')
6
+ task :rewrite_config do
7
+ Kuby.load!
8
+
9
+ config_file = File.join(Kuby.definition.kubernetes.plugin(:rails_app).root, 'config', 'database.yml')
8
10
  database = Kuby.definition.kubernetes.plugin(:rails_app).database
9
- File.write(config_file, YAML.dump(database.rewritten_configs))
10
- Kuby.logger.info("Wrote #{config_file}")
11
+
12
+ if database.respond_to?(:rewritten_configs)
13
+ File.write(config_file, YAML.dump(database.rewritten_configs))
14
+ Kuby.logger.info("Wrote #{config_file}")
15
+ end
11
16
  end
12
17
 
13
18
  task :create_unless_exists do
@@ -118,7 +118,7 @@ module Kuby
118
118
 
119
119
  @namespace ||= KubeDSL.namespace do
120
120
  metadata do
121
- name "#{spec.selector_app}-#{spec.definition.environment}"
121
+ name "#{spec.selector_app}-#{spec.definition.environment.name}"
122
122
  end
123
123
  end
124
124
 
@@ -7,10 +7,6 @@ module Kuby
7
7
  load File.expand_path(File.join('tasks', 'kuby.rake'), __dir__)
8
8
  end
9
9
 
10
- initializer 'kuby.startup' do |_app|
11
- Kuby.logger = Kuby::BasicLogger.new(STDERR)
12
- end
13
-
14
10
  initializer 'kuby.health_check_middleware' do |app|
15
11
  app.middleware.use Kuby::Middleware::HealthCheck
16
12
  end
@@ -16,6 +16,10 @@ module Kuby
16
16
  puts formatter.format(tokens)
17
17
  end
18
18
 
19
+ def setup
20
+ definition.kubernetes.setup
21
+ end
22
+
19
23
  def build
20
24
  docker.cli.build(
21
25
  dockerfile: docker.to_dockerfile,
@@ -49,6 +53,14 @@ module Kuby
49
53
  end
50
54
  end
51
55
 
56
+ def deploy
57
+ definition.kubernetes.deploy
58
+ end
59
+
60
+ def rollback
61
+ definition.kubernetes.rollback
62
+ end
63
+
52
64
  def print_resources
53
65
  kubernetes.before_deploy
54
66
 
@@ -2,67 +2,68 @@ require 'shellwords'
2
2
 
3
3
  namespace :kuby do
4
4
  def tasks
5
+ Kuby.load!
5
6
  @tasks ||= Kuby::Tasks.new(Kuby.definition)
6
7
  end
7
8
 
8
- task dockerfile: :environment do
9
+ task :dockerfile do
9
10
  tasks.print_dockerfile
10
11
  end
11
12
 
12
- task build: :environment do
13
+ task :build do
13
14
  tasks.build
14
15
  end
15
16
 
16
- task run: :environment do
17
+ task :run do
17
18
  tasks.run
18
19
  end
19
20
 
20
- task push: :environment do
21
+ task :push do
21
22
  tasks.push
22
23
  end
23
24
 
24
- task resources: :environment do
25
+ task :resources do
25
26
  tasks.print_resources
26
27
  end
27
28
 
28
- task :kubectl, [:cmd] => [:environment] do |_, args|
29
+ task :kubectl, [:cmd] do |_, args|
29
30
  tasks.kubectl(Shellwords.shellsplit(args[:cmd]))
30
31
  end
31
32
 
32
- task deploy: :environment do
33
- Kuby.definition.kubernetes.deploy
33
+ task :deploy do
34
+ tasks.deploy
34
35
  end
35
36
 
36
- task rollback: :environment do
37
- Kuby.definition.kubernetes.rollback
37
+ task :rollback do
38
+ tasks.rollback
38
39
  end
39
40
 
40
- task kubeconfig: :environment do
41
+ task :kubeconfig do
41
42
  tasks.print_kubeconfig
42
43
  end
43
44
 
44
- task setup: :environment do
45
- Kuby.definition.kubernetes.setup
45
+ task :setup do
46
+ tasks.setup
46
47
  end
47
48
 
48
49
  namespace :remote do
49
- task logs: :environment do
50
+ task :logs do
50
51
  tasks.remote_logs
51
52
  end
52
53
 
53
- task status: :environment do
54
+ task :status do
54
55
  tasks.remote_status
55
56
  end
56
57
 
57
- task shell: :environment do
58
+ task :shell do
58
59
  tasks.remote_shell
59
60
  end
60
61
 
61
- task console: :environment do
62
+ task :console do
62
63
  tasks.remote_console
63
64
  end
64
65
 
65
- task dbconsole: :environment do
66
+ task :dbconsole do
66
67
  tasks.remote_dbconsole
67
68
  end
68
69
  end
@@ -1,3 +1,3 @@
1
1
  module Kuby
2
- VERSION = '0.5.0'
2
+ VERSION = '0.6.0'
3
3
  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.5.0
4
+ version: 0.6.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: 2020-07-31 00:00:00.000000000 Z
11
+ date: 2020-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0.1'
61
+ version: '0.2'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0.1'
68
+ version: '0.2'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: kube-dsl
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -164,6 +164,8 @@ files:
164
164
  - lib/kuby/docker/debian.rb
165
165
  - lib/kuby/docker/dockerfile.rb
166
166
  - lib/kuby/docker/errors.rb
167
+ - lib/kuby/docker/inline_layer.rb
168
+ - lib/kuby/docker/layer.rb
167
169
  - lib/kuby/docker/layer_stack.rb
168
170
  - lib/kuby/docker/local_tags.rb
169
171
  - lib/kuby/docker/metadata.rb
@@ -175,7 +177,6 @@ files:
175
177
  - lib/kuby/docker/packages/package.rb
176
178
  - lib/kuby/docker/packages/simple_managed_package.rb
177
179
  - lib/kuby/docker/packages/yarn.rb
178
- - lib/kuby/docker/phase.rb
179
180
  - lib/kuby/docker/remote_tags.rb
180
181
  - lib/kuby/docker/setup_phase.rb
181
182
  - lib/kuby/docker/spec.rb
@@ -183,6 +184,7 @@ files:
183
184
  - lib/kuby/docker/timestamp_tag.rb
184
185
  - lib/kuby/docker/webserver_phase.rb
185
186
  - lib/kuby/docker/yarn_phase.rb
187
+ - lib/kuby/environment.rb
186
188
  - lib/kuby/kubernetes.rb
187
189
  - lib/kuby/kubernetes/deploy_task.rb
188
190
  - lib/kuby/kubernetes/deployer.rb