kuby-core 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ )