kuby-core 0.2.0 → 0.6.1

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -0
  3. data/Gemfile +0 -4
  4. data/README.md +3 -160
  5. data/kuby-core.gemspec +5 -4
  6. data/lib/ext/krane/kubernetes_resource.rb +16 -0
  7. data/lib/kuby.rb +32 -17
  8. data/lib/kuby/definition.rb +20 -14
  9. data/lib/kuby/docker.rb +2 -1
  10. data/lib/kuby/docker/alpine.rb +0 -1
  11. data/lib/kuby/docker/assets_phase.rb +1 -1
  12. data/lib/kuby/docker/bundler_phase.rb +4 -2
  13. data/lib/kuby/docker/copy_phase.rb +1 -1
  14. data/lib/kuby/docker/inline_layer.rb +15 -0
  15. data/lib/kuby/docker/{phase.rb → layer.rb} +6 -5
  16. data/lib/kuby/docker/layer_stack.rb +30 -4
  17. data/lib/kuby/docker/metadata.rb +27 -1
  18. data/lib/kuby/docker/package_phase.rb +1 -1
  19. data/lib/kuby/docker/packages.rb +5 -4
  20. data/lib/kuby/docker/packages/simple_managed_package.rb +25 -0
  21. data/lib/kuby/docker/setup_phase.rb +1 -1
  22. data/lib/kuby/docker/spec.rb +4 -4
  23. data/lib/kuby/docker/tags.rb +18 -0
  24. data/lib/kuby/docker/webserver_phase.rb +1 -1
  25. data/lib/kuby/docker/yarn_phase.rb +1 -1
  26. data/lib/kuby/environment.rb +22 -0
  27. data/lib/kuby/kubernetes.rb +1 -0
  28. data/lib/kuby/kubernetes/deploy_task.rb +33 -0
  29. data/lib/kuby/kubernetes/deployer.rb +1 -2
  30. data/lib/kuby/kubernetes/minikube_provider.rb +5 -5
  31. data/lib/kuby/kubernetes/plugins/nginx_ingress.rb +12 -0
  32. data/lib/kuby/kubernetes/plugins/rails_app/database.rb +30 -9
  33. data/lib/kuby/kubernetes/plugins/rails_app/generators/kuby.rb +83 -0
  34. data/lib/kuby/kubernetes/plugins/rails_app/mysql.rb +17 -5
  35. data/lib/kuby/kubernetes/plugins/rails_app/plugin.rb +28 -42
  36. data/lib/kuby/kubernetes/plugins/rails_app/postgres.rb +132 -0
  37. data/lib/kuby/kubernetes/plugins/rails_app/sqlite.rb +20 -0
  38. data/lib/kuby/kubernetes/plugins/rails_app/tasks.rake +9 -4
  39. data/lib/kuby/kubernetes/spec.rb +52 -37
  40. data/lib/kuby/railtie.rb +0 -4
  41. data/lib/kuby/tasks.rb +18 -0
  42. data/lib/kuby/tasks/kuby.rake +24 -17
  43. data/lib/kuby/version.rb +1 -1
  44. metadata +21 -15
@@ -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
@@ -40,6 +40,32 @@ module Kuby
40
40
  @tags.empty? ? default_tags : @tags
41
41
  end
42
42
 
43
+ def tag
44
+ t = ENV.fetch('KUBY_DOCKER_TAG') do
45
+ definition.docker.tags.latest_timestamp_tag
46
+ end
47
+
48
+ unless t
49
+ raise MissingTagError, 'could not find latest timestamped tag'
50
+ end
51
+
52
+ t.to_s
53
+ end
54
+
55
+ def previous_tag(current_tag)
56
+ t = definition.docker.tags.previous_timestamp_tag(current_tag)
57
+
58
+ unless t
59
+ raise MissingTagError, 'could not find previous timestamped tag'
60
+ end
61
+
62
+ t.to_s
63
+ end
64
+
65
+ def distro
66
+ @distro || DEFAULT_DISTRO
67
+ end
68
+
43
69
  def distro=(distro_name)
44
70
  @distro = distro_name
45
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,10 +1,11 @@
1
1
  module Kuby
2
2
  module Docker
3
3
  module Packages
