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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +2 -0
- data/Gemfile +16 -0
- data/LICENSE +21 -0
- data/README.md +186 -0
- data/Rakefile +14 -0
- data/kuby-core.gemspec +27 -0
- data/lib/kuby.rb +112 -0
- data/lib/kuby/basic_logger.rb +22 -0
- data/lib/kuby/cli_base.rb +75 -0
- data/lib/kuby/definition.rb +29 -0
- data/lib/kuby/docker.rb +27 -0
- data/lib/kuby/docker/alpine.rb +62 -0
- data/lib/kuby/docker/assets_phase.rb +11 -0
- data/lib/kuby/docker/bundler_phase.rb +56 -0
- data/lib/kuby/docker/cli.rb +72 -0
- data/lib/kuby/docker/copy_phase.rb +23 -0
- data/lib/kuby/docker/credentials.rb +11 -0
- data/lib/kuby/docker/debian.rb +66 -0
- data/lib/kuby/docker/dockerfile.rb +128 -0
- data/lib/kuby/docker/errors.rb +22 -0
- data/lib/kuby/docker/layer_stack.rb +42 -0
- data/lib/kuby/docker/local_tags.rb +38 -0
- data/lib/kuby/docker/metadata.rb +69 -0
- data/lib/kuby/docker/package_list.rb +34 -0
- data/lib/kuby/docker/package_phase.rb +59 -0
- data/lib/kuby/docker/packages.rb +10 -0
- data/lib/kuby/docker/packages/managed_package.rb +29 -0
- data/lib/kuby/docker/packages/nodejs.rb +29 -0
- data/lib/kuby/docker/packages/package.rb +22 -0
- data/lib/kuby/docker/packages/yarn.rb +47 -0
- data/lib/kuby/docker/phase.rb +21 -0
- data/lib/kuby/docker/remote_tags.rb +24 -0
- data/lib/kuby/docker/setup_phase.rb +29 -0
- data/lib/kuby/docker/spec.rb +147 -0
- data/lib/kuby/docker/tags.rb +39 -0
- data/lib/kuby/docker/timestamp_tag.rb +36 -0
- data/lib/kuby/docker/webserver_phase.rb +51 -0
- data/lib/kuby/docker/yarn_phase.rb +11 -0
- data/lib/kuby/kubernetes.rb +16 -0
- data/lib/kuby/kubernetes/deployer.rb +94 -0
- data/lib/kuby/kubernetes/docker_config.rb +27 -0
- data/lib/kuby/kubernetes/errors.rb +22 -0
- data/lib/kuby/kubernetes/manifest.rb +56 -0
- data/lib/kuby/kubernetes/minikube_provider.rb +51 -0
- data/lib/kuby/kubernetes/plugin.rb +55 -0
- data/lib/kuby/kubernetes/plugins.rb +8 -0
- data/lib/kuby/kubernetes/plugins/nginx_ingress.rb +61 -0
- data/lib/kuby/kubernetes/plugins/rails_app.rb +16 -0
- data/lib/kuby/kubernetes/plugins/rails_app/database.rb +58 -0
- data/lib/kuby/kubernetes/plugins/rails_app/mysql.rb +142 -0
- data/lib/kuby/kubernetes/plugins/rails_app/plugin.rb +393 -0
- data/lib/kuby/kubernetes/plugins/rails_app/postgres.rb +10 -0
- data/lib/kuby/kubernetes/plugins/rails_app/rewrite_db_config.rb +13 -0
- data/lib/kuby/kubernetes/plugins/rails_app/sqlite.rb +10 -0
- data/lib/kuby/kubernetes/plugins/rails_app/tasks.rake +23 -0
- data/lib/kuby/kubernetes/provider.rb +77 -0
- data/lib/kuby/kubernetes/registry_secret.rb +26 -0
- data/lib/kuby/kubernetes/spec.rb +152 -0
- data/lib/kuby/middleware.rb +5 -0
- data/lib/kuby/middleware/health_check.rb +16 -0
- data/lib/kuby/railtie.rb +18 -0
- data/lib/kuby/tasks.rb +135 -0
- data/lib/kuby/tasks/kuby.rake +63 -0
- data/lib/kuby/trailing_hash.rb +19 -0
- data/lib/kuby/version.rb +3 -0
- 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
|
data/lib/kuby/docker.rb
ADDED
@@ -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,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,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
|