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,77 @@
|
|
1
|
+
require 'kubernetes-cli'
|
2
|
+
|
3
|
+
module Kuby
|
4
|
+
module Kubernetes
|
5
|
+
class Provider
|
6
|
+
attr_reader :definition
|
7
|
+
|
8
|
+
def initialize(definition)
|
9
|
+
@definition = definition
|
10
|
+
after_initialize
|
11
|
+
end
|
12
|
+
|
13
|
+
def configure(&block)
|
14
|
+
# do nothing by default
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup
|
18
|
+
# do nothing by default
|
19
|
+
end
|
20
|
+
|
21
|
+
# called after all providers and plugins have been configured
|
22
|
+
def after_configuration
|
23
|
+
# do nothing by default
|
24
|
+
end
|
25
|
+
|
26
|
+
# called before any providers or plugins have been setup
|
27
|
+
def before_setup
|
28
|
+
# do nothing by default
|
29
|
+
end
|
30
|
+
|
31
|
+
# called after all providers and plugins have been setup
|
32
|
+
def after_setup
|
33
|
+
# do nothing by default
|
34
|
+
end
|
35
|
+
|
36
|
+
# called before the deploy is initiated
|
37
|
+
def before_deploy(manifest)
|
38
|
+
# do nothing by default
|
39
|
+
end
|
40
|
+
|
41
|
+
# called after the deploy has completed
|
42
|
+
def after_deploy(manifest)
|
43
|
+
# do nothing by default
|
44
|
+
end
|
45
|
+
|
46
|
+
def deploy
|
47
|
+
deployer.deploy
|
48
|
+
end
|
49
|
+
|
50
|
+
def rollback
|
51
|
+
deployer.rollback
|
52
|
+
end
|
53
|
+
|
54
|
+
def kubernetes_cli
|
55
|
+
@kubernetes_cli ||= ::KubernetesCLI.new(kubeconfig_path)
|
56
|
+
end
|
57
|
+
|
58
|
+
def kubeconfig_path
|
59
|
+
raise NotImplementedError, "please define #{__method__} in #{self.class.name}"
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def after_initialize
|
65
|
+
# override this in derived classes
|
66
|
+
end
|
67
|
+
|
68
|
+
def deployer
|
69
|
+
@deployer ||= Kuby::Kubernetes::Deployer.new(definition)
|
70
|
+
end
|
71
|
+
|
72
|
+
def spec
|
73
|
+
definition.kubernetes
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module Kuby
|
4
|
+
module Kubernetes
|
5
|
+
class RegistrySecret < ::KubeDSL::DSL::V1::Secret
|
6
|
+
array_field(:docker_config) { DockerConfig.new }
|
7
|
+
|
8
|
+
def initialize(&block)
|
9
|
+
instance_eval(&block) if block
|
10
|
+
end
|
11
|
+
|
12
|
+
def serialize
|
13
|
+
super.tap do |result|
|
14
|
+
result[:type] = 'kubernetes.io/dockerconfigjson'
|
15
|
+
result[:data] = {
|
16
|
+
:".dockerconfigjson" => Base64.strict_encode64({
|
17
|
+
auths: docker_configs.each_with_object({}) do |dc, ret|
|
18
|
+
ret.merge!(dc.serialize)
|
19
|
+
end
|
20
|
+
}.to_json)
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'kube-dsl'
|
2
|
+
|
3
|
+
module Kuby
|
4
|
+
module Kubernetes
|
5
|
+
class Spec
|
6
|
+
extend ::KubeDSL::ValueFields
|
7
|
+
|
8
|
+
attr_reader :definition, :plugins
|
9
|
+
|
10
|
+
def initialize(definition)
|
11
|
+
@definition = definition
|
12
|
+
@plugins = TrailingHash.new
|
13
|
+
|
14
|
+
# default plugins
|
15
|
+
add_plugin(:rails_app)
|
16
|
+
end
|
17
|
+
|
18
|
+
def provider(provider_name = nil, &block)
|
19
|
+
if provider_name
|
20
|
+
if @provider || provider_klass = Kuby.providers[provider_name]
|
21
|
+
@provider ||= provider_klass.new(definition)
|
22
|
+
@provider.configure(&block)
|
23
|
+
else
|
24
|
+
msg = if provider_name
|
25
|
+
"no provider registered with name #{provider_name}, "\
|
26
|
+
'do you need to add a gem to your Gemfile?'
|
27
|
+
else
|
28
|
+
'no provider configured'
|
29
|
+
end
|
30
|
+
|
31
|
+
raise MissingProviderError, msg
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
@provider
|
36
|
+
end
|
37
|
+
|
38
|
+
def configure_plugin(plugin_name, &block)
|
39
|
+
if @plugins[plugin_name] || plugin_klass = Kuby.plugins[plugin_name]
|
40
|
+
@plugins[plugin_name] ||= plugin_klass.new(definition)
|
41
|
+
@plugins[plugin_name].configure(&block) if block
|
42
|
+
else
|
43
|
+
raise MissingPluginError, "no plugin registered with name #{plugin_name}, "\
|
44
|
+
'do you need to add a gem to your Gemfile?'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
alias_method :add_plugin, :configure_plugin
|
49
|
+
|
50
|
+
def plugin(plugin_name)
|
51
|
+
@plugins[plugin_name]
|
52
|
+
end
|
53
|
+
|
54
|
+
def after_configuration
|
55
|
+
@plugins.each { |_, plg| plg.after_configuration }
|
56
|
+
provider.after_configuration
|
57
|
+
end
|
58
|
+
|
59
|
+
def setup
|
60
|
+
provider.before_setup
|
61
|
+
provider.setup
|
62
|
+
|
63
|
+
@plugins.each { |_, plg| plg.before_setup }
|
64
|
+
@plugins.each { |_, plg| plg.setup }
|
65
|
+
@plugins.each { |_, plg| plg.after_setup }
|
66
|
+
|
67
|
+
provider.after_setup
|
68
|
+
end
|
69
|
+
|
70
|
+
def deploy(tag = nil)
|
71
|
+
tag ||= latest_tag
|
72
|
+
|
73
|
+
unless tag
|
74
|
+
raise Kuby::Docker::MissingTagError, 'could not find latest timestamped tag'
|
75
|
+
end
|
76
|
+
|
77
|
+
set_tag(tag)
|
78
|
+
|
79
|
+
provider.before_deploy(resources)
|
80
|
+
@plugins.each { |_, plg| plg.before_deploy(resources) }
|
81
|
+
|
82
|
+
provider.deploy
|
83
|
+
|
84
|
+
@plugins.each { |_, plg| plg.after_deploy(resources) }
|
85
|
+
provider.after_deploy(resources)
|
86
|
+
end
|
87
|
+
|
88
|
+
def rollback
|
89
|
+
depl = provider.kubernetes_cli.get_object(
|
90
|
+
'deployment', namespace.metadata.name, deployment.metadata.name
|
91
|
+
)
|
92
|
+
|
93
|
+
image_url = depl.dig('spec', 'template', 'spec', 'containers', 0, 'image')
|
94
|
+
|
95
|
+
unless image_url
|
96
|
+
raise MissingDeploymentError, "couldn't find an existing deployment"
|
97
|
+
end
|
98
|
+
|
99
|
+
deployed_tag = ::Kuby::Docker::TimestampTag.try_parse(image_url.split(':').last)
|
100
|
+
all_tags = docker.tags.all.timestamp_tags.sort
|
101
|
+
tag_idx = all_tags.index { |tag| tag.time == deployed_tag.time } || 0
|
102
|
+
|
103
|
+
if tag_idx == 0
|
104
|
+
raise Kuby::Docker::MissingTagError, 'could not find previous tag'
|
105
|
+
end
|
106
|
+
|
107
|
+
previous_tag = all_tags[tag_idx - 1]
|
108
|
+
deploy(previous_tag.to_s)
|
109
|
+
end
|
110
|
+
|
111
|
+
def namespace(&block)
|
112
|
+
spec = self
|
113
|
+
|
114
|
+
@namespace ||= KubeDSL.namespace do
|
115
|
+
metadata do
|
116
|
+
name "#{spec.selector_app}-#{spec.definition.environment}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
@namespace.instance_eval(&block) if block
|
121
|
+
@namespace
|
122
|
+
end
|
123
|
+
|
124
|
+
def resources
|
125
|
+
@resources ||= Manifest.new([
|
126
|
+
namespace,
|
127
|
+
*@plugins.flat_map { |_, plugin| plugin.resources }
|
128
|
+
])
|
129
|
+
end
|
130
|
+
|
131
|
+
def set_tag(tag)
|
132
|
+
plugin(:rails_app).set_image("#{docker.metadata.image_url}:#{tag}")
|
133
|
+
end
|
134
|
+
|
135
|
+
def selector_app
|
136
|
+
@selector_app ||= definition.app_name.downcase
|
137
|
+
end
|
138
|
+
|
139
|
+
def docker
|
140
|
+
definition.docker
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
def latest_tag
|
146
|
+
@latest_tag ||= docker.tags.local.latest_tags.find do |tag|
|
147
|
+
tag != ::Kuby::Docker::Tags::LATEST
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
data/lib/kuby/railtie.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'rails/railtie'
|
3
|
+
|
4
|
+
module Kuby
|
5
|
+
class Railtie < ::Rails::Railtie
|
6
|
+
rake_tasks do
|
7
|
+
load File.expand_path(File.join('tasks', 'kuby.rake'), __dir__)
|
8
|
+
end
|
9
|
+
|
10
|
+
initializer 'kuby.startup' do |_app|
|
11
|
+
Kuby.logger = Kuby::BasicLogger.new(STDERR)
|
12
|
+
end
|
13
|
+
|
14
|
+
initializer 'kuby.health_check_middleware' do |app|
|
15
|
+
app.middleware.use Kuby::Middleware::HealthCheck
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/kuby/tasks.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'rouge'
|
2
|
+
|
3
|
+
module Kuby
|
4
|
+
class Tasks
|
5
|
+
attr_reader :definition
|
6
|
+
|
7
|
+
def initialize(definition)
|
8
|
+
@definition = definition
|
9
|
+
end
|
10
|
+
|
11
|
+
def print_dockerfile
|
12
|
+
theme = Rouge::Themes::Base16::Solarized.new
|
13
|
+
formatter = Rouge::Formatters::Terminal256.new(theme)
|
14
|
+
lexer = Rouge::Lexers::Docker.new
|
15
|
+
tokens = lexer.lex(Kuby.definition.docker.to_dockerfile.to_s)
|
16
|
+
puts formatter.format(tokens)
|
17
|
+
end
|
18
|
+
|
19
|
+
def build
|
20
|
+
docker.cli.build(
|
21
|
+
dockerfile: docker.to_dockerfile,
|
22
|
+
image_url: docker.metadata.image_url,
|
23
|
+
tags: docker.metadata.tags
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def run
|
28
|
+
dockerfile = docker.to_dockerfile
|
29
|
+
|
30
|
+
docker.cli.run(
|
31
|
+
image_url: docker.metadata.image_url,
|
32
|
+
tag: 'latest',
|
33
|
+
ports: dockerfile.exposed_ports
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def push
|
38
|
+
image_url = docker.metadata.image_url
|
39
|
+
|
40
|
+
begin
|
41
|
+
docker.tags.local.latest_tags.each do |tag|
|
42
|
+
docker.cli.push(image_url, tag)
|
43
|
+
end
|
44
|
+
rescue Kuby::Docker::MissingTagError => e
|
45
|
+
msg = "#{e.message} Run rake kuby:build to build the"\
|
46
|
+
'Docker image before running this task.'
|
47
|
+
|
48
|
+
Kuby.logger.fatal(msg)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def print_resources
|
53
|
+
kubernetes.resources.each do |res|
|
54
|
+
puts res.to_resource.serialize.to_yaml
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def print_kubeconfig
|
59
|
+
path = kubernetes.provider.kubeconfig_path
|
60
|
+
Kuby.logger.info("Printing contents of #{path}")
|
61
|
+
puts File.read(path)
|
62
|
+
end
|
63
|
+
|
64
|
+
def remote_logs
|
65
|
+
kubernetes_cli.logtail(namespace, match_labels.serialize)
|
66
|
+
end
|
67
|
+
|
68
|
+
def remote_status
|
69
|
+
kubernetes_cli.run_cmd(['-n', namespace, 'get', 'pods'])
|
70
|
+
end
|
71
|
+
|
72
|
+
def remote_shell
|
73
|
+
first_pod = get_first_pod
|
74
|
+
shell = docker.distro_spec.shell_exe
|
75
|
+
kubernetes_cli.exec_cmd(shell, namespace, first_pod.dig('metadata', 'name'))
|
76
|
+
end
|
77
|
+
|
78
|
+
def remote_console
|
79
|
+
first_pod = get_first_pod
|
80
|
+
|
81
|
+
kubernetes_cli.exec_cmd(
|
82
|
+
'bundle exec rails console', namespace, first_pod.dig('metadata', 'name')
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
def remote_dbconsole
|
87
|
+
first_pod = get_first_pod
|
88
|
+
|
89
|
+
kubernetes_cli.exec_cmd(
|
90
|
+
'bundle exec rails dbconsole', namespace, first_pod.dig('metadata', 'name')
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def get_first_pod
|
97
|
+
pods = kubernetes_cli.get_objects(
|
98
|
+
'pods', namespace, match_labels.serialize
|
99
|
+
)
|
100
|
+
|
101
|
+
if pods.empty?
|
102
|
+
raise Kuby::Kubernetes::MissingResourceError,
|
103
|
+
"Couldn't find any running pods in namespace '#{namespace}' :("
|
104
|
+
|
105
|
+
exit 1
|
106
|
+
end
|
107
|
+
|
108
|
+
pods.first
|
109
|
+
end
|
110
|
+
|
111
|
+
def namespace
|
112
|
+
kubernetes.namespace.metadata.name
|
113
|
+
end
|
114
|
+
|
115
|
+
def match_labels
|
116
|
+
rails_app.deployment.spec.selector.match_labels
|
117
|
+
end
|
118
|
+
|
119
|
+
def rails_app
|
120
|
+
kubernetes.plugin(:rails_app)
|
121
|
+
end
|
122
|
+
|
123
|
+
def kubernetes_cli
|
124
|
+
kubernetes.provider.kubernetes_cli
|
125
|
+
end
|
126
|
+
|
127
|
+
def kubernetes
|
128
|
+
Kuby.definition.kubernetes
|
129
|
+
end
|
130
|
+
|
131
|
+
def docker
|
132
|
+
Kuby.definition.docker
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
namespace :kuby do
|
2
|
+
def tasks
|
3
|
+
@tasks ||= Kuby::Tasks.new(Kuby.definition)
|
4
|
+
end
|
5
|
+
|
6
|
+
task dockerfile: :environment do
|
7
|
+
tasks.print_dockerfile
|
8
|
+
end
|
9
|
+
|
10
|
+
task build: :environment do
|
11
|
+
tasks.build
|
12
|
+
end
|
13
|
+
|
14
|
+
task run: :environment do
|
15
|
+
tasks.run
|
16
|
+
end
|
17
|
+
|
18
|
+
task push: :environment do
|
19
|
+
tasks.push
|
20
|
+
end
|
21
|
+
|
22
|
+
task resources: :environment do
|
23
|
+
tasks.print_resources
|
24
|
+
end
|
25
|
+
|
26
|
+
task deploy: :environment do
|
27
|
+
Kuby.definition.kubernetes.deploy
|
28
|
+
end
|
29
|
+
|
30
|
+
task rollback: :environment do
|
31
|
+
Kuby.definition.kubernetes.rollback
|
32
|
+
end
|
33
|
+
|
34
|
+
task kubeconfig: :environment do
|
35
|
+
tasks.print_kubeconfig
|
36
|
+
end
|
37
|
+
|
38
|
+
task setup: :environment do
|
39
|
+
Kuby.definition.kubernetes.setup
|
40
|
+
end
|
41
|
+
|
42
|
+
namespace :remote do
|
43
|
+
task logs: :environment do
|
44
|
+
tasks.remote_logs
|
45
|
+
end
|
46
|
+
|
47
|
+
task status: :environment do
|
48
|
+
tasks.remote_status
|
49
|
+
end
|
50
|
+
|
51
|
+
task shell: :environment do
|
52
|
+
tasks.remote_shell
|
53
|
+
end
|
54
|
+
|
55
|
+
task console: :environment do
|
56
|
+
tasks.remote_console
|
57
|
+
end
|
58
|
+
|
59
|
+
task dbconsole: :environment do
|
60
|
+
tasks.remote_dbconsole
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|