kuber_kit 0.2.6 → 0.3.1
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/Gemfile.lock +15 -13
- data/TODO.md +5 -6
- data/example/config.rb +3 -0
- data/example/images/ruby_app/Dockerfile +1 -1
- data/example/images/ruby_app/image.rb +3 -0
- data/lib/kuber_kit.rb +18 -3
- data/lib/kuber_kit/actions/configuration_loader.rb +19 -1
- data/lib/kuber_kit/actions/template_reader.rb +3 -6
- data/lib/kuber_kit/configs.rb +62 -32
- data/lib/kuber_kit/container.rb +14 -2
- data/lib/kuber_kit/core/configuration.rb +13 -4
- data/lib/kuber_kit/core/configuration_definition.rb +24 -4
- data/lib/kuber_kit/core/configuration_factory.rb +3 -2
- data/lib/kuber_kit/core/context_helper/base_helper.rb +4 -0
- data/lib/kuber_kit/core/context_helper/context_helper_factory.rb +3 -2
- data/lib/kuber_kit/core/context_helper/context_vars.rb +39 -0
- data/lib/kuber_kit/core/context_helper/image_helper.rb +17 -0
- data/lib/kuber_kit/core/image_definition.rb +4 -4
- data/lib/kuber_kit/env_file_reader/reader.rb +10 -6
- data/lib/kuber_kit/extensions/indocker_compat.rb +4 -0
- data/lib/kuber_kit/image_compiler/compiler.rb +1 -1
- data/lib/kuber_kit/service_deployer/deployer.rb +14 -8
- data/lib/kuber_kit/service_deployer/strategies/docker_compose.rb +24 -0
- data/lib/kuber_kit/service_deployer/strategies/kubernetes_runner.rb +9 -1
- data/lib/kuber_kit/shell/abstract_shell.rb +4 -0
- data/lib/kuber_kit/shell/commands/docker_compose_commands.rb +17 -0
- data/lib/kuber_kit/shell/commands/kubectl_commands.rb +12 -2
- data/lib/kuber_kit/shell/local_shell.rb +10 -0
- data/lib/kuber_kit/shell/ssh_shell.rb +4 -0
- data/lib/kuber_kit/template_reader/action_handler.rb +13 -0
- data/lib/kuber_kit/template_reader/reader.rb +13 -9
- data/lib/kuber_kit/template_reader/{abstract_template_reader.rb → strategies/abstract.rb} +1 -1
- data/lib/kuber_kit/template_reader/{artifact_file_reader.rb → strategies/artifact_file.rb} +1 -1
- data/lib/kuber_kit/version.rb +1 -1
- metadata +12 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d3088efcb997e3dda2dbcd85d584265b7a5eec03b20093265842e3396592ab35
|
4
|
+
data.tar.gz: a52e92d808c31b2a0f654e8ba2eaa9cf46a0b7352855f7602e63fee7a09a1271
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ab6df03558b78d0d146cd6d77224f048564ae8d84c9c6d18d2b9abddb6d6cd8c1e8306549bad16c254fd5632b83c2cfceb690518216251efc4008983aa31f86
|
7
|
+
data.tar.gz: 1a2837bfa4334e33fa3c7aee7274516c297e9df0b92adf0fd7f021576c3682af2b48f9336c80b849bf1c5655a88f4d3cf43a42f6669140f5875e99a6710b5a5e
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
kuber_kit (0.
|
4
|
+
kuber_kit (0.3.1)
|
5
5
|
cli-ui
|
6
6
|
contracts-lite
|
7
7
|
dry-auto_inject
|
@@ -35,23 +35,25 @@ GEM
|
|
35
35
|
coderay (~> 1.1)
|
36
36
|
method_source (~> 1.0)
|
37
37
|
rake (10.5.0)
|
38
|
-
rspec (3.
|
39
|
-
rspec-core (~> 3.
|
40
|
-
rspec-expectations (~> 3.
|
41
|
-
rspec-mocks (~> 3.
|
42
|
-
rspec-core (3.
|
43
|
-
rspec-support (~> 3.
|
44
|
-
rspec-expectations (3.
|
38
|
+
rspec (3.10.0)
|
39
|
+
rspec-core (~> 3.10.0)
|
40
|
+
rspec-expectations (~> 3.10.0)
|
41
|
+
rspec-mocks (~> 3.10.0)
|
42
|
+
rspec-core (3.10.0)
|
43
|
+
rspec-support (~> 3.10.0)
|
44
|
+
rspec-expectations (3.10.0)
|
45
45
|
diff-lcs (>= 1.2.0, < 2.0)
|
46
|
-
rspec-support (~> 3.
|
47
|
-
rspec-mocks (3.
|
46
|
+
rspec-support (~> 3.10.0)
|
47
|
+
rspec-mocks (3.10.0)
|
48
48
|
diff-lcs (>= 1.2.0, < 2.0)
|
49
|
-
rspec-support (~> 3.
|
50
|
-
rspec-support (3.
|
51
|
-
simplecov (0.
|
49
|
+
rspec-support (~> 3.10.0)
|
50
|
+
rspec-support (3.10.0)
|
51
|
+
simplecov (0.20.0)
|
52
52
|
docile (~> 1.1)
|
53
53
|
simplecov-html (~> 0.11)
|
54
|
+
simplecov_json_formatter (~> 0.1)
|
54
55
|
simplecov-html (0.12.3)
|
56
|
+
simplecov_json_formatter (0.1.2)
|
55
57
|
thor (1.0.1)
|
56
58
|
|
57
59
|
PLATFORMS
|
data/TODO.md
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
- find a way to launch job on each run, e.g. for migrations
|
2
|
-
- allow setting default configuration for kuberkit using env variable
|
3
|
-
- add ability to set container health checks
|
4
|
-
- implement interactive shell.exec!
|
5
|
-
- allow deploying only services enabled for specific configuration
|
6
1
|
- list services and require confirmation before deployment
|
7
|
-
-
|
2
|
+
- kit attach should list available deployments/pods, and ask for specific container if it has multiple containers
|
3
|
+
- add kit logs support, should work similar to kit attach
|
4
|
+
- allow deploying only services enabled for specific configuration
|
5
|
+
- find a way to always deploy some service, e.g. for migrations and env_files
|
6
|
+
- add ability to set container health checks
|
8
7
|
- template should be able to set default attributes
|
9
8
|
- template should be able to depend on image?
|
data/example/config.rb
ADDED
@@ -2,6 +2,9 @@ KuberKit
|
|
2
2
|
.define_image(:ruby_app)
|
3
3
|
.registry(:default)
|
4
4
|
.depends_on(:ruby, :app_sources)
|
5
|
+
.build_vars({
|
6
|
+
example_file_name: "example.txt"
|
7
|
+
})
|
5
8
|
.before_build do |context_helper, build_dir|
|
6
9
|
# copy file: local artifact
|
7
10
|
source_path = context_helper.artifact_path(:kuber_kit_example_data, "test.txt")
|
data/lib/kuber_kit.rb
CHANGED
@@ -57,6 +57,7 @@ module KuberKit
|
|
57
57
|
autoload :ImageHelper, 'core/context_helper/image_helper'
|
58
58
|
autoload :ServiceHelper, 'core/context_helper/service_helper'
|
59
59
|
autoload :ContextHelperFactory, 'core/context_helper/context_helper_factory'
|
60
|
+
autoload :ContextVars, 'core/context_helper/context_vars'
|
60
61
|
end
|
61
62
|
|
62
63
|
module Registries
|
@@ -87,6 +88,7 @@ module KuberKit
|
|
87
88
|
module Commands
|
88
89
|
autoload :BashCommands, 'shell/commands/bash_commands'
|
89
90
|
autoload :DockerCommands, 'shell/commands/docker_commands'
|
91
|
+
autoload :DockerComposeCommands, 'shell/commands/docker_compose_commands'
|
90
92
|
autoload :GitCommands, 'shell/commands/git_commands'
|
91
93
|
autoload :RsyncCommands, 'shell/commands/rsync_commands'
|
92
94
|
autoload :KubectlCommands, 'shell/commands/kubectl_commands'
|
@@ -127,9 +129,13 @@ module KuberKit
|
|
127
129
|
end
|
128
130
|
|
129
131
|
module TemplateReader
|
132
|
+
autoload :ActionHandler, 'template_reader/action_handler'
|
130
133
|
autoload :Reader, 'template_reader/reader'
|
131
|
-
|
132
|
-
|
134
|
+
|
135
|
+
module Strategies
|
136
|
+
autoload :Abstract, 'template_reader/strategies/abstract'
|
137
|
+
autoload :ArtifactFile, 'template_reader/strategies/artifact_file'
|
138
|
+
end
|
133
139
|
end
|
134
140
|
|
135
141
|
module ServiceDeployer
|
@@ -140,6 +146,7 @@ module KuberKit
|
|
140
146
|
|
141
147
|
module Strategies
|
142
148
|
autoload :Abstract, 'service_deployer/strategies/abstract'
|
149
|
+
autoload :DockerCompose, 'service_deployer/strategies/docker_compose'
|
143
150
|
autoload :Kubernetes, 'service_deployer/strategies/kubernetes'
|
144
151
|
autoload :KubernetesRunner, 'service_deployer/strategies/kubernetes_runner'
|
145
152
|
end
|
@@ -170,9 +177,9 @@ module KuberKit
|
|
170
177
|
autoload :Simple, 'ui/simple'
|
171
178
|
end
|
172
179
|
|
173
|
-
autoload :Configs, 'configs'
|
174
180
|
autoload :CLI, 'cli'
|
175
181
|
autoload :Container, 'container'
|
182
|
+
autoload :Configs, 'configs'
|
176
183
|
|
177
184
|
Import = Dry::AutoInject(Container)
|
178
185
|
|
@@ -211,6 +218,10 @@ module KuberKit
|
|
211
218
|
@current_configuration ||= Container['core.configuration_store'].get_configuration(@configuration_name)
|
212
219
|
end
|
213
220
|
|
221
|
+
def global_build_vars
|
222
|
+
KuberKit::Core::ContextHelper::ContextVars.new(current_configuration.global_build_vars)
|
223
|
+
end
|
224
|
+
|
214
225
|
def add_registry(registry)
|
215
226
|
Container["core.registry_store"].add(registry)
|
216
227
|
end
|
@@ -234,6 +245,10 @@ module KuberKit
|
|
234
245
|
def build_helper(&proc)
|
235
246
|
KuberKit::Core::ContextHelper::BaseHelper.class_exec(&proc)
|
236
247
|
end
|
248
|
+
|
249
|
+
def configure(&proc)
|
250
|
+
yield(Container["configs"])
|
251
|
+
end
|
237
252
|
end
|
238
253
|
end
|
239
254
|
|
@@ -1,4 +1,6 @@
|
|
1
1
|
class KuberKit::Actions::ConfigurationLoader
|
2
|
+
APP_CONFIG_FILENAME = "config.rb".freeze
|
3
|
+
|
2
4
|
include KuberKit::Import[
|
3
5
|
"core.registry_store",
|
4
6
|
"core.image_store",
|
@@ -14,11 +16,18 @@ class KuberKit::Actions::ConfigurationLoader
|
|
14
16
|
Contract Hash => Any
|
15
17
|
def call(options)
|
16
18
|
root_path = options[:path] || File.join(Dir.pwd, configs.kuber_kit_dirname)
|
19
|
+
|
20
|
+
# require config file first, in case if other dirs are overriden in config
|
21
|
+
config_file_path = File.join(root_path, APP_CONFIG_FILENAME)
|
22
|
+
if File.exists?(config_file_path)
|
23
|
+
require config_file_path
|
24
|
+
end
|
25
|
+
|
17
26
|
images_path = options[:images_path] || File.join(root_path, configs.images_dirname)
|
18
27
|
services_path = options[:services_path] || File.join(root_path, configs.services_dirname)
|
19
28
|
infra_path = options[:infra_path] || File.join(root_path, configs.infra_dirname)
|
20
29
|
configurations_path = options[:configurations_path] || File.join(root_path, configs.configurations_dirname)
|
21
|
-
configuration_name = options[:configuration]
|
30
|
+
configuration_name = ENV["KUBER_KIT_CONFIGURATION"] || options[:configuration]
|
22
31
|
|
23
32
|
logger.info "Launching kuber_kit with:"
|
24
33
|
logger.info " Root path: #{root_path.to_s.yellow}"
|
@@ -32,6 +41,10 @@ class KuberKit::Actions::ConfigurationLoader
|
|
32
41
|
ui.print_warning "WARNING", "KuberKit root path #{root_path} doesn't exist. You may want to pass it --path parameter."
|
33
42
|
end
|
34
43
|
|
44
|
+
if Gem::Version.new(KuberKit::VERSION) < Gem::Version.new(configs.kuber_kit_min_version)
|
45
|
+
raise KuberKit::Error, "The minimal required kuber_kit version is #{configs.kuber_kit_min_version}"
|
46
|
+
end
|
47
|
+
|
35
48
|
load_configurations(configurations_path, configuration_name)
|
36
49
|
load_infrastructure(infra_path)
|
37
50
|
|
@@ -43,6 +56,11 @@ class KuberKit::Actions::ConfigurationLoader
|
|
43
56
|
|
44
57
|
ui.create_task("Loading image definitions") do |task|
|
45
58
|
files = image_store.load_definitions(images_path)
|
59
|
+
|
60
|
+
configs.additional_images_paths.each do |path|
|
61
|
+
files += image_store.load_definitions(path)
|
62
|
+
end
|
63
|
+
|
46
64
|
task.update_title("Loaded #{files.count} image definitions")
|
47
65
|
end
|
48
66
|
|
@@ -1,16 +1,13 @@
|
|
1
1
|
class KuberKit::Actions::TemplateReader
|
2
2
|
include KuberKit::Import[
|
3
|
-
"core.template_store",
|
4
|
-
"template_reader.reader",
|
5
3
|
"shell.local_shell",
|
6
|
-
"ui"
|
4
|
+
"ui",
|
5
|
+
template_reader: "template_reader.action_handler",
|
7
6
|
]
|
8
7
|
|
9
8
|
Contract Symbol, Hash => Any
|
10
9
|
def call(template_name, options)
|
11
|
-
|
12
|
-
|
13
|
-
result = reader.read(local_shell, template)
|
10
|
+
result = template_reader.call(local_shell, template_name)
|
14
11
|
|
15
12
|
ui.print_info(template_name.to_s, result)
|
16
13
|
|
data/lib/kuber_kit/configs.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class KuberKit::Configs
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
4
|
+
AVAILABLE_CONFIGS = [
|
5
|
+
:image_dockerfile_name, :image_build_context_dir, :image_tag, :docker_ignore_list, :image_compile_dir,
|
6
|
+
:kuber_kit_dirname, :kuber_kit_min_version, :images_dirname, :services_dirname, :infra_dirname, :configurations_dirname,
|
7
|
+
:artifact_clone_dir, :service_config_dir, :deploy_strategy, :compile_simultaneous_limit,
|
8
|
+
:additional_images_paths
|
9
|
+
]
|
6
10
|
DOCKER_IGNORE_LIST = [
|
7
11
|
'Dockerfile',
|
8
12
|
'.DS_Store',
|
@@ -14,35 +18,61 @@ class KuberKit::Configs
|
|
14
18
|
'tmp',
|
15
19
|
'logs'
|
16
20
|
]
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
attr_accessor :image_dockerfile_name, :image_build_context_dir, :image_tag,
|
28
|
-
:docker_ignore_list, :image_compile_dir,
|
29
|
-
:kuber_kit_dirname, :images_dirname, :services_dirname, :infra_dirname, :configurations_dirname,
|
30
|
-
:artifact_clone_dir, :service_config_dir, :deploy_strategy, :compile_simultaneous_limit
|
21
|
+
|
22
|
+
AVAILABLE_CONFIGS.each do |config_name|
|
23
|
+
define_method(config_name) do
|
24
|
+
get(config_name.to_sym)
|
25
|
+
end
|
26
|
+
|
27
|
+
define_method(:"#{config_name}=") do |value|
|
28
|
+
set(config_name.to_sym, value)
|
29
|
+
end
|
30
|
+
end
|
31
31
|
|
32
32
|
def initialize
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
33
|
+
add_default_configs unless items.any?
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_default_configs
|
37
|
+
set :image_dockerfile_name, "Dockerfile"
|
38
|
+
set :image_build_context_dir, "build_context"
|
39
|
+
set :image_tag, 'latest'
|
40
|
+
set :image_compile_dir, "/tmp/kuber_kit/image_builds"
|
41
|
+
set :docker_ignore_list, DOCKER_IGNORE_LIST
|
42
|
+
set :kuber_kit_dirname, "kuber_kit"
|
43
|
+
set :kuber_kit_min_version, KuberKit::VERSION
|
44
|
+
set :images_dirname, "images"
|
45
|
+
set :services_dirname, "services"
|
46
|
+
set :infra_dirname, "infrastructure"
|
47
|
+
set :configurations_dirname, "configurations"
|
48
|
+
set :artifact_clone_dir, "/tmp/kuber_kit/artifacts"
|
49
|
+
set :service_config_dir, "/tmp/kuber_kit/services"
|
50
|
+
set :deploy_strategy, :kubernetes
|
51
|
+
set :compile_simultaneous_limit, 5
|
52
|
+
set :additional_images_paths, []
|
53
|
+
end
|
54
|
+
|
55
|
+
def items
|
56
|
+
@@items ||= {}
|
57
|
+
end
|
58
|
+
|
59
|
+
def set(key, value)
|
60
|
+
unless AVAILABLE_CONFIGS.include?(key)
|
61
|
+
raise ArgumentError, "#{key} is not a valid configuration key"
|
62
|
+
end
|
63
|
+
|
64
|
+
items[key] = value
|
65
|
+
end
|
66
|
+
|
67
|
+
def get(key)
|
68
|
+
unless AVAILABLE_CONFIGS.include?(key)
|
69
|
+
raise ArgumentError, "#{key} is not a valid configuration key"
|
70
|
+
end
|
71
|
+
|
72
|
+
items[key]
|
73
|
+
end
|
74
|
+
|
75
|
+
def reset!
|
76
|
+
@@items = {}
|
47
77
|
end
|
48
78
|
end
|
data/lib/kuber_kit/container.rb
CHANGED
@@ -117,6 +117,10 @@ class KuberKit::Container
|
|
117
117
|
KuberKit::Shell::Commands::DockerCommands.new
|
118
118
|
end
|
119
119
|
|
120
|
+
register "shell.docker_compose_commands" do
|
121
|
+
KuberKit::Shell::Commands::DockerComposeCommands.new
|
122
|
+
end
|
123
|
+
|
120
124
|
register "shell.git_commands" do
|
121
125
|
KuberKit::Shell::Commands::GitCommands.new
|
122
126
|
end
|
@@ -197,12 +201,16 @@ class KuberKit::Container
|
|
197
201
|
KuberKit::EnvFileReader::Strategies::ArtifactFile.new
|
198
202
|
end
|
199
203
|
|
204
|
+
register "template_reader.action_handler" do
|
205
|
+
KuberKit::TemplateReader::ActionHandler.new
|
206
|
+
end
|
207
|
+
|
200
208
|
register "template_reader.reader" do
|
201
209
|
KuberKit::TemplateReader::Reader.new
|
202
210
|
end
|
203
211
|
|
204
|
-
register "template_reader.
|
205
|
-
KuberKit::TemplateReader::
|
212
|
+
register "template_reader.strategies.artifact_file" do
|
213
|
+
KuberKit::TemplateReader::Strategies::ArtifactFile.new
|
206
214
|
end
|
207
215
|
|
208
216
|
register "service_deployer.action_handler" do
|
@@ -229,6 +237,10 @@ class KuberKit::Container
|
|
229
237
|
KuberKit::ServiceDeployer::Strategies::KubernetesRunner.new
|
230
238
|
end
|
231
239
|
|
240
|
+
register "service_deployer.strategies.docker_compose" do
|
241
|
+
KuberKit::ServiceDeployer::Strategies::DockerCompose.new
|
242
|
+
end
|
243
|
+
|
232
244
|
register "service_reader.action_handler" do
|
233
245
|
KuberKit::ServiceReader::ActionHandler.new
|
234
246
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
class KuberKit::Core::Configuration
|
2
2
|
attr_reader :name, :artifacts, :registries, :env_files, :templates, :kubeconfig_path,
|
3
|
-
:deploy_strategy, :deploy_namespace, :services_attributes, :build_servers
|
3
|
+
:deploy_strategy, :deploy_namespace, :services_attributes, :build_servers,
|
4
|
+
:global_build_vars
|
4
5
|
|
5
6
|
Contract KeywordArgs[
|
6
7
|
name: Symbol,
|
@@ -12,10 +13,12 @@ class KuberKit::Core::Configuration
|
|
12
13
|
deploy_strategy: Symbol,
|
13
14
|
deploy_namespace: Maybe[Symbol],
|
14
15
|
services_attributes: HashOf[Symbol => Hash],
|
15
|
-
build_servers: ArrayOf[KuberKit::Core::BuildServers::AbstractBuildServer]
|
16
|
+
build_servers: ArrayOf[KuberKit::Core::BuildServers::AbstractBuildServer],
|
17
|
+
global_build_vars: HashOf[Symbol => Any],
|
16
18
|
] => Any
|
17
19
|
def initialize(name:, artifacts:, registries:, env_files:, templates:, kubeconfig_path:,
|
18
|
-
deploy_strategy:, deploy_namespace:, services_attributes:, build_servers
|
20
|
+
deploy_strategy:, deploy_namespace:, services_attributes:, build_servers:,
|
21
|
+
global_build_vars:)
|
19
22
|
@name = name
|
20
23
|
@artifacts = artifacts
|
21
24
|
@registries = registries
|
@@ -24,11 +27,17 @@ class KuberKit::Core::Configuration
|
|
24
27
|
@kubeconfig_path = kubeconfig_path
|
25
28
|
@deploy_strategy = deploy_strategy
|
26
29
|
@deploy_namespace = deploy_namespace
|
27
|
-
@services_attributes = services_attributes
|
28
30
|
@build_servers = build_servers
|
31
|
+
@services_attributes = services_attributes
|
32
|
+
@global_build_vars = global_build_vars
|
29
33
|
end
|
30
34
|
|
31
35
|
def service_attributes(service_name)
|
32
36
|
services_attributes[service_name.to_sym] || {}
|
33
37
|
end
|
38
|
+
|
39
|
+
def global_build_args
|
40
|
+
puts "WARNING: global_build_args is deprecated, please use global_build_vars instead"
|
41
|
+
global_build_vars
|
42
|
+
end
|
34
43
|
end
|
@@ -27,7 +27,8 @@ class KuberKit::Core::ConfigurationDefinition
|
|
27
27
|
deploy_namespace: @deploy_namespace,
|
28
28
|
enabled_services: @enabled_services,
|
29
29
|
build_servers: @build_servers,
|
30
|
-
services_attributes:
|
30
|
+
services_attributes: @services_attributes,
|
31
|
+
global_build_vars: @global_build_vars,
|
31
32
|
)
|
32
33
|
end
|
33
34
|
|
@@ -93,9 +94,28 @@ class KuberKit::Core::ConfigurationDefinition
|
|
93
94
|
self
|
94
95
|
end
|
95
96
|
|
96
|
-
def enabled_services(
|
97
|
-
|
98
|
-
|
97
|
+
def enabled_services(services)
|
98
|
+
if services.is_a?(Hash)
|
99
|
+
@enabled_services += services.keys.map(&:to_sym)
|
100
|
+
@services_attributes = @services_attributes.merge(services)
|
101
|
+
return self
|
102
|
+
end
|
103
|
+
|
104
|
+
if services.is_a?(Array)
|
105
|
+
@enabled_services += services.map(&:to_sym)
|
106
|
+
return self
|
107
|
+
end
|
108
|
+
|
109
|
+
raise KuberKit::Error, "#enabled_services method accepts only Array or Hash"
|
110
|
+
end
|
111
|
+
|
112
|
+
def service_attributes(services)
|
113
|
+
@services_attributes = @services_attributes.merge(services)
|
114
|
+
self
|
115
|
+
end
|
116
|
+
|
117
|
+
def global_build_vars(variables)
|
118
|
+
@global_build_vars = variables
|
99
119
|
|
100
120
|
self
|
101
121
|
end
|
@@ -28,8 +28,9 @@ class KuberKit::Core::ConfigurationFactory
|
|
28
28
|
kubeconfig_path: configuration_attrs.kubeconfig_path,
|
29
29
|
deploy_strategy: configuration_attrs.deploy_strategy || configs.deploy_strategy,
|
30
30
|
deploy_namespace: configuration_attrs.deploy_namespace,
|
31
|
-
|
32
|
-
|
31
|
+
build_servers: build_servers,
|
32
|
+
services_attributes: configuration_attrs.services_attributes,
|
33
|
+
global_build_vars: configuration_attrs.global_build_vars || {},
|
33
34
|
)
|
34
35
|
end
|
35
36
|
|
@@ -5,12 +5,13 @@ class KuberKit::Core::ContextHelper::ContextHelperFactory
|
|
5
5
|
env_file_reader: "env_file_reader.action_handler"
|
6
6
|
]
|
7
7
|
|
8
|
-
def build_image_context(shell)
|
8
|
+
def build_image_context(shell, image)
|
9
9
|
KuberKit::Core::ContextHelper::ImageHelper.new(
|
10
10
|
image_store: image_store,
|
11
11
|
artifact_store: artifact_store,
|
12
12
|
shell: shell,
|
13
|
-
env_file_reader: env_file_reader
|
13
|
+
env_file_reader: env_file_reader,
|
14
|
+
image: image
|
14
15
|
)
|
15
16
|
end
|
16
17
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class KuberKit::Core::ContextHelper::ContextVars
|
2
|
+
attr_reader :parent, :parent_name
|
3
|
+
|
4
|
+
def initialize(context_vars, parent_name = nil, parent = nil)
|
5
|
+
@context_vars = context_vars
|
6
|
+
@parent_name = parent_name
|
7
|
+
@parent = parent
|
8
|
+
end
|
9
|
+
|
10
|
+
def method_missing(name, *args)
|
11
|
+
if args.size > 0
|
12
|
+
raise ArgumentError.new("context args does not accept any arguments")
|
13
|
+
end
|
14
|
+
|
15
|
+
value = @context_vars.fetch(name) do
|
16
|
+
raise(KuberKit::Error, "build arg '#{format_arg(name)}' is not defined, available args: #{@context_vars.inspect}")
|
17
|
+
end
|
18
|
+
|
19
|
+
if value.is_a?(Hash)
|
20
|
+
return self.class.new(value, name, self)
|
21
|
+
end
|
22
|
+
|
23
|
+
value
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def format_arg(name)
|
29
|
+
string = [@parent_name, name].compact.join(".")
|
30
|
+
parent = @parent
|
31
|
+
|
32
|
+
while parent do
|
33
|
+
string = [parent.parent_name, string].compact.join(".")
|
34
|
+
parent = parent.parent
|
35
|
+
end
|
36
|
+
|
37
|
+
string
|
38
|
+
end
|
39
|
+
end
|
@@ -1,2 +1,19 @@
|
|
1
1
|
class KuberKit::Core::ContextHelper::ImageHelper < KuberKit::Core::ContextHelper::BaseHelper
|
2
|
+
def initialize(image_store:, artifact_store:, shell:, env_file_reader:, image:)
|
3
|
+
super(
|
4
|
+
image_store: image_store,
|
5
|
+
artifact_store: artifact_store,
|
6
|
+
shell: shell,
|
7
|
+
env_file_reader: env_file_reader
|
8
|
+
)
|
9
|
+
@image = image
|
10
|
+
end
|
11
|
+
|
12
|
+
def image_name
|
13
|
+
@image.name.to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
def build_vars
|
17
|
+
KuberKit::Core::ContextHelper::ContextVars.new(@image.build_vars)
|
18
|
+
end
|
2
19
|
end
|
@@ -66,14 +66,14 @@ class KuberKit::Core::ImageDefinition
|
|
66
66
|
self
|
67
67
|
end
|
68
68
|
|
69
|
-
def before_build(&block)
|
70
|
-
@before_build_callback = block
|
69
|
+
def before_build(lambda_arg = nil, &block)
|
70
|
+
@before_build_callback = lambda_arg || block
|
71
71
|
|
72
72
|
self
|
73
73
|
end
|
74
74
|
|
75
|
-
def after_build(&block)
|
76
|
-
@after_build_callback = block
|
75
|
+
def after_build(lambda_arg = nil, &block)
|
76
|
+
@after_build_callback = lambda_arg || block
|
77
77
|
|
78
78
|
self
|
79
79
|
end
|
@@ -5,6 +5,11 @@ class KuberKit::EnvFileReader::Reader
|
|
5
5
|
"env_file_reader.strategies.artifact_file",
|
6
6
|
]
|
7
7
|
|
8
|
+
def initialize(**injected_deps)
|
9
|
+
super(injected_deps)
|
10
|
+
add_default_strategies
|
11
|
+
end
|
12
|
+
|
8
13
|
def use_reader(env_file_reader, env_file_class:)
|
9
14
|
@@readers ||= {}
|
10
15
|
|
@@ -16,8 +21,6 @@ class KuberKit::EnvFileReader::Reader
|
|
16
21
|
end
|
17
22
|
|
18
23
|
def read(shell, env_file)
|
19
|
-
add_default_readers
|
20
|
-
|
21
24
|
reader = @@readers[env_file.class]
|
22
25
|
|
23
26
|
raise ReaderNotFoundError, "Can't find reader for env file #{env_file}" if reader.nil?
|
@@ -25,11 +28,12 @@ class KuberKit::EnvFileReader::Reader
|
|
25
28
|
reader.read(shell, env_file)
|
26
29
|
end
|
27
30
|
|
28
|
-
def add_default_readers
|
29
|
-
use_reader(artifact_file, env_file_class: KuberKit::Core::EnvFiles::ArtifactFile)
|
30
|
-
end
|
31
|
-
|
32
31
|
def reset!
|
33
32
|
@@readers = {}
|
34
33
|
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def add_default_strategies
|
37
|
+
use_reader(artifact_file, env_file_class: KuberKit::Core::EnvFiles::ArtifactFile)
|
38
|
+
end
|
35
39
|
end
|
@@ -9,7 +9,7 @@ class KuberKit::ImageCompiler::Compiler
|
|
9
9
|
def compile(shell, image, builds_dir)
|
10
10
|
image_build_dir = File.join(builds_dir, image.name.to_s)
|
11
11
|
|
12
|
-
context_helper = context_helper_factory.build_image_context(shell)
|
12
|
+
context_helper = context_helper_factory.build_image_context(shell, image)
|
13
13
|
image_build_dir_creator.create(shell, image, image_build_dir, context_helper: context_helper)
|
14
14
|
|
15
15
|
image_builder.build(shell, image, image_build_dir, context_helper: context_helper)
|
@@ -4,9 +4,15 @@ class KuberKit::ServiceDeployer::Deployer
|
|
4
4
|
include KuberKit::Import[
|
5
5
|
"core.service_store",
|
6
6
|
"service_deployer.strategies.kubernetes",
|
7
|
-
"service_deployer.strategies.kubernetes_runner"
|
7
|
+
"service_deployer.strategies.kubernetes_runner",
|
8
|
+
"service_deployer.strategies.docker_compose"
|
8
9
|
]
|
9
10
|
|
11
|
+
def initialize(**injected_deps)
|
12
|
+
super(injected_deps)
|
13
|
+
add_default_strategies
|
14
|
+
end
|
15
|
+
|
10
16
|
def register_strategy(strategy_name, strategy)
|
11
17
|
@@strategies ||= {}
|
12
18
|
|
@@ -19,8 +25,6 @@ class KuberKit::ServiceDeployer::Deployer
|
|
19
25
|
|
20
26
|
Contract KuberKit::Shell::AbstractShell, KuberKit::Core::Service, Symbol => Any
|
21
27
|
def deploy(shell, service, strategy_name)
|
22
|
-
add_default_strategies
|
23
|
-
|
24
28
|
deployer = @@strategies[strategy_name]
|
25
29
|
|
26
30
|
raise StrategyNotFoundError, "Can't find strategy with name #{strategy_name}" if deployer.nil?
|
@@ -28,12 +32,14 @@ class KuberKit::ServiceDeployer::Deployer
|
|
28
32
|
deployer.deploy(shell, service)
|
29
33
|
end
|
30
34
|
|
31
|
-
def add_default_strategies
|
32
|
-
register_strategy(:kubernetes, kubernetes)
|
33
|
-
register_strategy(:kubernetes_runner, kubernetes_runner)
|
34
|
-
end
|
35
|
-
|
36
35
|
def reset!
|
37
36
|
@@strategies = {}
|
38
37
|
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def add_default_strategies
|
41
|
+
register_strategy(:kubernetes, kubernetes)
|
42
|
+
register_strategy(:kubernetes_runner, kubernetes_runner)
|
43
|
+
register_strategy(:docker_compose, docker_compose)
|
44
|
+
end
|
39
45
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class KuberKit::ServiceDeployer::Strategies::DockerCompose < KuberKit::ServiceDeployer::Strategies::Abstract
|
2
|
+
include KuberKit::Import[
|
3
|
+
"service_reader.reader",
|
4
|
+
"shell.docker_compose_commands",
|
5
|
+
"configs",
|
6
|
+
]
|
7
|
+
|
8
|
+
Contract KuberKit::Shell::AbstractShell, KuberKit::Core::Service => Any
|
9
|
+
def deploy(shell, service)
|
10
|
+
service_config = reader.read(shell, service)
|
11
|
+
config_path = "#{configs.service_config_dir}/#{service.name}.yml"
|
12
|
+
shell.write(config_path, service_config)
|
13
|
+
|
14
|
+
deployment_service_name = service.attribute(:deployment_service_name, default: service.name.to_s)
|
15
|
+
deployment_command_name = service.attribute(:deployment_command_name, default: "bash")
|
16
|
+
deployment_interactive = service.attribute(:deployment_interactive, default: false)
|
17
|
+
|
18
|
+
docker_compose_commands.run(shell, config_path,
|
19
|
+
service: deployment_service_name,
|
20
|
+
command: deployment_command_name,
|
21
|
+
interactive: deployment_interactive
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
@@ -19,9 +19,17 @@ class KuberKit::ServiceDeployer::Strategies::KubernetesRunner < KuberKit::Servic
|
|
19
19
|
|
20
20
|
deployment_delete_enabled = service.attribute(:deployment_delete_enabled, default: true)
|
21
21
|
if deployment_delete_enabled
|
22
|
-
|
22
|
+
delete_resource_if_exists(shell, deployment_resource_type, deployment_resource_name, kubeconfig_path: kubeconfig_path, namespace: deploy_namespace)
|
23
23
|
end
|
24
24
|
|
25
25
|
kubectl_commands.apply_file(shell, config_path, kubeconfig_path: kubeconfig_path, namespace: deploy_namespace)
|
26
26
|
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def delete_resource_if_exists(shell, resource_type, resource_name, kubeconfig_path:, namespace: )
|
30
|
+
unless kubectl_commands.resource_exists?(shell, resource_type, resource_name, kubeconfig_path: kubeconfig_path, namespace: namespace)
|
31
|
+
return false
|
32
|
+
end
|
33
|
+
kubectl_commands.delete_resource(shell, resource_type, resource_name, kubeconfig_path: kubeconfig_path, namespace: namespace)
|
34
|
+
end
|
27
35
|
end
|
@@ -6,6 +6,10 @@ class KuberKit::Shell::AbstractShell
|
|
6
6
|
raise KuberKit::NotImplementedError, "must be implemented"
|
7
7
|
end
|
8
8
|
|
9
|
+
def interactive!(command)
|
10
|
+
raise KuberKit::NotImplementedError, "must be implemented"
|
11
|
+
end
|
12
|
+
|
9
13
|
def read(file_path)
|
10
14
|
raise KuberKit::NotImplementedError, "must be implemented"
|
11
15
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class KuberKit::Shell::Commands::DockerComposeCommands
|
2
|
+
def run(shell, path, service:, command:, interactive: false)
|
3
|
+
command_parts = [
|
4
|
+
"docker-compose",
|
5
|
+
"-f #{path}",
|
6
|
+
"run",
|
7
|
+
service,
|
8
|
+
command
|
9
|
+
]
|
10
|
+
|
11
|
+
if interactive
|
12
|
+
shell.interactive!(command_parts.join(" "))
|
13
|
+
else
|
14
|
+
shell.exec!(command_parts.join(" "))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -16,9 +16,8 @@ class KuberKit::Shell::Commands::KubectlCommands
|
|
16
16
|
|
17
17
|
command_parts += Array(command_list)
|
18
18
|
|
19
|
-
# TODO: investigate how to do it with shell.
|
20
19
|
if interactive
|
21
|
-
|
20
|
+
shell.interactive!(command_parts.join(" "))
|
22
21
|
else
|
23
22
|
shell.exec!(command_parts.join(" "))
|
24
23
|
end
|
@@ -41,6 +40,17 @@ class KuberKit::Shell::Commands::KubectlCommands
|
|
41
40
|
kubectl_run(shell, command_parts, kubeconfig_path: kubeconfig_path, interactive: interactive, namespace: namespace)
|
42
41
|
end
|
43
42
|
|
43
|
+
def resource_exists?(shell, resource_type, resource_name, kubeconfig_path: nil, namespace: nil)
|
44
|
+
result = find_resources(shell, resource_type, resource_name, kubeconfig_path: kubeconfig_path, namespace: namespace)
|
45
|
+
result && result != ""
|
46
|
+
end
|
47
|
+
|
48
|
+
def find_resources(shell, resource_type, resource_name, jsonpath: ".items[*].metadata.name", kubeconfig_path: nil, namespace: nil)
|
49
|
+
command = %Q{get #{resource_type} --field-selector=metadata.name=#{resource_name} -o jsonpath='{#{jsonpath}}'}
|
50
|
+
|
51
|
+
kubectl_run(shell, command, kubeconfig_path: kubeconfig_path, namespace: namespace)
|
52
|
+
end
|
53
|
+
|
44
54
|
def delete_resource(shell, resource_type, resource_name, kubeconfig_path: nil, namespace: nil)
|
45
55
|
command = %Q{delete #{resource_type} #{resource_name}}
|
46
56
|
|
@@ -30,6 +30,16 @@ class KuberKit::Shell::LocalShell < KuberKit::Shell::AbstractShell
|
|
30
30
|
result
|
31
31
|
end
|
32
32
|
|
33
|
+
def interactive!(command, log_command: true)
|
34
|
+
command_number = command_counter.get_number.to_s.rjust(2, "0")
|
35
|
+
|
36
|
+
if log_command
|
37
|
+
logger.info("Interactive: [#{command_number}]: #{command.to_s.cyan}")
|
38
|
+
end
|
39
|
+
|
40
|
+
system(command)
|
41
|
+
end
|
42
|
+
|
33
43
|
def sync(local_path, remote_path, exclude: nil, delete: true)
|
34
44
|
rsync_commands.rsync(self, local_path, remote_path, exclude: exclude, delete: delete)
|
35
45
|
end
|
@@ -38,6 +38,10 @@ class KuberKit::Shell::SshShell < KuberKit::Shell::LocalShell
|
|
38
38
|
raise ShellError.new(e.message)
|
39
39
|
end
|
40
40
|
|
41
|
+
def interactive!(command, log_command: true)
|
42
|
+
raise "Currently interactive run is not supported for ssh shell."
|
43
|
+
end
|
44
|
+
|
41
45
|
def sync(local_path, remote_path, exclude: nil, delete: true)
|
42
46
|
rsync_commands.rsync(
|
43
47
|
local_shell, local_path, remote_path,
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class KuberKit::TemplateReader::ActionHandler
|
2
|
+
include KuberKit::Import[
|
3
|
+
"template_reader.reader",
|
4
|
+
"core.template_store",
|
5
|
+
]
|
6
|
+
|
7
|
+
Contract KuberKit::Shell::AbstractShell, Symbol => Any
|
8
|
+
def call(shell, template_name)
|
9
|
+
template = template_store.get(template_name)
|
10
|
+
|
11
|
+
reader.read(shell, template)
|
12
|
+
end
|
13
|
+
end
|
@@ -2,22 +2,25 @@ class KuberKit::TemplateReader::Reader
|
|
2
2
|
ReaderNotFoundError = Class.new(KuberKit::NotFoundError)
|
3
3
|
|
4
4
|
include KuberKit::Import[
|
5
|
-
"template_reader.
|
5
|
+
"template_reader.strategies.artifact_file",
|
6
6
|
]
|
7
7
|
|
8
|
+
def initialize(**injected_deps)
|
9
|
+
super(injected_deps)
|
10
|
+
add_default_strategies
|
11
|
+
end
|
12
|
+
|
8
13
|
def use_reader(template_reader, template_class:)
|
9
14
|
@@readers ||= {}
|
10
15
|
|
11
|
-
if !template_reader.is_a?(KuberKit::TemplateReader::
|
12
|
-
raise ArgumentError.new("should be an instance of KuberKit::TemplateReader::
|
16
|
+
if !template_reader.is_a?(KuberKit::TemplateReader::Strategies::Abstract)
|
17
|
+
raise ArgumentError.new("should be an instance of KuberKit::TemplateReader::Strategies::Abstract, got: #{template_reader.inspect}")
|
13
18
|
end
|
14
19
|
|
15
20
|
@@readers[template_class] = template_reader
|
16
21
|
end
|
17
22
|
|
18
23
|
def read(shell, template)
|
19
|
-
add_default_readers
|
20
|
-
|
21
24
|
reader = @@readers[template.class]
|
22
25
|
|
23
26
|
raise ReaderNotFoundError, "Can't find reader for template #{template}" if reader.nil?
|
@@ -25,11 +28,12 @@ class KuberKit::TemplateReader::Reader
|
|
25
28
|
reader.read(shell, template)
|
26
29
|
end
|
27
30
|
|
28
|
-
def add_default_readers
|
29
|
-
use_reader(artifact_file_reader, template_class: KuberKit::Core::Templates::ArtifactFile)
|
30
|
-
end
|
31
|
-
|
32
31
|
def reset!
|
33
32
|
@@readers = {}
|
34
33
|
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def add_default_strategies
|
37
|
+
use_reader(artifact_file, template_class: KuberKit::Core::Templates::ArtifactFile)
|
38
|
+
end
|
35
39
|
end
|
data/lib/kuber_kit/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kuber_kit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Iskander Khaziev
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-12-
|
11
|
+
date: 2020-12-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: contracts-lite
|
@@ -162,6 +162,7 @@ files:
|
|
162
162
|
- example/app_data/service.yml
|
163
163
|
- example/app_data/test.env
|
164
164
|
- example/app_data/test.txt
|
165
|
+
- example/config.rb
|
165
166
|
- example/configurations/review.rb
|
166
167
|
- example/images/app_sources/Dockerfile
|
167
168
|
- example/images/app_sources/build_context/source.rb
|
@@ -212,6 +213,7 @@ files:
|
|
212
213
|
- lib/kuber_kit/core/configuration_store.rb
|
213
214
|
- lib/kuber_kit/core/context_helper/base_helper.rb
|
214
215
|
- lib/kuber_kit/core/context_helper/context_helper_factory.rb
|
216
|
+
- lib/kuber_kit/core/context_helper/context_vars.rb
|
215
217
|
- lib/kuber_kit/core/context_helper/image_helper.rb
|
216
218
|
- lib/kuber_kit/core/context_helper/service_helper.rb
|
217
219
|
- lib/kuber_kit/core/env_files/abstract_env_file.rb
|
@@ -256,6 +258,7 @@ files:
|
|
256
258
|
- lib/kuber_kit/service_deployer/deployer.rb
|
257
259
|
- lib/kuber_kit/service_deployer/service_list_resolver.rb
|
258
260
|
- lib/kuber_kit/service_deployer/strategies/abstract.rb
|
261
|
+
- lib/kuber_kit/service_deployer/strategies/docker_compose.rb
|
259
262
|
- lib/kuber_kit/service_deployer/strategies/kubernetes.rb
|
260
263
|
- lib/kuber_kit/service_deployer/strategies/kubernetes_runner.rb
|
261
264
|
- lib/kuber_kit/service_deployer/strategy_detector.rb
|
@@ -265,15 +268,17 @@ files:
|
|
265
268
|
- lib/kuber_kit/shell/command_counter.rb
|
266
269
|
- lib/kuber_kit/shell/commands/bash_commands.rb
|
267
270
|
- lib/kuber_kit/shell/commands/docker_commands.rb
|
271
|
+
- lib/kuber_kit/shell/commands/docker_compose_commands.rb
|
268
272
|
- lib/kuber_kit/shell/commands/git_commands.rb
|
269
273
|
- lib/kuber_kit/shell/commands/kubectl_commands.rb
|
270
274
|
- lib/kuber_kit/shell/commands/rsync_commands.rb
|
271
275
|
- lib/kuber_kit/shell/local_shell.rb
|
272
276
|
- lib/kuber_kit/shell/ssh_session.rb
|
273
277
|
- lib/kuber_kit/shell/ssh_shell.rb
|
274
|
-
- lib/kuber_kit/template_reader/
|
275
|
-
- lib/kuber_kit/template_reader/artifact_file_reader.rb
|
278
|
+
- lib/kuber_kit/template_reader/action_handler.rb
|
276
279
|
- lib/kuber_kit/template_reader/reader.rb
|
280
|
+
- lib/kuber_kit/template_reader/strategies/abstract.rb
|
281
|
+
- lib/kuber_kit/template_reader/strategies/artifact_file.rb
|
277
282
|
- lib/kuber_kit/tools/file_presence_checker.rb
|
278
283
|
- lib/kuber_kit/tools/logger_factory.rb
|
279
284
|
- lib/kuber_kit/ui.rb
|
@@ -284,7 +289,7 @@ homepage: https://github.com/ArtStation/kuber_kit
|
|
284
289
|
licenses:
|
285
290
|
- MIT
|
286
291
|
metadata: {}
|
287
|
-
post_install_message:
|
292
|
+
post_install_message:
|
288
293
|
rdoc_options: []
|
289
294
|
require_paths:
|
290
295
|
- lib
|
@@ -300,7 +305,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
300
305
|
version: '0'
|
301
306
|
requirements: []
|
302
307
|
rubygems_version: 3.0.8
|
303
|
-
signing_key:
|
308
|
+
signing_key:
|
304
309
|
specification_version: 4
|
305
310
|
summary: Docker Containers Build & Deployment
|
306
311
|
test_files: []
|