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.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2 -0
  3. data/Gemfile +16 -0
  4. data/LICENSE +21 -0
  5. data/README.md +186 -0
  6. data/Rakefile +14 -0
  7. data/kuby-core.gemspec +27 -0
  8. data/lib/kuby.rb +112 -0
  9. data/lib/kuby/basic_logger.rb +22 -0
  10. data/lib/kuby/cli_base.rb +75 -0
  11. data/lib/kuby/definition.rb +29 -0
  12. data/lib/kuby/docker.rb +27 -0
  13. data/lib/kuby/docker/alpine.rb +62 -0
  14. data/lib/kuby/docker/assets_phase.rb +11 -0
  15. data/lib/kuby/docker/bundler_phase.rb +56 -0
  16. data/lib/kuby/docker/cli.rb +72 -0
  17. data/lib/kuby/docker/copy_phase.rb +23 -0
  18. data/lib/kuby/docker/credentials.rb +11 -0
  19. data/lib/kuby/docker/debian.rb +66 -0
  20. data/lib/kuby/docker/dockerfile.rb +128 -0
  21. data/lib/kuby/docker/errors.rb +22 -0
  22. data/lib/kuby/docker/layer_stack.rb +42 -0
  23. data/lib/kuby/docker/local_tags.rb +38 -0
  24. data/lib/kuby/docker/metadata.rb +69 -0
  25. data/lib/kuby/docker/package_list.rb +34 -0
  26. data/lib/kuby/docker/package_phase.rb +59 -0
  27. data/lib/kuby/docker/packages.rb +10 -0
  28. data/lib/kuby/docker/packages/managed_package.rb +29 -0
  29. data/lib/kuby/docker/packages/nodejs.rb +29 -0
  30. data/lib/kuby/docker/packages/package.rb +22 -0
  31. data/lib/kuby/docker/packages/yarn.rb +47 -0
  32. data/lib/kuby/docker/phase.rb +21 -0
  33. data/lib/kuby/docker/remote_tags.rb +24 -0
  34. data/lib/kuby/docker/setup_phase.rb +29 -0
  35. data/lib/kuby/docker/spec.rb +147 -0
  36. data/lib/kuby/docker/tags.rb +39 -0
  37. data/lib/kuby/docker/timestamp_tag.rb +36 -0
  38. data/lib/kuby/docker/webserver_phase.rb +51 -0
  39. data/lib/kuby/docker/yarn_phase.rb +11 -0
  40. data/lib/kuby/kubernetes.rb +16 -0
  41. data/lib/kuby/kubernetes/deployer.rb +94 -0
  42. data/lib/kuby/kubernetes/docker_config.rb +27 -0
  43. data/lib/kuby/kubernetes/errors.rb +22 -0
  44. data/lib/kuby/kubernetes/manifest.rb +56 -0
  45. data/lib/kuby/kubernetes/minikube_provider.rb +51 -0
  46. data/lib/kuby/kubernetes/plugin.rb +55 -0
  47. data/lib/kuby/kubernetes/plugins.rb +8 -0
  48. data/lib/kuby/kubernetes/plugins/nginx_ingress.rb +61 -0
  49. data/lib/kuby/kubernetes/plugins/rails_app.rb +16 -0
  50. data/lib/kuby/kubernetes/plugins/rails_app/database.rb +58 -0
  51. data/lib/kuby/kubernetes/plugins/rails_app/mysql.rb +142 -0
  52. data/lib/kuby/kubernetes/plugins/rails_app/plugin.rb +393 -0
  53. data/lib/kuby/kubernetes/plugins/rails_app/postgres.rb +10 -0
  54. data/lib/kuby/kubernetes/plugins/rails_app/rewrite_db_config.rb +13 -0
  55. data/lib/kuby/kubernetes/plugins/rails_app/sqlite.rb +10 -0
  56. data/lib/kuby/kubernetes/plugins/rails_app/tasks.rake +23 -0
  57. data/lib/kuby/kubernetes/provider.rb +77 -0
  58. data/lib/kuby/kubernetes/registry_secret.rb +26 -0
  59. data/lib/kuby/kubernetes/spec.rb +152 -0
  60. data/lib/kuby/middleware.rb +5 -0
  61. data/lib/kuby/middleware/health_check.rb +16 -0
  62. data/lib/kuby/railtie.rb +18 -0
  63. data/lib/kuby/tasks.rb +135 -0
  64. data/lib/kuby/tasks/kuby.rake +63 -0
  65. data/lib/kuby/trailing_hash.rb +19 -0
  66. data/lib/kuby/version.rb +3 -0
  67. metadata +233 -0
