kuby-core 0.1.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -0
  3. data/Gemfile +0 -4
  4. data/README.md +3 -160
  5. data/kuby-core.gemspec +6 -5
  6. data/lib/ext/krane/kubernetes_resource.rb +16 -0
  7. data/lib/kuby.rb +32 -17
  8. data/lib/kuby/definition.rb +22 -16
  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/errors.rb +0 -12
  31. data/lib/kuby/kubernetes/minikube_provider.rb +5 -5
  32. data/lib/kuby/kubernetes/plugins/nginx_ingress.rb +12 -0
  33. data/lib/kuby/kubernetes/plugins/rails_app/database.rb +30 -9
  34. data/lib/kuby/kubernetes/plugins/rails_app/generators/kuby.rb +83 -0
  35. data/lib/kuby/kubernetes/plugins/rails_app/mysql.rb +17 -5
  36. data/lib/kuby/kubernetes/plugins/rails_app/plugin.rb +28 -42
  37. data/lib/kuby/kubernetes/plugins/rails_app/postgres.rb +132 -0
  38. data/lib/kuby/kubernetes/plugins/rails_app/sqlite.rb +20 -0
  39. data/lib/kuby/kubernetes/plugins/rails_app/tasks.rake +9 -4
  40. data/lib/kuby/kubernetes/spec.rb +52 -37
  41. data/lib/kuby/railtie.rb +0 -4
  42. data/lib/kuby/tasks.rb +18 -0
  43. data/lib/kuby/tasks/kuby.rake +24 -17
  44. data/lib/kuby/version.rb +1 -1
  45. metadata +25 -19
@@ -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]
@@ -1,17 +1,5 @@
1
1
  module Kuby
2
2
  module Kubernetes
3
- class KubernetesCLIError < StandardError; end
4
-
5
- class InvalidResourceError < KubernetesCLIError
6
- attr_accessor :resource
7
- end
8
-
9
- class InvalidResourceUriError < KubernetesCLIError
10
- attr_accessor :resource_uri
11
- end
12
-
13
- class GetResourceError < KubernetesCLIError; end
14
-
15
3
  class MissingDeploymentError < StandardError; end
16
4
  class MissingProviderError < StandardError; end
17
5
  class MissingPluginError < StandardError; 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