kuby-core 0.1.0

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 (67) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2 -0
  3. data/Gemfile +16 -0
  4. data/LICENSE +21 -0
  5. data/README.md +186 -0
  6. data/Rakefile +14 -0
  7. data/kuby-core.gemspec +27 -0
  8. data/lib/kuby.rb +112 -0
  9. data/lib/kuby/basic_logger.rb +22 -0
  10. data/lib/kuby/cli_base.rb +75 -0
  11. data/lib/kuby/definition.rb +29 -0
  12. data/lib/kuby/docker.rb +27 -0
  13. data/lib/kuby/docker/alpine.rb +62 -0
  14. data/lib/kuby/docker/assets_phase.rb +11 -0
  15. data/lib/kuby/docker/bundler_phase.rb +56 -0
  16. data/lib/kuby/docker/cli.rb +72 -0
  17. data/lib/kuby/docker/copy_phase.rb +23 -0
  18. data/lib/kuby/docker/credentials.rb +11 -0
  19. data/lib/kuby/docker/debian.rb +66 -0
  20. data/lib/kuby/docker/dockerfile.rb +128 -0
  21. data/lib/kuby/docker/errors.rb +22 -0
  22. data/lib/kuby/docker/layer_stack.rb +42 -0
  23. data/lib/kuby/docker/local_tags.rb +38 -0
  24. data/lib/kuby/docker/metadata.rb +69 -0
  25. data/lib/kuby/docker/package_list.rb +34 -0
  26. data/lib/kuby/docker/package_phase.rb +59 -0
  27. data/lib/kuby/docker/packages.rb +10 -0
  28. data/lib/kuby/docker/packages/managed_package.rb +29 -0
  29. data/lib/kuby/docker/packages/nodejs.rb +29 -0
  30. data/lib/kuby/docker/packages/package.rb +22 -0
  31. data/lib/kuby/docker/packages/yarn.rb +47 -0
  32. data/lib/kuby/docker/phase.rb +21 -0
  33. data/lib/kuby/docker/remote_tags.rb +24 -0
  34. data/lib/kuby/docker/setup_phase.rb +29 -0
  35. data/lib/kuby/docker/spec.rb +147 -0
  36. data/lib/kuby/docker/tags.rb +39 -0
  37. data/lib/kuby/docker/timestamp_tag.rb +36 -0
  38. data/lib/kuby/docker/webserver_phase.rb +51 -0
  39. data/lib/kuby/docker/yarn_phase.rb +11 -0
  40. data/lib/kuby/kubernetes.rb +16 -0
  41. data/lib/kuby/kubernetes/deployer.rb +94 -0
  42. data/lib/kuby/kubernetes/docker_config.rb +27 -0
  43. data/lib/kuby/kubernetes/errors.rb +22 -0
  44. data/lib/kuby/kubernetes/manifest.rb +56 -0
  45. data/lib/kuby/kubernetes/minikube_provider.rb +51 -0
  46. data/lib/kuby/kubernetes/plugin.rb +55 -0
  47. data/lib/kuby/kubernetes/plugins.rb +8 -0
  48. data/lib/kuby/kubernetes/plugins/nginx_ingress.rb +61 -0
  49. data/lib/kuby/kubernetes/plugins/rails_app.rb +16 -0
  50. data/lib/kuby/kubernetes/plugins/rails_app/database.rb +58 -0
  51. data/lib/kuby/kubernetes/plugins/rails_app/mysql.rb +142 -0
  52. data/lib/kuby/kubernetes/plugins/rails_app/plugin.rb +393 -0
  53. data/lib/kuby/kubernetes/plugins/rails_app/postgres.rb +10 -0
  54. data/lib/kuby/kubernetes/plugins/rails_app/rewrite_db_config.rb +13 -0
  55. data/lib/kuby/kubernetes/plugins/rails_app/sqlite.rb +10 -0
  56. data/lib/kuby/kubernetes/plugins/rails_app/tasks.rake +23 -0
  57. data/lib/kuby/kubernetes/provider.rb +77 -0
  58. data/lib/kuby/kubernetes/registry_secret.rb +26 -0
  59. data/lib/kuby/kubernetes/spec.rb +152 -0
  60. data/lib/kuby/middleware.rb +5 -0
  61. data/lib/kuby/middleware/health_check.rb +16 -0
  62. data/lib/kuby/railtie.rb +18 -0
  63. data/lib/kuby/tasks.rb +135 -0
  64. data/lib/kuby/tasks/kuby.rake +63 -0
  65. data/lib/kuby/trailing_hash.rb +19 -0
  66. data/lib/kuby/version.rb +3 -0
  67. metadata +233 -0