4
- autoload :ManagedPackage, 'kuby/docker/packages/managed_package'
5
- autoload :Nodejs, 'kuby/docker/packages/nodejs'
6
- autoload :Package, 'kuby/docker/packages/package'
7
- autoload :Yarn, 'kuby/docker/packages/yarn'
4
+ autoload :ManagedPackage, 'kuby/docker/packages/managed_package'
5
+ autoload :Nodejs, 'kuby/docker/packages/nodejs'
6
+ autoload :Package, 'kuby/docker/packages/package'
7
+ autoload :SimpleManagedPackage, 'kuby/docker/packages/simple_managed_package'
8
+ autoload :Yarn, 'kuby/docker/packages/yarn'
8
9
  end
9
10
  end
10
11
  end
@@ -0,0 +1,25 @@
1
+ module Kuby
2
+ module Docker
3
+ module Packages
4
+ class SimpleManagedPackage
5
+ attr_reader :name
6
+
7
+ def initialize(name)
8
+ @name = name
9
+ end
10
+
11
+ def package_name_for(distro)
12
+ name
13
+ end
14
+
15
+ def with_version(*)
16
+ self
17
+ end
18
+
19
+ def managed?
20
+ true
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -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)
@@ -19,10 +19,28 @@ module Kuby
19
19
  (local.latest_tags + remote.latest_tags).uniq
20
20
  end
21
21
 
22
+ def previous_timestamp_tag(current_tag)
23
+ current_tag = ::Kuby::Docker::TimestampTag.try_parse(current_tag)
24
+ all_tags = timestamp_tags.sort
25
+
26
+ idx = all_tags.index do |tag|
27
+ tag.time == current_tag.time
28
+ end
29
+
30
+ idx ||= 0
31
+ return nil unless idx > 0
32
+
33
+ all_tags[idx - 1]
34
+ end
35
+
22
36
  def timestamp_tags
23
37
  (local.timestamp_tags + remote.timestamp_tags).uniq
24
38
  end
25
39
 
40
+ def latest_timestamp_tag
41
+ @latest_timestamp_tag ||= timestamp_tags.sort.last
42
+ end
43
+
26
44
  def all
27
45
  self
28
46
  end
@@ -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
@@ -4,6 +4,7 @@ module Kuby
4
4
  module Kubernetes
5
5
  autoload :MinikubeProvider, 'kuby/kubernetes/minikube_provider'
6
6
  autoload :Deployer, 'kuby/kubernetes/deployer'
7
+ autoload :DeployTask, 'kuby/kubernetes/deploy_task'
7
8
  autoload :DockerConfig, 'kuby/kubernetes/docker_config'
8
9
  autoload :Manifest, 'kuby/kubernetes/manifest'
9
10
  autoload :Monitors, 'kuby/kubernetes/monitors'
@@ -0,0 +1,33 @@
1
+ require 'krane'
2
+ require 'ext/krane/kubernetes_resource'
3
+ require 'kubectl-rb'
4
+
5
+ module Kuby
6
+ module Kubernetes
7
+ class DeployTask
8
+ attr_reader :deploy_task
9
+
10
+ def initialize(**kwargs)
11
+ @deploy_task ||= ::Krane::DeployTask.new(**kwargs)
12
+ end
13
+
14
+ def run!(**kwargs)
15
+ new_path = "#{File.dirname(KubectlRb.executable)}:#{ENV['PATH']}"
16
+
17
+ with_env('PATH' => new_path) do
18
+ deploy_task.run!(**kwargs)
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def with_env(new_env)
25
+ old_env = ENV.to_h
26
+ ENV.replace(old_env.merge(new_env))
27
+ yield
28
+ ensure
29
+ ENV.replace(old_env)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,5 +1,4 @@
1
1
  require 'fileutils'
2
- require 'krane'
3
2
  require 'securerandom'
4
3
  require 'yaml'
5
4
 
@@ -62,7 +61,7 @@ module Kuby
62
61
  File.write(resource_path, resource.to_resource.to_yaml)
63
62
  end
64
63
 
65
- task = ::Krane::DeployTask.new(
64
+ task = ::Kuby::Kubernetes::DeployTask.new(
66
65
  namespace: namespace.metadata.name,
67
66
  context: cli.current_context,
68
67
  filenames: [tmpdir]
@@ -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