kuby-core 0.5.0 → 0.7.2
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 +4 -4
- data/CHANGELOG.md +49 -0
- data/kuby-core.gemspec +4 -1
- data/lib/kuby.rb +23 -17
- data/lib/kuby/definition.rb +20 -14
- data/lib/kuby/docker.rb +2 -1
- data/lib/kuby/docker/alpine.rb +0 -1
- data/lib/kuby/docker/assets_phase.rb +1 -1
- data/lib/kuby/docker/bundler_phase.rb +4 -2
- data/lib/kuby/docker/cli.rb +32 -0
- data/lib/kuby/docker/copy_phase.rb +1 -1
- data/lib/kuby/docker/errors.rb +1 -0
- data/lib/kuby/docker/inline_layer.rb +15 -0
- data/lib/kuby/docker/{phase.rb → layer.rb} +6 -5
- data/lib/kuby/docker/layer_stack.rb +30 -4
- data/lib/kuby/docker/metadata.rb +10 -2
- data/lib/kuby/docker/package_phase.rb +1 -1
- data/lib/kuby/docker/setup_phase.rb +1 -1
- data/lib/kuby/docker/spec.rb +4 -4
- data/lib/kuby/docker/timestamp_tag.rb +6 -3
- data/lib/kuby/docker/webserver_phase.rb +1 -1
- data/lib/kuby/docker/yarn_phase.rb +1 -1
- data/lib/kuby/environment.rb +22 -0
- data/lib/kuby/kubernetes/minikube_provider.rb +5 -5
- data/lib/kuby/kubernetes/plugins/nginx_ingress.rb +12 -0
- data/lib/kuby/kubernetes/plugins/rails_app/database.rb +30 -9
- data/lib/kuby/kubernetes/plugins/rails_app/generators/kuby.rb +43 -4
- data/lib/kuby/kubernetes/plugins/rails_app/mysql.rb +14 -4
- data/lib/kuby/kubernetes/plugins/rails_app/plugin.rb +15 -14
- data/lib/kuby/kubernetes/plugins/rails_app/postgres.rb +132 -0
- data/lib/kuby/kubernetes/plugins/rails_app/sqlite.rb +20 -0
- data/lib/kuby/kubernetes/plugins/rails_app/tasks.rake +9 -4
- data/lib/kuby/kubernetes/spec.rb +1 -1
- data/lib/kuby/railtie.rb +0 -4
- data/lib/kuby/tasks.rb +31 -0
- data/lib/kuby/tasks/kuby.rake +19 -18
- data/lib/kuby/version.rb +1 -1
- data/spec/docker/timestamp_tag_spec.rb +11 -0
- data/spec/spec_helper.rb +102 -0
- metadata +23 -5
data/lib/kuby/docker/spec.rb
CHANGED
@@ -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)
|
@@ -1,13 +1,16 @@
|
|
1
1
|
module Kuby
|
2
2
|
module Docker
|
3
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
4
|
FORMAT = '%Y%m%d%H%M%S'.freeze
|
6
5
|
|
7
6
|
def self.try_parse(str)
|
8
|
-
|
9
|
-
|
7
|
+
time = begin
|
8
|
+
Time.strptime(str, FORMAT)
|
9
|
+
rescue ArgumentError
|
10
|
+
return nil
|
10
11
|
end
|
12
|
+
|
13
|
+
new(time)
|
11
14
|
end
|
12
15
|
|
13
16
|
attr_reader :time
|
@@ -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
|
@@ -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
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'yaml'
|
3
|
+
|
1
4
|
module Kuby
|
2
5
|
module Kubernetes
|
3
6
|
module Plugins
|
@@ -11,27 +14,29 @@ module Kuby
|
|
11
14
|
'postgresql' => Postgres
|
12
15
|
}.freeze
|
13
16
|
|
14
|
-
def self.get(
|
15
|
-
|
17
|
+
def self.get(rails_app)
|
18
|
+
if rails_app.manage_database?
|
19
|
+
new(rails_app).database
|
20
|
+
end
|
16
21
|
end
|
17
22
|
|
18
23
|
def self.get_adapter(adapter)
|
19
24
|
ADAPTER_MAP.fetch(adapter) do
|
20
|
-
raise UnsupportedDatabaseError, "Kuby does not support the '#{adapter}'"\
|
25
|
+
raise UnsupportedDatabaseError, "Kuby does not support the '#{adapter}' "\
|
21
26
|
'database adapter'
|
22
27
|
end
|
23
28
|
end
|
24
29
|
|
25
|
-
attr_reader :
|
30
|
+
attr_reader :rails_app
|
26
31
|
|
27
|
-
def initialize(
|
28
|
-
@
|
32
|
+
def initialize(rails_app)
|
33
|
+
@rails_app = rails_app
|
29
34
|
end
|
30
35
|
|
31
36
|
def database
|
32
37
|
@database ||= self.class
|
33
38
|
.get_adapter(adapter)
|
34
|
-
.new(
|
39
|
+
.new(rails_app, environment, db_configs)
|
35
40
|
end
|
36
41
|
|
37
42
|
private
|
@@ -45,11 +50,27 @@ module Kuby
|
|
45
50
|
end
|
46
51
|
|
47
52
|
def environment
|
48
|
-
@environment ||= definition.environment
|
53
|
+
@environment ||= rails_app.definition.environment.name
|
49
54
|
end
|
50
55
|
|
51
56
|
def db_configs
|
52
|
-
@db_configs ||=
|
57
|
+
@db_configs ||= YAML.load(ERB.new(File.read(db_config_path)).result)
|
58
|
+
end
|
59
|
+
|
60
|
+
def db_config_path
|
61
|
+
@db_config_path ||= begin
|
62
|
+
db_config_paths.first or
|
63
|
+
raise "Couldn't find database config at #{rails_app.root}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def db_config_paths
|
68
|
+
@db_config_paths ||=
|
69
|
+
Dir.glob(
|
70
|
+
File.join(
|
71
|
+
rails_app.root, 'config', 'database.{yml,erb,yml.erb,yaml,yaml.erb}'
|
72
|
+
)
|
73
|
+
)
|
53
74
|
end
|
54
75
|
end
|
55
76
|
end
|
@@ -6,15 +6,35 @@ class KubyGenerator < Rails::Generators::Base
|
|
6
6
|
initializer(
|
7
7
|
'kuby.rb',
|
8
8
|
<<~END
|
9
|
+
require 'kuby'
|
10
|
+
|
11
|
+
Kuby.load!
|
12
|
+
END
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_config_file
|
17
|
+
create_file(
|
18
|
+
'kuby.rb',
|
19
|
+
<<~END
|
20
|
+
require 'active_support/encrypted_configuration'
|
21
|
+
|
9
22
|
# Define a production Kuby deploy environment
|
10
23
|
Kuby.define(:production) do
|
24
|
+
app_creds = ActiveSupport::EncryptedConfiguration.new(
|
25
|
+
config_path: File.join('config', 'credentials.yml.enc'),
|
26
|
+
key_path: File.join('config', 'master.key'),
|
27
|
+
env_key: 'RAILS_MASTER_KEY',
|
28
|
+
raise_if_missing_key: true
|
29
|
+
)
|
30
|
+
|
11
31
|
docker do
|
12
32
|
# Configure your Docker registry credentials here. Add them to your
|
13
33
|
# Rails credentials file by running `bundle exec rake credentials:edit`.
|
14
34
|
credentials do
|
15
|
-
username
|
16
|
-
password
|
17
|
-
email
|
35
|
+
username app_creds[:KUBY_DOCKER_USERNAME]
|
36
|
+
password app_creds[:KUBY_DOCKER_PASSWORD]
|
37
|
+
email app_creds[:KUBY_DOCKER_EMAIL]
|
18
38
|
end
|
19
39
|
|
20
40
|
# Configure the URL to your Docker image here, eg:
|
@@ -28,7 +48,8 @@ class KubyGenerator < Rails::Generators::Base
|
|
28
48
|
# Add a plugin that facilitates deploying a Rails app.
|
29
49
|
add_plugin :rails_app
|
30
50
|
|
31
|
-
# Use minikube as the default
|
51
|
+
# Use minikube as the provider, which is the default installed by
|
52
|
+
# Docker Desktop.
|
32
53
|
# See: https://github.com/kubernetes/minikube
|
33
54
|
#
|
34
55
|
# Note: you will likely want to use a different provider when deploying
|
@@ -41,4 +62,22 @@ class KubyGenerator < Rails::Generators::Base
|
|
41
62
|
END
|
42
63
|
)
|
43
64
|
end
|
65
|
+
|
66
|
+
def create_dockerignore
|
67
|
+
create_file(
|
68
|
+
'.dockerignore',
|
69
|
+
<<~END
|
70
|
+
.bundle/
|
71
|
+
vendor/bundle
|
72
|
+
node_modules/
|
73
|
+
.node_modules/
|
74
|
+
**/.git*
|
75
|
+
tmp/
|
76
|
+
log/
|
77
|
+
engines/**/log/
|
78
|
+
engines/**/tmp/
|
79
|
+
public/assets
|
80
|
+
END
|
81
|
+
)
|
82
|
+
end
|
44
83
|
end
|
@@ -56,6 +56,20 @@ module Kuby
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
+
def storage(amount)
|
60
|
+
database do
|
61
|
+
spec do
|
62
|
+
storage do
|
63
|
+
resources do
|
64
|
+
requests do
|
65
|
+
set :storage, amount
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
59
73
|
def secret(&block)
|
60
74
|
context = self
|
61
75
|
|
@@ -118,10 +132,6 @@ module Kuby
|
|
118
132
|
definition.kubernetes
|
119
133
|
end
|
120
134
|
|
121
|
-
def app
|
122
|
-
definition.app
|
123
|
-
end
|
124
|
-
|
125
135
|
private
|
126
136
|
|
127
137
|
def config
|
@@ -13,12 +13,17 @@ module Kuby
|
|
13
13
|
ENV_SECRETS = [MASTER_KEY_VAR].freeze
|
14
14
|
ENV_EXCLUDE = ['RAILS_ENV'].freeze
|
15
15
|
|
16
|
-
|
16
|
+
value_field :root, default: '.'
|
17
|
+
value_fields :hostname, :tls_enabled
|
18
|
+
value_fields :manage_database, :database, :replicas
|
19
|
+
|
20
|
+
alias_method :manage_database?, :manage_database
|
17
21
|
|
18
22
|
def initialize(definition)
|
19
23
|
@definition = definition
|
20
24
|
@tls_enabled = true
|
21
25
|
@replicas = 1
|
26
|
+
@manage_database = true
|
22
27
|
end
|
23
28
|
|
24
29
|
def configure(&block)
|
@@ -26,11 +31,7 @@ module Kuby
|
|
26
31
|
end
|
27
32
|
|
28
33
|
def after_configuration
|
29
|
-
|
30
|
-
# is here as a placeholder to indicate we'd like to be able to
|
31
|
-
# handle Rails apps that don't use a database, i.e. don't have
|
32
|
-
# activerecord configured
|
33
|
-
if @database = Database.get(definition)
|
34
|
+
if @database = Database.get(self)
|
34
35
|
definition.kubernetes.plugins[database] = @database
|
35
36
|
definition.kubernetes.add_plugin(:kube_db)
|
36
37
|
|
@@ -43,7 +44,11 @@ module Kuby
|
|
43
44
|
definition.kubernetes.add_plugin(:nginx_ingress)
|
44
45
|
|
45
46
|
if @tls_enabled
|
46
|
-
|
47
|
+
context = self
|
48
|
+
|
49
|
+
definition.kubernetes.add_plugin(:cert_manager) do
|
50
|
+
email context.definition.docker.credentials.email
|
51
|
+
end
|
47
52
|
end
|
48
53
|
end
|
49
54
|
|
@@ -178,9 +183,9 @@ module Kuby
|
|
178
183
|
if master_key = ENV[MASTER_KEY_VAR]
|
179
184
|
add MASTER_KEY_VAR.to_sym, master_key
|
180
185
|
else
|
181
|
-
master_key_path = spec.
|
186
|
+
master_key_path = File.join(spec.root, 'config', 'master.key')
|
182
187
|
|
183
|
-
if
|
188
|
+
if File.exist?(master_key_path)
|
184
189
|
add MASTER_KEY_VAR.to_sym, File.read(master_key_path).strip
|
185
190
|
end
|
186
191
|
end
|
@@ -344,7 +349,7 @@ module Kuby
|
|
344
349
|
app_secrets,
|
345
350
|
deployment,
|
346
351
|
ingress,
|
347
|
-
*database
|
352
|
+
*database&.resources
|
348
353
|
]
|
349
354
|
end
|
350
355
|
|
@@ -364,10 +369,6 @@ module Kuby
|
|
364
369
|
definition.kubernetes
|
365
370
|
end
|
366
371
|
|
367
|
-
def app
|
368
|
-
definition.app
|
369
|
-
end
|
370
|
-
|
371
372
|
def namespace
|
372
373
|
definition.kubernetes.namespace
|
373
374
|
end
|
@@ -1,10 +1,142 @@
|
|
1
|
+
require 'kube-dsl'
|
2
|
+
require 'kuby/kube-db'
|
3
|
+
|
1
4
|
module Kuby
|
2
5
|
module Kubernetes
|
3
6
|
module Plugins
|
4
7
|
module RailsApp
|
5
8
|
class Postgres < Kuby::Kubernetes::Plugin
|
9
|
+
ROLE = 'web'.freeze
|
10
|
+
|
11
|
+
attr_reader :definition, :environment, :configs
|
12
|
+
|
13
|
+
def initialize(definition, environment, configs)
|
14
|
+
@definition = definition
|
15
|
+
@environment = environment
|
16
|
+
@configs = configs
|
17
|
+
|
18
|
+
user(config['username'])
|
19
|
+
password(config['password'])
|
20
|
+
end
|
21
|
+
|
22
|
+
def resources
|
23
|
+
@resources ||= [secret, database]
|
24
|
+
end
|
25
|
+
|
26
|
+
def after_configuration
|
27
|
+
definition.docker.package_phase.add(:postgres_dev)
|
28
|
+
definition.docker.package_phase.add(:postgres_client)
|
29
|
+
end
|
30
|
+
|
31
|
+
def host
|
32
|
+
# host is the same as the name thanks to k8s DNS
|
33
|
+
@host ||= database.metadata.name
|
34
|
+
end
|
35
|
+
|
36
|
+
def rewritten_configs
|
37
|
+
# deep dup
|
38
|
+
@rewritten_configs ||= Marshal.load(Marshal.dump(configs)).tap do |new_configs|
|
39
|
+
new_configs[environment]['host'] = host
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def user(user)
|
44
|
+
secret do
|
45
|
+
data do
|
46
|
+
set :POSTGRES_USER, user
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def password(password)
|
52
|
+
secret do
|
53
|
+
data do
|
54
|
+
set :POSTGRES_PASSWORD, password
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def secret(&block)
|
60
|
+
context = self
|
61
|
+
|
62
|
+
@secret ||= KubeDSL.secret do
|
63
|
+
metadata do
|
64
|
+
name "#{context.base_name}-postgres-secret"
|
65
|
+
namespace context.kubernetes.namespace.metadata.name
|
66
|
+
end
|
67
|
+
|
68
|
+
type 'Opaque'
|
69
|
+
end
|
70
|
+
|
71
|
+
@secret.instance_eval(&block) if block
|
72
|
+
@secret
|
73
|
+
end
|
74
|
+
|
75
|
+
def database(&block)
|
76
|
+
context = self
|
77
|
+
|
78
|
+
@database ||= Kuby::KubeDB.postgres do
|
79
|
+
api_version 'kubedb.com/v1alpha1'
|
80
|
+
|
81
|
+
metadata do
|
82
|
+
name "#{context.base_name}-postgres"
|
83
|
+
namespace context.kubernetes.namespace.metadata.name
|
84
|
+
end
|
85
|
+
|
86
|
+
spec do
|
87
|
+
database_secret do
|
88
|
+
secret_name context.secret.metadata.name
|
89
|
+
end
|
90
|
+
|
91
|
+
version '11.2'
|
92
|
+
standby_mode 'Hot'
|
93
|
+
streaming_mode 'asynchronous'
|
94
|
+
storage_type 'Durable'
|
95
|
+
|
96
|
+
storage do
|
97
|
+
storage_class_name context.kubernetes.provider.storage_class_name
|
98
|
+
access_modes ['ReadWriteOnce']
|
99
|
+
|
100
|
+
resources do
|
101
|
+
requests do
|
102
|
+
add :storage, '10Gi'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
termination_policy 'DoNotTerminate'
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
@database.instance_eval(&block) if block
|
112
|
+
@database
|
113
|
+
end
|
114
|
+
|
115
|
+
def base_name
|
116
|
+
@base_name ||= "#{kubernetes.selector_app}-#{ROLE}"
|
117
|
+
end
|
118
|
+
|
119
|
+
def kubernetes
|
120
|
+
definition.kubernetes
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def config
|
126
|
+
configs[environment]
|
127
|
+
end
|
6
128
|
end
|
7
129
|
end
|
8
130
|
end
|
9
131
|
end
|
10
132
|
end
|
133
|
+
|
134
|
+
Kuby.register_package(:postgres_dev,
|
135
|
+
debian: 'postgresql-client',
|
136
|
+
alpine: 'postgresql-dev'
|
137
|
+
)
|
138
|
+
|
139
|
+
Kuby.register_package(:postgres_client,
|
140
|
+
debian: 'postgresql-client',
|
141
|
+
alpine: 'postgresql-client'
|
142
|
+
)
|