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
|
+
module Docker
|
3
|
+
class SetupPhase < Phase
|
4
|
+
DEFAULT_WORKING_DIR = '/usr/src/app'.freeze
|
5
|
+
|
6
|
+
attr_accessor :base_image, :working_dir
|
7
|
+
|
8
|
+
def apply_to(dockerfile)
|
9
|
+
dockerfile.from(base_image || default_base_image)
|
10
|
+
dockerfile.workdir(working_dir || DEFAULT_WORKING_DIR)
|
11
|
+
dockerfile.env("RAILS_ENV=#{Kuby.env}")
|
12
|
+
dockerfile.env("KUBY_ENV=#{Kuby.env}")
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def default_base_image
|
18
|
+
@default_base_image ||= case metadata.distro
|
19
|
+
when :debian
|
20
|
+
"ruby:#{RUBY_VERSION}"
|
21
|
+
when :alpine
|
22
|
+
"ruby:#{RUBY_VERSION}-alpine"
|
23
|
+
else
|
24
|
+
# ERROR
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'docker/remote'
|
2
|
+
|
3
|
+
module Kuby
|
4
|
+
module Docker
|
5
|
+
class Spec
|
6
|
+
attr_reader :definition
|
7
|
+
|
8
|
+
def initialize(definition)
|
9
|
+
@definition = definition
|
10
|
+
end
|
11
|
+
|
12
|
+
def base_image(image_url)
|
13
|
+
setup_phase.base_image = image_url
|
14
|
+
end
|
15
|
+
|
16
|
+
def working_dir(dir)
|
17
|
+
setup_phase.working_dir = dir
|
18
|
+
end
|
19
|
+
|
20
|
+
def rails_env(env)
|
21
|
+
setup_phase.rails_env = env
|
22
|
+
end
|
23
|
+
|
24
|
+
def bundler_version(version)
|
25
|
+
bundler_phase.bundler_version = version
|
26
|
+
end
|
27
|
+
|
28
|
+
def gemfile(path)
|
29
|
+
bundler_phase.gemfile = path
|
30
|
+
end
|
31
|
+
|
32
|
+
def package(pkg)
|
33
|
+
package_phase << pkg
|
34
|
+
end
|
35
|
+
|
36
|
+
def distro(distro_name)
|
37
|
+
metadata.distro = distro_name
|
38
|
+
@distro_spec = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def files(path)
|
42
|
+
copy_phase << path
|
43
|
+
end
|
44
|
+
|
45
|
+
def port(port)
|
46
|
+
webserver_phase.port = port
|
47
|
+
end
|
48
|
+
|
49
|
+
def image_url(url)
|
50
|
+
metadata.image_url = url
|
51
|
+
end
|
52
|
+
|
53
|
+
def use(*args)
|
54
|
+
layer_stack.use(*args)
|
55
|
+
end
|
56
|
+
|
57
|
+
def insert(*args)
|
58
|
+
layer_stack.insert(*args)
|
59
|
+
end
|
60
|
+
|
61
|
+
def delete(*args)
|
62
|
+
layer_stack.delete(*args)
|
63
|
+
end
|
64
|
+
|
65
|
+
def credentials(&block)
|
66
|
+
@credentials ||= Credentials.new
|
67
|
+
@credentials.instance_eval(&block) if block
|
68
|
+
@credentials
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_dockerfile
|
72
|
+
Dockerfile.new.tap do |df|
|
73
|
+
layer_stack.each { |layer| layer.apply_to(df) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def setup_phase
|
78
|
+
@setup_phase ||= SetupPhase.new(definition)
|
79
|
+
end
|
80
|
+
|
81
|
+
def package_phase
|
82
|
+
@package_phase ||= PackagePhase.new(definition)
|
83
|
+
end
|
84
|
+
|
85
|
+
def bundler_phase
|
86
|
+
@bundler_phase ||= BundlerPhase.new(definition)
|
87
|
+
end
|
88
|
+
|
89
|
+
def yarn_phase
|
90
|
+
@yarn_phase ||= YarnPhase.new(definition)
|
91
|
+
end
|
92
|
+
|
93
|
+
def copy_phase
|
94
|
+
@copy_phase ||= CopyPhase.new(definition)
|
95
|
+
end
|
96
|
+
|
97
|
+
def assets_phase
|
98
|
+
@assets_phase ||= AssetsPhase.new(definition)
|
99
|
+
end
|
100
|
+
|
101
|
+
def webserver_phase
|
102
|
+
@webserver_phase ||= WebserverPhase.new(definition)
|
103
|
+
end
|
104
|
+
|
105
|
+
def metadata
|
106
|
+
@metadata ||= Metadata.new(definition)
|
107
|
+
end
|
108
|
+
|
109
|
+
def tags
|
110
|
+
@tags ||= Tags.new(cli, remote_client, metadata)
|
111
|
+
end
|
112
|
+
|
113
|
+
def cli
|
114
|
+
@cli ||= Docker::CLI.new
|
115
|
+
end
|
116
|
+
|
117
|
+
def remote_client
|
118
|
+
@remote_client ||= ::Docker::Remote::Client.new(
|
119
|
+
metadata.image_host, metadata.image_repo,
|
120
|
+
credentials.username, credentials.password,
|
121
|
+
)
|
122
|
+
end
|
123
|
+
|
124
|
+
def distro_spec
|
125
|
+
@distro_spec ||= if distro_klass = Kuby.distros[metadata.distro]
|
126
|
+
distro_klass.new(self)
|
127
|
+
else
|
128
|
+
raise MissingDistroError, "distro '#{metadata.distro}' hasn't been registered"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def layer_stack
|
135
|
+
@layer_stack ||= LayerStack.new.tap do |stack|
|
136
|
+
stack.use(:setup_phase, setup_phase)
|
137
|
+
stack.use(:package_phase, package_phase)
|
138
|
+
stack.use(:bundler_phase, bundler_phase)
|
139
|
+
stack.use(:yarn_phase, yarn_phase)
|
140
|
+
stack.use(:copy_phase, copy_phase)
|
141
|
+
stack.use(:assets_phase, assets_phase)
|
142
|
+
stack.use(:webserver_phase, webserver_phase)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Kuby
|
2
|
+
module Docker
|
3
|
+
class Tags
|
4
|
+
LATEST = Metadata::LATEST_TAG
|
5
|
+
|
6
|
+
attr_reader :cli, :remote_client, :metadata
|
7
|
+
|
8
|
+
def initialize(cli, remote_client, metadata)
|
9
|
+
@cli = cli
|
10
|
+
@remote_client = remote_client
|
11
|
+
@metadata = metadata
|
12
|
+
end
|
13
|
+
|
14
|
+
def tags
|
15
|
+
(local.tags + remote.tags).uniq
|
16
|
+
end
|
17
|
+
|
18
|
+
def latest_tags
|
19
|
+
(local.latest_tags + remote.latest_tags).uniq
|
20
|
+
end
|
21
|
+
|
22
|
+
def timestamp_tags
|
23
|
+
(local.timestamp_tags + remote.timestamp_tags).uniq
|
24
|
+
end
|
25
|
+
|
26
|
+
def all
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def local
|
31
|
+
@local ||= LocalTags.new(cli, metadata)
|
32
|
+
end
|
33
|
+
|
34
|
+
def remote
|
35
|
+
@remote ||= RemoteTags.new(remote_client, metadata)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Kuby
|
2
|
+
module Docker
|
3
|
+
class TimestampTag
|
4
|
+
RE = /20[\d]{2}(?:0[1-9]|11|12)(?:0[1-9]|1[1-9]|2[1-9]|3[01])/.freeze
|
5
|
+
FORMAT = '%Y%m%d%H%M%S'.freeze
|
6
|
+
|
7
|
+
def self.try_parse(str)
|
8
|
+
if str =~ RE
|
9
|
+
new(Time.strptime(str, FORMAT))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :time
|
14
|
+
|
15
|
+
def initialize(time)
|
16
|
+
@time = time
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
time.strftime(FORMAT)
|
21
|
+
end
|
22
|
+
|
23
|
+
def <=>(other)
|
24
|
+
time <=> other.time
|
25
|
+
end
|
26
|
+
|
27
|
+
def hash
|
28
|
+
time.hash
|
29
|
+
end
|
30
|
+
|
31
|
+
def eql?(other)
|
32
|
+
time == other.time
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Kuby
|
2
|
+
module Docker
|
3
|
+
class WebserverPhase < Phase
|
4
|
+
class Puma
|
5
|
+
attr_reader :phase
|
6
|
+
|
7
|
+
def initialize(phase)
|
8
|
+
@phase = phase
|
9
|
+
end
|
10
|
+
|
11
|
+
def apply_to(dockerfile)
|
12
|
+
dockerfile.cmd(
|
13
|
+
'puma',
|
14
|
+
'--workers', '4',
|
15
|
+
'--bind', 'tcp://0.0.0.0',
|
16
|
+
'--port', phase.port,
|
17
|
+
'--pidfile', './server.pid',
|
18
|
+
'./config.ru'
|
19
|
+
)
|
20
|
+
|
21
|
+
dockerfile.expose(phase.port)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
DEFAULT_PORT = 8080
|
26
|
+
WEBSERVER_MAP = { puma: Puma }.freeze
|
27
|
+
|
28
|
+
attr_accessor :port, :webserver
|
29
|
+
|
30
|
+
def apply_to(dockerfile)
|
31
|
+
ws = webserver || default_webserver
|
32
|
+
ws_class = WEBSERVER_MAP[ws]
|
33
|
+
raise "No webserver named #{ws}" unless ws_class
|
34
|
+
|
35
|
+
ws_class.new(self).apply_to(dockerfile)
|
36
|
+
end
|
37
|
+
|
38
|
+
def port
|
39
|
+
@port || DEFAULT_PORT
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def default_webserver
|
45
|
+
if Gem.loaded_specs.include?('puma')
|
46
|
+
:puma
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'kuby/kubernetes/errors'
|
2
|
+
|
3
|
+
module Kuby
|
4
|
+
module Kubernetes
|
5
|
+
autoload :MinikubeProvider, 'kuby/kubernetes/minikube_provider'
|
6
|
+
autoload :Deployer, 'kuby/kubernetes/deployer'
|
7
|
+
autoload :DockerConfig, 'kuby/kubernetes/docker_config'
|
8
|
+
autoload :Manifest, 'kuby/kubernetes/manifest'
|
9
|
+
autoload :Monitors, 'kuby/kubernetes/monitors'
|
10
|
+
autoload :Plugin, 'kuby/kubernetes/plugin'
|
11
|
+
autoload :Plugins, 'kuby/kubernetes/plugins'
|
12
|
+
autoload :Provider, 'kuby/kubernetes/provider'
|
13
|
+
autoload :RegistrySecret, 'kuby/kubernetes/registry_secret'
|
14
|
+
autoload :Spec, 'kuby/kubernetes/spec'
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'krane'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module Kuby
|
7
|
+
module Kubernetes
|
8
|
+
class Deployer
|
9
|
+
attr_reader :definition
|
10
|
+
|
11
|
+
def initialize(definition)
|
12
|
+
@definition = definition
|
13
|
+
end
|
14
|
+
|
15
|
+
def deploy
|
16
|
+
namespaced, global = all_resources.partition do |resource|
|
17
|
+
# Unfortunately we can't use respond_to here because all KubeDSL
|
18
|
+
# objects use ObjectMeta, which has a namespace field. Not sure
|
19
|
+
# why, since it makes no sense for a namespace to have a namespace.
|
20
|
+
# Instead we just check for nil here.
|
21
|
+
resource.metadata.namespace
|
22
|
+
end
|
23
|
+
|
24
|
+
deploy_global_resources(global)
|
25
|
+
deploy_namespaced_resources(namespaced)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def deploy_global_resources(resources)
|
31
|
+
resources.each do |res|
|
32
|
+
Kuby.logger.info(
|
33
|
+
"Validating global resource, #{res.kind_sym.to_s.humanize.downcase} '#{res.metadata.name}'"
|
34
|
+
)
|
35
|
+
|
36
|
+
cli.apply(res, dry_run: true)
|
37
|
+
end
|
38
|
+
|
39
|
+
resources.each do |res|
|
40
|
+
Kuby.logger.info(
|
41
|
+
"Deploying #{res.kind_sym.to_s.humanize.downcase} '#{res.metadata.name}'"
|
42
|
+
)
|
43
|
+
|
44
|
+
cli.apply(res)
|
45
|
+
end
|
46
|
+
rescue InvalidResourceError => e
|
47
|
+
Kuby.logger.fatal(e.message)
|
48
|
+
Kuby.logger.fatal(e.resource.to_resource.to_yaml)
|
49
|
+
end
|
50
|
+
|
51
|
+
def deploy_namespaced_resources(resources)
|
52
|
+
old_kubeconfig = ENV['KUBECONFIG']
|
53
|
+
ENV['KUBECONFIG'] = provider.kubeconfig_path
|
54
|
+
|
55
|
+
tmpdir = Dir.mktmpdir('kuby-deploy-resources')
|
56
|
+
|
57
|
+
resources.each do |resource|
|
58
|
+
resource_path = File.join(
|
59
|
+
tmpdir, "#{SecureRandom.hex(6)}-#{resource.kind_sym.downcase}.yaml"
|
60
|
+
)
|
61
|
+
|
62
|
+
File.write(resource_path, resource.to_resource.to_yaml)
|
63
|
+
end
|
64
|
+
|
65
|
+
task = ::Krane::DeployTask.new(
|
66
|
+
namespace: namespace.metadata.name,
|
67
|
+
context: cli.current_context,
|
68
|
+
filenames: [tmpdir]
|
69
|
+
)
|
70
|
+
|
71
|
+
task.run!(verify_result: true, prune: false)
|
72
|
+
ensure
|
73
|
+
ENV['KUBECONFIG'] = old_kubeconfig
|
74
|
+
FileUtils.rm_rf(tmpdir)
|
75
|
+
end
|
76
|
+
|
77
|
+
def provider
|
78
|
+
definition.kubernetes.provider
|
79
|
+
end
|
80
|
+
|
81
|
+
def namespace
|
82
|
+
definition.kubernetes.namespace
|
83
|
+
end
|
84
|
+
|
85
|
+
def all_resources
|
86
|
+
definition.kubernetes.resources
|
87
|
+
end
|
88
|
+
|
89
|
+
def cli
|
90
|
+
provider.kubernetes_cli
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'kube-dsl'
|
3
|
+
|
4
|
+
module Kuby
|
5
|
+
module Kubernetes
|
6
|
+
class DockerConfig
|
7
|
+
extend ::KubeDSL::ValueFields
|
8
|
+
|
9
|
+
value_fields :registry_host, :username, :password, :email
|
10
|
+
|
11
|
+
def initialize(&block)
|
12
|
+
instance_eval(&block) if block
|
13
|
+
end
|
14
|
+
|
15
|
+
def serialize
|
16
|
+
{
|
17
|
+
registry_host.to_sym => {
|
18
|
+
username: username,
|
19
|
+
password: password,
|
20
|
+
email: email,
|
21
|
+
auth: Base64.strict_encode64("#{username}:#{password}")
|
22
|
+
}
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|