kuby-core 0.2.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -0
  3. data/Gemfile +0 -4
  4. data/README.md +3 -160
  5. data/kuby-core.gemspec +5 -4
  6. data/lib/ext/krane/kubernetes_resource.rb +16 -0
  7. data/lib/kuby.rb +32 -17
  8. data/lib/kuby/definition.rb +20 -14
  9. data/lib/kuby/docker.rb +2 -1
  10. data/lib/kuby/docker/alpine.rb +0 -1
  11. data/lib/kuby/docker/assets_phase.rb +1 -1
  12. data/lib/kuby/docker/bundler_phase.rb +4 -2
  13. data/lib/kuby/docker/copy_phase.rb +1 -1
  14. data/lib/kuby/docker/inline_layer.rb +15 -0
  15. data/lib/kuby/docker/{phase.rb → layer.rb} +6 -5
  16. data/lib/kuby/docker/layer_stack.rb +30 -4
  17. data/lib/kuby/docker/metadata.rb +27 -1
  18. data/lib/kuby/docker/package_phase.rb +1 -1
  19. data/lib/kuby/docker/packages.rb +5 -4
  20. data/lib/kuby/docker/packages/simple_managed_package.rb +25 -0
  21. data/lib/kuby/docker/setup_phase.rb +1 -1
  22. data/lib/kuby/docker/spec.rb +4 -4
  23. data/lib/kuby/docker/tags.rb +18 -0
  24. data/lib/kuby/docker/webserver_phase.rb +1 -1
  25. data/lib/kuby/docker/yarn_phase.rb +1 -1
  26. data/lib/kuby/environment.rb +22 -0
  27. data/lib/kuby/kubernetes.rb +1 -0
  28. data/lib/kuby/kubernetes/deploy_task.rb +33 -0
  29. data/lib/kuby/kubernetes/deployer.rb +1 -2
  30. data/lib/kuby/kubernetes/minikube_provider.rb +5 -5
  31. data/lib/kuby/kubernetes/plugins/nginx_ingress.rb +12 -0
  32. data/lib/kuby/kubernetes/plugins/rails_app/database.rb +30 -9
  33. data/lib/kuby/kubernetes/plugins/rails_app/generators/kuby.rb +83 -0
  34. data/lib/kuby/kubernetes/plugins/rails_app/mysql.rb +17 -5
  35. data/lib/kuby/kubernetes/plugins/rails_app/plugin.rb +28 -42
  36. data/lib/kuby/kubernetes/plugins/rails_app/postgres.rb +132 -0
  37. data/lib/kuby/kubernetes/plugins/rails_app/sqlite.rb +20 -0
  38. data/lib/kuby/kubernetes/plugins/rails_app/tasks.rake +9 -4
  39. data/lib/kuby/kubernetes/spec.rb +52 -37
  40. data/lib/kuby/railtie.rb +0 -4
  41. data/lib/kuby/tasks.rb +18 -0
  42. data/lib/kuby/tasks/kuby.rake +24 -17
  43. data/lib/kuby/version.rb +1 -1
  44. metadata +21 -15
@@ -0,0 +1,83 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/base'
3
+
4
+ class KubyGenerator < Rails::Generators::Base
5
+ def create_initializer_file
6
+ initializer(
7
+ 'kuby.rb',
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
+
22
+ # Define a production Kuby deploy environment
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
+
31
+ docker do
32
+ # Configure your Docker registry credentials here. Add them to your
33
+ # Rails credentials file by running `bundle exec rake credentials:edit`.
34
+ credentials do
35
+ username app_creds[:KUBY_DOCKER_USERNAME]
36
+ password app_creds[:KUBY_DOCKER_PASSWORD]
37
+ email app_creds[:KUBY_DOCKER_EMAIL]
38
+ end
39
+
40
+ # Configure the URL to your Docker image here, eg:
41
+ # image_url 'foo.bar.com/me/myproject'
42
+ #
43
+ # If you're using Gitlab's Docker registry, try something like this:
44
+ # image_url 'registry.gitlab.com/<username>/<repo>'
45
+ end
46
+
47
+ kubernetes do
48
+ # Add a plugin that facilitates deploying a Rails app.
49
+ add_plugin :rails_app
50
+
51
+ # Use minikube as the provider, which is the default installed by
52
+ # Docker Desktop.
53
+ # See: https://github.com/kubernetes/minikube
54
+ #
55
+ # Note: you will likely want to use a different provider when deploying
56
+ # your application into a production environment. To configure a different
57
+ # provider, add the corresponding gem to your gemfile and update the
58
+ # following line according to the provider gem's README.
59
+ provider :minikube
60
+ end
61
+ end
62
+ END
63
+ )
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
83
+ end
@@ -6,6 +6,8 @@ module Kuby
6
6
  module Plugins