@@ -0,0 +1,29 @@
1
+ module Kuby
2
+ class Definition
3
+ attr_reader :environment, :app
4
+
5
+ def initialize(environment, app, &block)
6
+ @environment = environment
7
+ @app = app
8
+
9
+ instance_eval(&block) if block
10
+ kubernetes.after_configuration
11
+ end
12
+
13
+ def docker(&block)
14
+ @docker ||= Docker::Spec.new(self)
15
+ @docker.instance_eval(&block) if block
16
+ @docker
17
+ end
18
+
19
+ def kubernetes(&block)
20
+ @kubernetes ||= Kubernetes::Spec.new(self)
21
+ @kubernetes.instance_eval(&block) if block
22
+ @kubernetes
23
+ end
24
+
25
+ def app_name
26
+ @app_name ||= app.class.module_parent.name
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,27 @@
1
+ require 'kuby/docker/errors'
2
+
3
+ module Kuby
4
+ module Docker
5
+ autoload :Alpine, 'kuby/docker/alpine'
6
+ autoload :AssetsPhase, 'kuby/docker/assets_phase'
7
+ autoload :BundlerPhase, 'kuby/docker/bundler_phase'
8
+ autoload :CLI, 'kuby/docker/cli'
9
+ autoload :CopyPhase, 'kuby/docker/copy_phase'
10
+ autoload :Credentials, 'kuby/docker/credentials'
11
+ autoload :Debian, 'kuby/docker/debian'
12
+ autoload :Dockerfile, 'kuby/docker/dockerfile'
13
+ autoload :LayerStack, 'kuby/docker/layer_stack'
14
+ autoload :LocalTags, 'kuby/docker/local_tags'
15
+ autoload :Metadata, 'kuby/docker/metadata'
16
+ autoload :Packages, 'kuby/docker/packages'
17
+ autoload :PackagePhase, 'kuby/docker/package_phase'
18
+ autoload :Phase, 'kuby/docker/phase'
19
+ autoload :RemoteTags, 'kuby/docker/remote_tags'
20
+ autoload :SetupPhase, 'kuby/docker/setup_phase'
21
+ autoload :Spec, 'kuby/docker/spec'
22
+ autoload :Tags, 'kuby/docker/tags'
23
+ autoload :TimestampTag, 'kuby/docker/timestamp_tag'
24
+ autoload :WebserverPhase, 'kuby/docker/webserver_phase'
25
+ autoload :YarnPhase, 'kuby/docker/yarn_phase'
26
+ end
27
+ end
@@ -0,0 +1,62 @@
1
+ module Kuby
2
+ module Docker
3
+ class Alpine
4
+ SHELL_EXE = '/bin/sh'.freeze
5
+
6
+ DEFAULT_PACKAGES = [
7
+ [:ca_certificates],
8
+ [:nodejs, '12.14.1'],
9
+ [:yarn, '1.21.1'],
10
+ [:c_toolchain],
11
+ [:sqlite_dev],
12
+ [:tzdata]
13
+ ].freeze
14
+
15
+ attr_reader :phase
16
+
17
+ def initialize(phase)
18
+ @phase = phase
19
+ end
20
+
21
+ def install(packages, into:)
22
+ dockerfile = into
23
+ install_managed(packages, dockerfile)
24
+ install_unmanaged(packages, dockerfile)
25
+ end
26
+
27
+ def default_packages
28
+ DEFAULT_PACKAGES
29
+ end
30
+
31
+ def shell_exe
32
+ SHELL_EXE
33
+ end
34
+
35
+ private
36
+
37
+ def install_managed(packages, dockerfile)
38
+ pkgs = packages.select(&:managed?)
39
+
40
+ unless pkgs.empty?
41
+ package_names = pkgs.map { |pkg| pkg.package_name_for(:alpine) }
42
+ dockerfile.run(
43
+ "apk add --no-cache #{package_names.join(' ')}"
44
+ )
45
+ end
46
+ end
47
+
48
+ def install_unmanaged(packages, dockerfile)
49
+ packages
50
+ .reject(&:managed?)
51
+ .each do |package|
52
+ if package.respond_to?(:install_on_alpine)
53
+ package.send(:install_on_alpine, dockerfile)
54
+ else
55
+ raise UnsupportedDistroError, "Couldn't install #{package.name} "\
56
+ "because alpine is an unsupported distro"
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,11 @@
1
+ module Kuby
2
+ module Docker
3
+ class AssetsPhase < Phase
4
+ def apply_to(dockerfile)
5
+ dockerfile.run(
6
+ 'bundle', 'exec', 'rake', 'assets:precompile'
7
+ )
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,56 @@
1
+ module Kuby
2
+ module Docker
3
+ class BundlerPhase < Phase
4
+ DEFAULT_WITHOUT = ['development', 'test', 'deploy'].freeze
5
+
6
+ attr_accessor :version, :gemfile, :without
7
+
8
+ def apply_to(dockerfile)
9
+ gf = gemfile || default_gemfile
10
+ lf = "#{gf}.lock"
11
+ v = version || default_version
12
+ wo = without || DEFAULT_WITHOUT
13
+
14
+ dockerfile.run("gem install bundler -v #{v}")
15
+
16
+ # bundle install
17
+ dockerfile.copy(gf, '.')
18
+ dockerfile.copy(lf, '.')
19
+
20
+ # set bundle path so docker will cache the bundle
21
+ dockerfile.run('mkdir ./bundle')
22
+ dockerfile.env('BUNDLE_PATH=./bundle')
23
+
24
+ unless wo.empty?
25
+ dockerfile.env("BUNDLE_WITHOUT='#{wo.join(' ')}'")
26
+ end
27
+
28
+ dockerfile.run(
29
+ 'bundle', 'install',
30
+ '--jobs', '$(nproc)',
31
+ '--retry', '3',
32
+ '--gemfile', gf
33
+ )
34
+
35
+ # generate binstubs and add the bin directory to our path
36
+ dockerfile.run('bundle binstubs --all')
37
+ dockerfile.env("PATH=./bin:$PATH")
38
+ end
39
+
40
+ private
41
+
42
+ def default_version
43
+ Bundler::VERSION
44
+ end
45
+
46
+ def default_gemfile
47
+ Bundler
48
+ .definition
49
+ .gemfiles
50
+ .first
51
+ .relative_path_from(app.root)
52
+ .to_s
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,72 @@
1
+ require 'json'
2
+ require 'open3'
3
+
4
+ module Kuby
5
+ module Docker
6
+ class CLI < CLIBase
7
+ attr_reader :executable
8
+
9
+ def initialize(executable = nil)
10
+ @executable = executable || `which docker`.strip
11
+ end
12
+
13
+ def build(dockerfile:, image_url:, tags:)
14
+ cmd = [
15
+ executable, 'build',
16
+ *tags.flat_map { |tag| ['-t', "#{image_url}:#{tag}"] },
17
+ '-f-', '.'
18
+ ]
19
+
20
+ open3_w({}, cmd) do |stdin, _wait_threads|
21
+ stdin.puts(dockerfile.to_s)
22
+ end
23
+
24
+ unless last_status.success?
25
+ raise BuildError, 'build failed: docker command exited with '\
26
+ "status code #{last_status.exitstatus}"
27
+ end
28
+ end
29
+
30
+ def run(image_url:, tag: 'latest', env: {}, ports: [])
31
+ cmd = [
32
+ executable, 'run',
33
+ *env.flat_map { |k, v| ['-e', "#{k}=#{v}"] },
34
+ *ports.flat_map { |port| ['-p', "#{port}:#{port}"] },
35
+ '--init',
36
+ '--rm',
37
+ "#{image_url}:#{tag}"
38
+ ]
39
+
40
+ execc(cmd)
41
+ end
42
+
43
+ def images(image_url)
44
+ cmd = [
45
+ executable, 'images', image_url,
46
+ '--format', '"{{json . }}"'
47
+ ]
48
+
49
+ backticks(cmd).split("\n").map do |image_data|
50
+ JSON.parse(image_data).each_with_object({}) do |(k, v), ret|
51
+ ret[k.underscore.to_sym] = v
52
+ end
53
+ end
54
+ end
55
+
56
+ def push(image_url, tag)
57
+ systemm([
58
+ executable, 'push', "#{image_url}:#{tag}"
59
+ ])
60
+
61
+ unless last_status.success?
62
+ raise PushError, 'push failed: docker command exited with '\
63
+ "status code #{last_status.exitstatus}"
64
+ end
65
+ end
66
+
67
+ def status_key
68
+ :kuby_docker_cli_last_status
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,23 @@
1
+ module Kuby
2
+ module Docker
3
+ class CopyPhase < Phase
4
+ DEFAULT_PATHS = ['./'].freeze
5
+
6
+ attr_reader :paths
7
+
8
+ def initialize(*args)
9
+ super
10
+ @paths = []
11
+ end
12
+
13
+ def <<(path)
14
+ paths << path
15
+ end
16
+
17
+ def apply_to(dockerfile)
18
+ to_copy = paths.empty? ? DEFAULT_PATHS : paths
19
+ to_copy.each { |path| dockerfile.copy(path, '.') }
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,11 @@
1
+ require 'kube-dsl'
2
+
3
+ module Kuby
4
+ module Docker
5
+ class Credentials
6
+ extend ::KubeDSL::ValueFields
7
+
8
+ value_fields :username, :password, :email
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,66 @@
1
+ module Kuby
2
+ module Docker
3
+ class Debian
4
+ SHELL_EXE = '/bin/bash'
5
+
6
+ DEFAULT_PACKAGES = [
7
+ [:ca_certificates],
8
+ [:nodejs, '12.14.1'],
9
+ [:yarn, '1.21.1']
10
+ ].freeze
11
+
12
+ attr_reader :phase
13
+
14
+ def initialize(phase)
15
+ @phase = phase
16
+ end
17
+
18
+ def install(packages, into:)
19
+ dockerfile = into
20
+ install_managed(packages, dockerfile)
21
+ install_unmanaged(packages, dockerfile)
22
+ end
23
+
24
+ def default_packages
25
+ DEFAULT_PACKAGES
26
+ end
27
+
28
+ def shell_exe
29
+ SHELL_EXE
30
+ end
31
+
32
+ private
33
+
34
+ def install_managed(packages, dockerfile)
35
+ pkgs = packages.select(&:managed?)
36
+
37
+ pkg_cmd = <<~CMD.strip
38
+ apt-get update -qq && \\
39
+ DEBIAN_FRONTEND=noninteractive apt-get install -qq -y --no-install-recommends apt-transport-https && \\
40
+ DEBIAN_FRONTEND=noninteractive apt-get install -qq -y --no-install-recommends apt-utils && \\
41
+ DEBIAN_FRONTEND=noninteractive apt-get install -qq -y --no-install-recommends
42
+ CMD
43
+
44
+ unless pkgs.empty?
45
+ package_names = pkgs.map { |pkg| pkg.package_name_for(:debian) }
46
+ pkg_cmd << " \\\n #{package_names.join(" \\\n ")}"
47
+ end
48
+
49
+ dockerfile.run(pkg_cmd)
50
+ end
51
+
52
+ def install_unmanaged(packages, dockerfile)
53
+ packages
54
+ .reject(&:managed?)
55
+ .each do |package|
56
+ if package.respond_to?(:install_on_debian)
57
+ package.send(:install_on_debian, dockerfile)
58
+ else
59
+ raise UnsupportedDistroError, "Couldn't install #{package.name} "\
60
+ "because debian is an unsupported distro"
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,128 @@
1
+ module Kuby
2
+ module Docker
3
+ class Dockerfile
4
+ class Command
5
+ attr_reader :args
6
+
7
+ def initialize(*args)
8
+ @args = args
9
+ end
10
+
11
+ def to_s
12
+ args.join(' ')
13
+ end
14
+ end
15
+
16
+ class From < Command
17
+ attr_reader :image_url, :as
18
+
19
+ def initialize(image_url, as: nil)
20
+ @image_url = image_url
21
+ @as = as
22
+ end
23
+
24
+ def to_s
25
+ str = "FROM #{image_url}"
26
+ str << " AS #{as}" if as
27
+ str
28
+ end
29
+ end
30
+
31
+ class Workdir < Command
32
+ def to_s; "WORKDIR #{super}"; end
33
+ end
34
+
35
+ class Env < Command
36
+ def to_s; "ENV #{super}"; end
37
+ end
38
+
39
+ class Run < Command
40
+ def to_s; "RUN #{super}"; end
41
+ end
42
+
43
+ class Copy < Command
44
+ attr_reader :source, :dest, :from
45
+
46
+ def initialize(source, dest, from: nil)
47
+ @source = source
48
+ @dest = dest
49
+ @from = from
50
+ end
51
+
52
+ def to_s
53
+ cmd = ['COPY']
54
+ cmd << "--from=#{from}" if from
55
+ cmd += [source, dest]
56
+ cmd.join(' ')
57
+ end
58
+ end
59
+
60
+ class Expose < Command
61
+ def to_s; "EXPOSE #{super}"; end
62
+ end
63
+
64
+ class Cmd < Command
65
+ def to_s; "CMD #{super}"; end
66
+ end
67
+
68
+ attr_reader :commands, :cursor
69
+
70
+ def initialize
71
+ @commands = []
72
+ @cursor = 0
73
+ end
74
+
75
+ def from(*args, **kwargs)
76
+ add From.new(*args, **kwargs)
77
+ end
78
+
79
+ def workdir(*args)
80
+ add Workdir.new(*args)
81
+ end
82
+
83
+ def env(*args)
84
+ add Env.new(*args)
85
+ end
86
+
87
+ def run(*args)
88
+ add Run.new(*args)
89
+ end
90
+
91
+ def copy(*args, **kwargs)
92
+ add Copy.new(*args, **kwargs)
93
+ end
94
+
95
+ def expose(*args)
96
+ add Expose.new(*args)
97
+ end
98
+
99
+ def cmd(*args)
100
+ add Cmd.new(*args)
101
+ end
102
+
103
+ def to_s
104
+ commands.map(&:to_s).join("\n")
105
+ end
106
+
107
+ def exposed_ports
108
+ commands
109
+ .select { |c| c.is_a?(Expose) }
110
+ .map { |c| c.args.first }
111
+ end
112
+
113
+ def insert_at(pos)
114
+ @cursor = pos
115
+ yield
116
+ ensure
117
+ @cursor = commands.size
118
+ end
119
+
120
+ private
121
+
122
+ def add(cmd)
123
+ commands.insert(cursor, cmd)
124
+ @cursor += 1
125
+ end
126
+ end
127
+ end
128
+ end