kuby-core 0.8.1 → 0.9.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 +4 -4
- data/CHANGELOG.md +12 -1
- data/README.md +11 -1
- data/bin/kuby +4 -0
- data/kuby-core.gemspec +5 -2
- data/lib/kuby.rb +46 -18
- data/lib/kuby/basic_logger.rb +13 -0
- data/lib/kuby/cli_base.rb +81 -8
- data/lib/kuby/commands.rb +220 -0
- data/lib/kuby/definition.rb +1 -3
- data/lib/kuby/dev_setup.rb +255 -0
- data/lib/kuby/docker.rb +1 -0
- data/lib/kuby/docker/bundler_phase.rb +3 -3
- data/lib/kuby/docker/cli.rb +13 -1
- data/lib/kuby/docker/dev_spec.rb +131 -0
- data/lib/kuby/docker/dockerfile.rb +16 -1
- data/lib/kuby/docker/layer_stack.rb +4 -0
- data/lib/kuby/docker/local_tags.rb +4 -0
- data/lib/kuby/docker/metadata.rb +0 -22
- data/lib/kuby/docker/setup_phase.rb +3 -2
- data/lib/kuby/docker/spec.rb +31 -5
- data/lib/kuby/environment.rb +10 -1
- data/lib/kuby/kubernetes.rb +9 -9
- data/lib/kuby/kubernetes/deploy_task.rb +4 -0
- data/lib/kuby/kubernetes/deployer.rb +63 -11
- data/lib/kuby/kubernetes/{minikube_provider.rb → docker_desktop_provider.rb} +4 -4
- data/lib/kuby/kubernetes/provider.rb +8 -4
- data/lib/kuby/kubernetes/spec.rb +23 -22
- data/lib/kuby/plugin_registry.rb +27 -0
- data/lib/kuby/plugins/rails_app/generators/kuby.rb +3 -15
- data/lib/kuby/plugins/rails_app/plugin.rb +230 -40
- data/lib/kuby/rails_commands.rb +89 -0
- data/lib/kuby/railtie.rb +0 -4
- data/lib/kuby/tasks.rb +76 -23
- data/lib/kuby/version.rb +1 -1
- data/spec/docker/metadata_spec.rb +0 -108
- data/spec/docker/spec_spec.rb +266 -0
- data/spec/spec_helper.rb +8 -1
- metadata +44 -9
- data/lib/kuby/tasks/kuby.rake +0 -70
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'digest'
|
2
|
+
|
1
3
|
module Kuby
|
2
4
|
module Docker
|
3
5
|
class Dockerfile
|
@@ -65,6 +67,10 @@ module Kuby
|
|
65
67
|
def to_s; "CMD #{super}"; end
|
66
68
|
end
|
67
69
|
|
70
|
+
class Arg < Command
|
71
|
+
def to_s; "ARG #{super}"; end
|
72
|
+
end
|
73
|
+
|
68
74
|
attr_reader :commands, :cursor
|
69
75
|
|
70
76
|
def initialize
|
@@ -84,6 +90,10 @@ module Kuby
|
|
84
90
|
add Env.new(*args)
|
85
91
|
end
|
86
92
|
|
93
|
+
def arg(*args)
|
94
|
+
add Arg.new(*args)
|
95
|
+
end
|
96
|
+
|
87
97
|
def run(*args)
|
88
98
|
add Run.new(*args)
|
89
99
|
end
|
@@ -101,7 +111,12 @@ module Kuby
|
|
101
111
|
end
|
102
112
|
|
103
113
|
def to_s
|
104
|
-
|
114
|
+
# ensure trailing newline
|
115
|
+
"#{commands.map(&:to_s).join("\n")}\n"
|
116
|
+
end
|
117
|
+
|
118
|
+
def checksum
|
119
|
+
Digest::SHA256.hexdigest(to_s)
|
105
120
|
end
|
106
121
|
|
107
122
|
def exposed_ports
|
data/lib/kuby/docker/metadata.rb
CHANGED
@@ -36,28 +36,6 @@ module Kuby
|
|
36
36
|
@tags.empty? ? default_tags : @tags
|
37
37
|
end
|
38
38
|
|
39
|
-
def tag
|
40
|
-
t = ENV.fetch('KUBY_DOCKER_TAG') do
|
41
|
-
environment.docker.tags.latest_timestamp_tag
|
42
|
-
end
|
43
|
-
|
44
|
-
unless t
|
45
|
-
raise MissingTagError, 'could not find latest timestamped tag'
|
46
|
-
end
|
47
|
-
|
48
|
-
t.to_s
|
49
|
-
end
|
50
|
-
|
51
|
-
def previous_tag(current_tag)
|
52
|
-
t = environment.docker.tags.previous_timestamp_tag(current_tag)
|
53
|
-
|
54
|
-
unless t
|
55
|
-
raise MissingTagError, 'could not find previous timestamped tag'
|
56
|
-
end
|
57
|
-
|
58
|
-
t.to_s
|
59
|
-
end
|
60
|
-
|
61
39
|
def distro
|
62
40
|
@distro || DEFAULT_DISTRO
|
63
41
|
end
|
@@ -3,13 +3,14 @@ module Kuby
|
|
3
3
|
class SetupPhase < Layer
|
4
4
|
DEFAULT_WORKING_DIR = '/usr/src/app'.freeze
|
5
5
|
|
6
|
-
attr_accessor :base_image, :working_dir
|
6
|
+
attr_accessor :base_image, :working_dir, :rails_env
|
7
7
|
|
8
8
|
def apply_to(dockerfile)
|
9
9
|
dockerfile.from(base_image || default_base_image)
|
10
10
|
dockerfile.workdir(working_dir || DEFAULT_WORKING_DIR)
|
11
|
-
dockerfile.env("RAILS_ENV=#{Kuby.env}")
|
11
|
+
dockerfile.env("RAILS_ENV=#{rails_env || Kuby.env}")
|
12
12
|
dockerfile.env("KUBY_ENV=#{Kuby.env}")
|
13
|
+
dockerfile.arg('RAILS_MASTER_KEY')
|
13
14
|
end
|
14
15
|
|
15
16
|
private
|
data/lib/kuby/docker/spec.rb
CHANGED
@@ -22,15 +22,15 @@ module Kuby
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def bundler_version(version)
|
25
|
-
bundler_phase.
|
25
|
+
bundler_phase.version = version
|
26
26
|
end
|
27
27
|
|
28
28
|
def gemfile(path)
|
29
29
|
bundler_phase.gemfile = path
|
30
30
|
end
|
31
31
|
|
32
|
-
def package(
|
33
|
-
package_phase
|
32
|
+
def package(*args)
|
33
|
+
package_phase.add(*args)
|
34
34
|
end
|
35
35
|
|
36
36
|
def distro(distro_name)
|
@@ -62,6 +62,10 @@ module Kuby
|
|
62
62
|
layer_stack.delete(*args)
|
63
63
|
end
|
64
64
|
|
65
|
+
def exists?(*args)
|
66
|
+
layer_stack.includes?(*args)
|
67
|
+
end
|
68
|
+
|
65
69
|
def credentials(&block)
|
66
70
|
@credentials ||= Credentials.new
|
67
71
|
@credentials.instance_eval(&block) if block
|
@@ -106,8 +110,26 @@ module Kuby
|
|
106
110
|
@metadata ||= Metadata.new(environment)
|
107
111
|
end
|
108
112
|
|
109
|
-
def
|
110
|
-
|
113
|
+
def tag
|
114
|
+
t = ENV.fetch('KUBY_DOCKER_TAG') do
|
115
|
+
tags.latest_timestamp_tag
|
116
|
+
end
|
117
|
+
|
118
|
+
unless t
|
119
|
+
raise MissingTagError, 'could not find latest timestamped tag'
|
120
|
+
end
|
121
|
+
|
122
|
+
t.to_s
|
123
|
+
end
|
124
|
+
|
125
|
+
def previous_tag(current_tag)
|
126
|
+
t = tags.previous_timestamp_tag(current_tag)
|
127
|
+
|
128
|
+
unless t
|
129
|
+
raise MissingTagError, 'could not find previous timestamped tag'
|
130
|
+
end
|
131
|
+
|
132
|
+
t.to_s
|
111
133
|
end
|
112
134
|
|
113
135
|
def cli
|
@@ -129,6 +151,10 @@ module Kuby
|
|
129
151
|
end
|
130
152
|
end
|
131
153
|
|
154
|
+
def tags
|
155
|
+
@tags ||= Tags.new(cli, remote_client, metadata)
|
156
|
+
end
|
157
|
+
|
132
158
|
private
|
133
159
|
|
134
160
|
def layer_stack
|
data/lib/kuby/environment.rb
CHANGED
@@ -8,7 +8,12 @@ module Kuby
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def docker(&block)
|
11
|
-
@docker ||=
|
11
|
+
@docker ||= if development?
|
12
|
+
Docker::DevSpec.new(self)
|
13
|
+
else
|
14
|
+
Docker::Spec.new(self)
|
15
|
+
end
|
16
|
+
|
12
17
|
@docker.instance_eval(&block) if block
|
13
18
|
@docker
|
14
19
|
end
|
@@ -22,5 +27,9 @@ module Kuby
|
|
22
27
|
def app_name
|
23
28
|
definition.app_name
|
24
29
|
end
|
30
|
+
|
31
|
+
def development?
|
32
|
+
name == 'development'
|
33
|
+
end
|
25
34
|
end
|
26
35
|
end
|
data/lib/kuby/kubernetes.rb
CHANGED
@@ -2,14 +2,14 @@ require 'kuby/kubernetes/errors'
|
|
2
2
|
|
3
3
|
module Kuby
|
4
4
|
module Kubernetes
|
5
|
-
autoload :
|
6
|
-
autoload :
|
7
|
-
autoload :
|
8
|
-
autoload :
|
9
|
-
autoload :Manifest,
|
10
|
-
autoload :Plugins,
|
11
|
-
autoload :Provider,
|
12
|
-
autoload :RegistrySecret,
|
13
|
-
autoload :Spec,
|
5
|
+
autoload :Deployer, 'kuby/kubernetes/deployer'
|
6
|
+
autoload :DeployTask, 'kuby/kubernetes/deploy_task'
|
7
|
+
autoload :DockerConfig, 'kuby/kubernetes/docker_config'
|
8
|
+
autoload :DockerDesktopProvider, 'kuby/kubernetes/docker_desktop_provider'
|
9
|
+
autoload :Manifest, 'kuby/kubernetes/manifest'
|
10
|
+
autoload :Plugins, 'kuby/kubernetes/plugins'
|
11
|
+
autoload :Provider, 'kuby/kubernetes/provider'
|
12
|
+
autoload :RegistrySecret, 'kuby/kubernetes/registry_secret'
|
13
|
+
autoload :Spec, 'kuby/kubernetes/spec'
|
14
14
|
end
|
15
15
|
end
|
@@ -6,22 +6,42 @@ module Kuby
|
|
6
6
|
module Kubernetes
|
7
7
|
class Deployer
|
8
8
|
attr_reader :environment
|
9
|
+
attr_accessor :logdev
|
9
10
|
|
10
11
|
def initialize(environment)
|
11
12
|
@environment = environment
|
12
13
|
end
|
13
14
|
|
14
15
|
def deploy
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
restart_rails_deployment_if_necessary do
|
17
|
+
namespaced, global = all_resources.partition do |resource|
|
18
|
+
# Unfortunately we can't use respond_to here because all KubeDSL
|
19
|
+
# objects use ObjectMeta, which has a namespace field. Not sure
|
20
|
+
# why, since it makes no sense for a namespace to have a namespace.
|
21
|
+
# Instead we just check for nil here.
|
22
|
+
resource.metadata.namespace
|
23
|
+
end
|
24
|
+
|
25
|
+
deploy_global_resources(global)
|
26
|
+
deploy_namespaced_resources(namespaced)
|
21
27
|
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# adhere to the "CLI" interface
|
31
|
+
def with_pipes(out = STDOUT, err = STDERR)
|
32
|
+
previous_logdev = logdev
|
33
|
+
@logdev = err
|
34
|
+
yield
|
35
|
+
ensure
|
36
|
+
@logdev = previous_logdev
|
37
|
+
end
|
38
|
+
|
39
|
+
def logdev
|
40
|
+
@logdev || STDERR
|
41
|
+
end
|
22
42
|
|
23
|
-
|
24
|
-
|
43
|
+
def last_status
|
44
|
+
nil
|
25
45
|
end
|
26
46
|
|
27
47
|
private
|
@@ -67,22 +87,54 @@ module Kuby
|
|
67
87
|
filenames: [tmpdir]
|
68
88
|
)
|
69
89
|
|
90
|
+
task.logger.reopen(logdev)
|
91
|
+
|
70
92
|
task.run!(verify_result: true, prune: false)
|
71
93
|
ensure
|
72
94
|
ENV['KUBECONFIG'] = old_kubeconfig
|
73
95
|
FileUtils.rm_rf(tmpdir)
|
74
96
|
end
|
75
97
|
|
98
|
+
def restart_rails_deployment_if_necessary
|
99
|
+
deployed_image = nil
|
100
|
+
current_image = "#{docker.metadata.image_url}:#{docker.tag}"
|
101
|
+
|
102
|
+
if rails_app = kubernetes.plugin(:rails_app)
|
103
|
+
deployment_name = rails_app.deployment.metadata.name
|
104
|
+
|
105
|
+
deployment = cli.get_object(
|
106
|
+
'deployment', namespace.metadata.name, deployment_name
|
107
|
+
)
|
108
|
+
|
109
|
+
deployed_image = deployment.dig(*%w(spec template spec containers), 0, 'image')
|
110
|
+
end
|
111
|
+
|
112
|
+
yield
|
113
|
+
|
114
|
+
if deployed_image == current_image
|
115
|
+
Kuby.logger.info('Docker image URL did not change, restarting Rails deployment manually')
|
116
|
+
cli.restart_deployment(namespace.metadata.name, deployment_name)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
76
120
|
def provider
|
77
|
-
|
121
|
+
kubernetes.provider
|
78
122
|
end
|
79
123
|
|
80
124
|
def namespace
|
81
|
-
|
125
|
+
kubernetes.namespace
|
82
126
|
end
|
83
127
|
|
84
128
|
def all_resources
|
85
|
-
|
129
|
+
kubernetes.resources
|
130
|
+
end
|
131
|
+
|
132
|
+
def docker
|
133
|
+
environment.docker
|
134
|
+
end
|
135
|
+
|
136
|
+
def kubernetes
|
137
|
+
environment.kubernetes
|
86
138
|
end
|
87
139
|
|
88
140
|
def cli
|
@@ -2,7 +2,7 @@ require 'kube-dsl'
|
|
2
2
|
|
3
3
|
module Kuby
|
4
4
|
module Kubernetes
|
5
|
-
class
|
5
|
+
class DockerDesktopProvider < Provider
|
6
6
|
STORAGE_CLASS_NAME = 'hostpath'.freeze
|
7
7
|
|
8
8
|
class Config
|
@@ -20,9 +20,9 @@ module Kuby
|
|
20
20
|
def after_configuration
|
21
21
|
if rails_app = spec.plugin(:rails_app)
|
22
22
|
# Remove ingress and change service type from ClusterIP to
|
23
|
-
# LoadBalancer. No need to set up ingress for
|
24
|
-
# it handles all the localhost mapping, etc if you set
|
25
|
-
# service LB.
|
23
|
+
# LoadBalancer. No need to set up ingress for Docker Desktop
|
24
|
+
# since it handles all the localhost mapping, etc if you set
|
25
|
+
# up a service LB.
|
26
26
|
rails_app.resources.delete(rails_app.ingress)
|
27
27
|
rails_app.service.spec { type 'LoadBalancer' }
|
28
28
|
end
|
@@ -55,20 +55,24 @@ module Kuby
|
|
55
55
|
@kubernetes_cli ||= ::KubernetesCLI.new(kubeconfig_path)
|
56
56
|
end
|
57
57
|
|
58
|
+
def helm_cli
|
59
|
+
@helm_cli ||= ::HelmCLI.new(kubeconfig_path)
|
60
|
+
end
|
61
|
+
|
58
62
|
def kubeconfig_path
|
59
63
|
raise NotImplementedError, "please define #{__method__} in #{self.class.name}"
|
60
64
|
end
|
61
65
|
|
66
|
+
def deployer
|
67
|
+
@deployer ||= Kuby::Kubernetes::Deployer.new(environment)
|
68
|
+
end
|
69
|
+
|
62
70
|
private
|
63
71
|
|
64
72
|
def after_initialize
|
65
73
|
# override this in derived classes
|
66
74
|
end
|
67
75
|
|
68
|
-
def deployer
|
69
|
-
@deployer ||= Kuby::Kubernetes::Deployer.new(environment)
|
70
|
-
end
|
71
|
-
|
72
76
|
def spec
|
73
77
|
environment.kubernetes
|
74
78
|
end
|
data/lib/kuby/kubernetes/spec.rb
CHANGED
@@ -36,13 +36,12 @@ module Kuby
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def configure_plugin(plugin_name, &block)
|
39
|
-
|
40
|
-
|
41
|
-
@plugins[plugin_name].
|
42
|
-
else
|
43
|
-
raise MissingPluginError, "no plugin registered with name #{plugin_name}, "\
|
44
|
-
'do you need to add a gem to your Gemfile?'
|
39
|
+
unless @plugins.include?(plugin_name)
|
40
|
+
plugin_klass = Kuby.plugins.find(plugin_name)
|
41
|
+
@plugins[plugin_name] = plugin_klass.new(environment)
|
45
42
|
end
|
43
|
+
|
44
|
+
@plugins[plugin_name].configure(&block) if block
|
46
45
|
end
|
47
46
|
|
48
47
|
alias_method :add_plugin, :configure_plugin
|
@@ -57,7 +56,7 @@ module Kuby
|
|
57
56
|
end
|
58
57
|
|
59
58
|
def before_deploy
|
60
|
-
@tag ||= docker.
|
59
|
+
@tag ||= docker.tag
|
61
60
|
|
62
61
|
provider.before_deploy(resources)
|
63
62
|
@plugins.each { |_, plg| plg.before_deploy(resources) }
|
@@ -66,7 +65,7 @@ module Kuby
|
|
66
65
|
end
|
67
66
|
|
68
67
|
def after_deploy
|
69
|
-
@tag ||= docker.
|
68
|
+
@tag ||= docker.tag
|
70
69
|
|
71
70
|
@plugins.each { |_, plg| plg.after_deploy(resources) }
|
72
71
|
provider.after_deploy(resources)
|
@@ -129,22 +128,24 @@ module Kuby
|
|
129
128
|
def registry_secret(&block)
|
130
129
|
spec = self
|
131
130
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
131
|
+
unless environment.development?
|
132
|
+
@registry_secret ||= RegistrySecret.new do
|
133
|
+
metadata do
|
134
|
+
name "#{spec.selector_app}-registry-secret"
|
135
|
+
namespace spec.namespace.metadata.name
|
136
|
+
end
|
137
137
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
138
|
+
docker_config do
|
139
|
+
registry_host spec.docker.metadata.image_host
|
140
|
+
username spec.docker.credentials.username
|
141
|
+
password spec.docker.credentials.password
|
142
|
+
email spec.docker.credentials.email
|
143
|
+
end
|
143
144
|
end
|
144
|
-
end
|
145
145
|
|
146
|
-
|
147
|
-
|
146
|
+
@registry_secret.instance_eval(&block) if block
|
147
|
+
@registry_secret
|
148
|
+
end
|
148
149
|
end
|
149
150
|
|
150
151
|
def resources
|
@@ -152,7 +153,7 @@ module Kuby
|
|
152
153
|
namespace,
|
153
154
|
registry_secret,
|
154
155
|
*@plugins.flat_map { |_, plugin| plugin.resources }
|
155
|
-
])
|
156
|
+
].compact)
|
156
157
|
end
|
157
158
|
|
158
159
|
def selector_app
|