7
7
  module RailsApp
8
8
  class MySQL < Kuby::Kubernetes::Plugin
9
+ ROLE = 'web'.freeze
10
+
9
11
  attr_reader :definition, :environment, :configs
10
12
 
11
13
  def initialize(definition, environment, configs)
@@ -54,6 +56,20 @@ module Kuby
54
56
  end
55
57
  end
56
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
+
57
73
  def secret(&block)
58
74
  context = self
59
75
 
@@ -109,17 +125,13 @@ module Kuby
109
125
  end
110
126
 
111
127
  def base_name
112
- @base_name ||= "#{kubernetes.selector_app}-#{environment}"
128
+ @base_name ||= "#{kubernetes.selector_app}-#{ROLE}"
113
129
  end
114
130
 
115
131
  def kubernetes
116
132
  definition.kubernetes
117
133
  end
118
134
 
119
- def app
120
- definition.app
121
- end
122
-
123
135
  private
124
136
 
125
137
  def config
@@ -13,11 +13,17 @@ module Kuby
13
13
  ENV_SECRETS = [MASTER_KEY_VAR].freeze
14
14
  ENV_EXCLUDE = ['RAILS_ENV'].freeze
15
15
 
16
- value_fields :hostname, :tls_enabled, :database
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
25
+ @replicas = 1
26
+ @manage_database = true
21
27
  end
22
28
 
23
29
  def configure(&block)
@@ -25,11 +31,7 @@ module Kuby
25
31
  end
26
32
 
27
33
  def after_configuration
28
- # currently Database.get doesn't return nil, but this if statement
29
- # is here as a placeholder to indicate we'd like to be able to
30
- # handle Rails apps that don't use a database, i.e. don't have
31
- # activerecord configured
32
- if @database = Database.get(definition)
34
+ if @database = Database.get(self)
33
35
  definition.kubernetes.plugins[database] = @database
34
36
  definition.kubernetes.add_plugin(:kube_db)
35
37
 
@@ -42,7 +44,11 @@ module Kuby
42
44
  definition.kubernetes.add_plugin(:nginx_ingress)
43
45
 
44
46
  if @tls_enabled
45
- definition.kubernetes.add_plugin(:cert_manager)
47
+ context = self
48
+
49
+ definition.kubernetes.add_plugin(:cert_manager) do
50
+ email context.definition.docker.credentials.email
51
+ end
46
52
  end
47
53
  end
48
54
 
@@ -53,14 +59,9 @@ module Kuby
53
59
  cert_manager.annotate_ingress(ing)
54
60
  end
55
61
  end
56
- end
57
62
 
58
- def database(&block)
59
- @database.instance_eval(&block) if block
60
- @database
61
- end
63
+ image_with_tag = "#{docker.metadata.image_url}:#{kubernetes.tag}"
62
64
 
63
- def set_image(image_with_tag)
64
65
  deployment do
65
66
  spec do
66
67
  template do
@@ -82,6 +83,11 @@ module Kuby
82
83
  end
83
84
  end
84
85
 
86
+ def database(&block)
87
+ @database.instance_eval(&block) if block
88
+ @database
89
+ end
90
+
85
91
  def service(&block)