@@ -0,0 +1,22 @@
1
+ module Kuby
2
+ module Kubernetes
3
+ class KubernetesCLIError < StandardError; end
4
+
5
+ class InvalidResourceError < KubernetesCLIError
6
+ attr_accessor :resource
7
+ end
8
+
9
+ class InvalidResourceUriError < KubernetesCLIError
10
+ attr_accessor :resource_uri
11
+ end
12
+
13
+ class GetResourceError < KubernetesCLIError; end
14
+
15
+ class MissingDeploymentError < StandardError; end
16
+ class MissingProviderError < StandardError; end
17
+ class MissingPluginError < StandardError; end
18
+
19
+ class MissingResourceError < StandardError; end
20
+ class DuplicateResourceError < StandardError; end
21
+ end
22
+ end
@@ -0,0 +1,56 @@
1
+ require 'set'
2
+
3
+ module Kuby
4
+ module Kubernetes
5
+ class Manifest
6
+ include Enumerable
7
+
8
+ def initialize(resources)
9
+ @resources = resources
10
+
11
+ ensure_all_resources_unique!
12
+ end
13
+
14
+ def each(&block)
15
+ @resources.each(&block)
16
+ end
17
+
18
+ def find(kind, name)
19
+ @resources.find do |resource|
20
+ matches?(resource, kind, name)
21
+ end
22
+ end
23
+
24
+ def delete(kind, name)
25
+ idx = @resources.index do |resource|
26
+ matches?(resource, kind, name)
27
+ end
28
+
29
+ resources.delete(idx) if idx
30
+ end
31
+
32
+ def <<(resource)
33
+ @resources << resource
34
+ end
35
+
36
+ private
37
+
38
+ def matches?(resource, kind, name)
39
+ resource.kind_sym == kind && resource.metadata.name == name
40
+ end
41
+
42
+ def ensure_all_resources_unique!
43
+ seen = Set.new
44
+
45
+ @resources.each do |resource|
46
+ key = "#{resource.kind_sym}-#{resource.metadata.name}"
47
+
48
+ if seen.include?(key)
49
+ raise DuplicateResourceError, "found more than one #{resource.kind.downcase} "\
50
+ "resource named '#{resource.metadata.name}'"
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,51 @@
1
+ require 'kube-dsl'
2
+
3
+ module Kuby
4
+ module Kubernetes
5
+ class MinikubeProvider < Provider
6
+ STORAGE_CLASS_NAME = 'hostpath'.freeze
7
+
8
+ class Config
9
+ extend ::KubeDSL::ValueFields
10
+
11
+ value_fields :kubeconfig
12
+ end
13
+
14
+ attr_reader :config
15
+
16
+ def configure(&block)
17
+ config.instance_eval(&block) if block
18
+ end
19
+
20
+ def after_configuration
21
+ if rails_app = spec.plugin(:rails_app)
22
+ # Remove ingress and change service type from ClusterIP to
23
+ # LoadBalancer. No need to set up ingress for minikube since
24
+ # it handles all the localhost mapping, etc if you set up a
25
+ # service LB.
26
+ rails_app.resources.delete(rails_app.ingress)
27
+ rails_app.service.spec { type 'LoadBalancer' }
28
+ end
29
+
30
+ configure do
31
+ # default kubeconfig path
32
+ kubeconfig File.join(ENV['HOME'], '.kube', 'config')
33
+ end
34
+ end
35
+
36
+ def kubeconfig_path
37
+ config.kubeconfig
38
+ end
39
+
40
+ def storage_class_name
41
+ STORAGE_CLASS_NAME
42
+ end
43
+
44
+ private
45
+
46
+ def after_initialize
47
+ @config = Config.new
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,55 @@
1
+ module Kuby
2
+ module Kubernetes
3
+ class Plugin
4
+ attr_reader :definition
5
+
6
+ def initialize(definition)
7
+ @definition = definition
8
+ after_initialize
9
+ end
10
+
11
+ def configure(&block)
12
+ # do nothing by default
13
+ end
14
+
15
+ def setup
16
+ # do nothing by default
17
+ end
18
+
19
+ def resources
20
+ []
21
+ end
22
+
23
+ # called after all plugins have been configured
24
+ def after_configuration
25
+ # do nothing by default
26
+ end
27
+
28
+ # called before any plugins have been setup
29
+ def before_setup
30
+ # do nothing by default
31
+ end
32
+
33
+ # called after all plugins have been setup
34
+ def after_setup
35
+ # do nothing by default
36
+ end
37
+
38
+ # called before deploying any resources
39
+ def before_deploy(manifest)
40
+ # do nothing by default
41
+ end
42
+
43
+ # called after deploying all resources
44
+ def after_deploy(manifest)
45
+ # do nothing by default
46
+ end
47
+
48
+ private
49
+
50
+ def after_initialize
51
+ # override this in derived classes
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,8 @@
1
+ module Kuby
2
+ module Kubernetes
3
+ module Plugins
4
+ autoload :NginxIngress, 'kuby/kubernetes/plugins/nginx_ingress'
5
+ autoload :RailsApp, 'kuby/kubernetes/plugins/rails_app'
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,61 @@
1
+ require 'kube-dsl'
2
+
3
+ module Kuby
4
+ module Kubernetes
5
+ module Plugins
6
+ class NginxIngress < Plugin
7
+ class Config
8
+ extend ::KubeDSL::ValueFields
9
+
10
+ value_fields :provider
11
+ end
12
+
13
+ VERSION = '0.27.1'.freeze
14
+ DEFAULT_PROVIDER = 'cloud-generic'.freeze
15
+ NAMESPACE = 'ingress-nginx'.freeze
16
+ SERVICE_NAME = 'ingress-nginx'.freeze
17
+
18
+ SETUP_RESOURCES = [
19
+ "https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-#{VERSION}/deploy/static/mandatory.yaml",
20
+ "https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-#{VERSION}/deploy/static/provider/%{provider}.yaml"
21
+ ].freeze
22
+
23
+ def configure(&block)
24
+ @config.instance_eval(&block) if block
25
+ end
26
+
27
+ def setup
28
+ Kuby.logger.info('Deploying nginx ingress resources')
29
+
30
+ SETUP_RESOURCES.each do |uri|
31
+ uri = uri % { provider: @config.provider || DEFAULT_PROVIDER }
32
+ kubernetes_cli.apply_uri(uri)
33
+ end
34
+
35
+ Kuby.logger.info('Nginx ingress resources deployed!')
36
+ rescue => e
37
+ Kuby.logger.fatal(e.message)
38
+ raise
39
+ end
40
+
41
+ def namespace
42
+ NAMESPACE
43
+ end
44
+
45
+ def service_name
46
+ SERVICE_NAME
47
+ end
48
+
49
+ private
50
+
51
+ def after_initialize
52
+ @config = Config.new
53
+ end
54
+
55
+ def kubernetes_cli
56
+ definition.kubernetes.provider.kubernetes_cli
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,16 @@
1
+ module Kuby
2
+ module Kubernetes
3
+ module Plugins
4
+ module RailsApp
5
+ autoload :Database, 'kuby/kubernetes/plugins/rails_app/database'
6
+ autoload :MySQL, 'kuby/kubernetes/plugins/rails_app/mysql'
7
+ autoload :Plugin, 'kuby/kubernetes/plugins/rails_app/plugin'
8
+ autoload :Postgres, 'kuby/kubernetes/plugins/rails_app/postgres'
9
+ autoload :RewriteDbConfig, 'kuby/kubernetes/plugins/rails_app/rewrite_db_config'
10
+ autoload :Sqlite, 'kuby/kubernetes/plugins/rails_app/sqlite'
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ load File.expand_path(File.join('rails_app', 'tasks.rake'), __dir__)
@@ -0,0 +1,58 @@
1
+ module Kuby
2
+ module Kubernetes
3
+ module Plugins
4
+ module RailsApp
5
+ class UnsupportedDatabaseError < StandardError; end
6
+
7
+ class Database
8
+ ADAPTER_MAP = {
9
+ 'sqlite3' => Sqlite,
10
+ 'mysql2' => MySQL,
11
+ 'postgresql' => Postgres
12
+ }.freeze
13
+
14
+ def self.get(definition)
15
+ new(definition).database
16
+ end
17
+
18
+ def self.get_adapter(adapter)
19
+ ADAPTER_MAP.fetch(adapter) do
20
+ raise UnsupportedDatabaseError, "Kuby does not support the '#{adapter}'"\
21
+ 'database adapter'
22
+ end
23
+ end
24
+
25
+ attr_reader :definition
26
+
27
+ def initialize(definition)
28
+ @definition = definition
29
+ end
30
+
31
+ def database
32
+ @database ||= self.class
33
+ .get_adapter(adapter)
34
+ .new(definition, environment, db_configs)
35
+ end
36
+
37
+ private
38
+
39
+ def adapter
40
+ db_config['adapter']
41
+ end
42
+
43
+ def db_config
44
+ @db_config ||= db_configs[environment]
45
+ end
46
+
47
+ def environment
48
+ @environment ||= definition.environment
49
+ end
50
+
51
+ def db_configs
52
+ @db_configs ||= definition.app.config.database_configuration
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,142 @@
1
+ require 'kube-dsl'
2
+ require 'kuby/kube-db'
3
+
4
+ module Kuby
5
+ module Kubernetes
6
+ module Plugins
7
+ module RailsApp
8
+ class MySQL < Kuby::Kubernetes::Plugin
9
+ attr_reader :definition, :environment, :configs
10
+
11
+ def initialize(definition, environment, configs)
12
+ @definition = definition
13
+ @environment = environment
14
+ @configs = configs
15
+
16
+ user(config['username'])
17
+ password(config['password'])
18
+ end
19
+
20
+ def resources
21
+ @resources ||= [secret, database]
22
+ end
23
+
24
+ def after_configuration
25
+ definition.docker.package_phase.add(:mysql_dev)
26
+ definition.docker.package_phase.add(:mysql_client)
27
+ end
28
+
29
+ def host
30
+ # host is the same as the name thanks to k8s DNS
31
+ @host ||= database.metadata.name
32
+ end
33
+
34
+ def rewritten_configs
35
+ # deep dup
36
+ @rewritten_configs ||= Marshal.load(Marshal.dump(configs)).tap do |new_configs|
37
+ new_configs[environment]['host'] = host
38
+ end
39
+ end
40
+
41
+ def user(user)
42
+ secret do
43
+ data do
44
+ set :user, user
45
+ end
46
+ end
47
+ end
48
+
49
+ def password(password)
50
+ secret do
51
+ data do
52
+ set :password, password
53
+ end
54
+ end
55
+ end
56
+
57
+ def secret(&block)
58
+ context = self
59
+
60
+ @secret ||= KubeDSL.secret do
61
+ metadata do
62
+ name "#{context.base_name}-mysql-secret"
63
+ namespace context.kubernetes.namespace.metadata.name
64
+ end
65
+
66
+ type 'Opaque'
67
+ end
68
+
69
+ @secret.instance_eval(&block) if block
70
+ @secret
71
+ end
72
+
73
+ def database(&block)
74
+ context = self
75
+
76
+ @database ||= Kuby::KubeDB.my_sql do
77
+ api_version 'kubedb.com/v1alpha1'
78
+
79
+ metadata do
80
+ name "#{context.base_name}-mysql"
81
+ namespace context.kubernetes.namespace.metadata.name
82
+ end
83
+
84
+ spec do
85
+ database_secret do
86
+ secret_name context.secret.metadata.name
87
+ end
88
+
89
+ version '5.7-v2'
90
+ storage_type 'Durable'
91
+
92
+ storage do
93
+ storage_class_name context.kubernetes.provider.storage_class_name
94
+ access_modes ['ReadWriteOnce']
95
+
96
+ resources do
97
+ requests do
98
+ add :storage, '10Gi'
99
+ end
100
+ end
101
+ end
102
+
103
+ termination_policy 'DoNotTerminate'
104
+ end
105
+ end
106
+
107
+ @database.instance_eval(&block) if block
108
+ @database
109
+ end
110
+
111
+ def base_name
112
+ @base_name ||= "#{kubernetes.selector_app}-#{environment}"
113
+ end
114
+
115
+ def kubernetes
116
+ definition.kubernetes
117
+ end
118
+
119
+ def app
120
+ definition.app
121
+ end
122
+
123
+ private
124
+
125
+ def config
126
+ configs[environment]
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ Kuby.register_package(:mysql_client,
135
+ debian: 'default-mysql-client',
136
+ alpine: 'mariadb-client'
137
+ )
138
+
139
+ Kuby.register_package(:mysql_dev,
140
+ debian: 'default-libmysqlclient-dev',
141
+ alpine: 'mariadb-dev'
142
+ )