86
92
  spec = self
87
93
 
@@ -177,9 +183,9 @@ module Kuby
177
183
  if master_key = ENV[MASTER_KEY_VAR]
178
184
  add MASTER_KEY_VAR.to_sym, master_key
179
185
  else
180
- master_key_path = spec.app.root.join('config', 'master.key')
186
+ master_key_path = File.join(spec.root, 'config', 'master.key')
181
187
 
182
- if master_key_path.exist?
188
+ if File.exist?(master_key_path)
183
189
  add MASTER_KEY_VAR.to_sym, File.read(master_key_path).strip
184
190
  end
185
191
  end
@@ -190,33 +196,12 @@ module Kuby
190
196
  @app_secrets
191
197
  end
192
198
 
193
- def registry_secret(&block)
194
- spec = self
195
-
196
- @registry_secret ||= RegistrySecret.new do
197
- metadata do
198
- name "#{spec.selector_app}-registry-secret"
199
- namespace spec.namespace.metadata.name
200
- end
201
-
202
- docker_config do
203
- registry_host spec.docker.metadata.image_host
204
- username spec.docker.credentials.username
205
- password spec.docker.credentials.password
206
- email spec.docker.credentials.email
207
- end
208
- end
209
-
210
- @registry_secret.instance_eval(&block) if block
211
- @registry_secret
212
- end
213
-
214
199
  def deployment(&block)
215
200
  kube_spec = self
216
201
 
217
202
  @deployment ||= KubeDSL.deployment do
218
203
  metadata do
219
- name "#{kube_spec.selector_app}-deployment"
204
+ name "#{kube_spec.selector_app}-#{kube_spec.role}"
220
205
  namespace kube_spec.namespace.metadata.name
221
206
 
222
207
  labels do
@@ -226,6 +211,8 @@ module Kuby
226
211
  end
227
212
 
228
213
  spec do
214
+ replicas kube_spec.replicas
215
+
229
216
  selector do
230
217
  match_labels do
231
218
  add :app, kube_spec.selector_app
@@ -299,7 +286,7 @@ module Kuby
299
286
  end
300
287
 
301
288
  image_pull_secret do
302
- name kube_spec.registry_secret.metadata.name
289
+ name kube_spec.definition.kubernetes.registry_secret.metadata.name
303
290
  end
304
291
 
305
292
  restart_policy 'Always'
@@ -360,10 +347,9 @@ module Kuby
360
347
  service_account,
361
348
  config_map,
362
349
  app_secrets,
363
- registry_secret,
364
350
  deployment,
365
351
  ingress,
366
- *database.resources
352
+ *database&.resources
367
353
  ]
368
354
  end
369
355
 
@@ -379,8 +365,8 @@ module Kuby
379
365
  definition.docker
380
366
  end
381
367
 
382
- def app
383
- definition.app
368
+ def kubernetes
369
+ definition.kubernetes
384
370
  end
385
371
 
386
372
  def namespace
@@ -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
+ )
@@ -3,8 +3,28 @@ module Kuby
3
3
  module Plugins
4
4
  module RailsApp
5
5
  class Sqlite < Kuby::Kubernetes::Plugin
6
+ attr_reader :definition
7
+
8
+ def initialize(definition, *)
9
+ @definition = definition
10
+ end
11
+
12
+ def after_configuration
13
+ definition.docker.package_phase.add(:sqlite_dev)
14
+ definition.docker.package_phase.add(:sqlite_client)
15
+ end
6
16
  end
7
17
  end
8
18
  end
9
19
  end
10
20
  end
21
+
22
+ Kuby.register_package(:sqlite_dev,
23
+ debian: 'libsqlite3-dev',
24
+ alpine: 'sqlite-dev'
25
+ )
26
+
27
+ Kuby.register_package(:sqlite_client,
28
+ debian: 'sqlite3',
29
+ alpine: 'sqlite'
30
+ )