kuber_kit 0.1.5 → 0.2.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/Gemfile.lock +4 -2
- data/README.md +16 -3
- data/TODO.md +5 -4
- data/example/app_data/env_file.yml +10 -0
- data/example/configurations/review.rb +2 -1
- data/example/images/app_sources/Dockerfile +1 -1
- data/example/images/ruby_app/image.rb +4 -4
- data/example/infrastructure/build_servers.rb +8 -0
- data/example/infrastructure/templates.rb +5 -0
- data/example/services/env_file.rb +6 -0
- data/kuber_kit.gemspec +1 -0
- data/lib/kuber_kit.rb +51 -23
- data/lib/kuber_kit/actions/configuration_loader.rb +11 -2
- data/lib/kuber_kit/actions/env_file_reader.rb +8 -5
- data/lib/kuber_kit/actions/image_compiler.rb +18 -22
- data/lib/kuber_kit/actions/kubectl_applier.rb +6 -1
- data/lib/kuber_kit/actions/kubectl_attacher.rb +19 -0
- data/lib/kuber_kit/actions/service_deployer.rb +13 -3
- data/lib/kuber_kit/actions/service_reader.rb +9 -6
- data/lib/kuber_kit/actions/template_reader.rb +5 -0
- data/lib/kuber_kit/cli.rb +51 -20
- data/lib/kuber_kit/configs.rb +24 -22
- data/lib/kuber_kit/container.rb +43 -19
- data/lib/kuber_kit/core/artifacts/artifact_store.rb +12 -23
- data/lib/kuber_kit/core/build_servers/abstract_build_server.rb +21 -0
- data/lib/kuber_kit/core/build_servers/build_server.rb +24 -0
- data/lib/kuber_kit/core/build_servers/build_server_store.rb +18 -0
- data/lib/kuber_kit/core/configuration.rb +14 -4
- data/lib/kuber_kit/core/configuration_definition.rb +22 -1
- data/lib/kuber_kit/core/configuration_factory.rb +11 -1
- data/lib/kuber_kit/core/configuration_store.rb +14 -24
- data/lib/kuber_kit/core/context_helper/base_helper.rb +12 -7
- data/lib/kuber_kit/core/context_helper/context_helper_factory.rb +11 -8
- data/lib/kuber_kit/core/context_helper/service_helper.rb +7 -6
- data/lib/kuber_kit/core/env_files/env_file_store.rb +8 -23
- data/lib/kuber_kit/core/image_store.rb +8 -18
- data/lib/kuber_kit/core/registries/registry_store.rb +8 -23
- data/lib/kuber_kit/core/service.rb +6 -2
- data/lib/kuber_kit/core/service_factory.rb +4 -1
- data/lib/kuber_kit/core/service_store.rb +13 -23
- data/lib/kuber_kit/core/store.rb +48 -0
- data/lib/kuber_kit/core/templates/template_store.rb +12 -23
- data/lib/kuber_kit/env_file_reader/action_handler.rb +12 -0
- data/lib/kuber_kit/env_file_reader/reader.rb +4 -4
- data/lib/kuber_kit/env_file_reader/{abstract_env_file_reader.rb → strategies/abstract.rb} +1 -1
- data/lib/kuber_kit/env_file_reader/{artifact_file_reader.rb → strategies/artifact_file.rb} +1 -1
- data/lib/kuber_kit/image_compiler/action_handler.rb +21 -0
- data/lib/kuber_kit/image_compiler/build_server_pool.rb +30 -0
- data/lib/kuber_kit/image_compiler/build_server_pool_factory.rb +13 -0
- data/lib/kuber_kit/image_compiler/compiler.rb +2 -5
- data/lib/kuber_kit/image_compiler/image_build_dir_creator.rb +13 -7
- data/lib/kuber_kit/image_compiler/image_dependency_resolver.rb +25 -5
- data/lib/kuber_kit/preprocessing/file_preprocessor.rb +5 -4
- data/lib/kuber_kit/preprocessing/text_preprocessor.rb +1 -1
- data/lib/kuber_kit/service_deployer/action_handler.rb +16 -0
- data/lib/kuber_kit/service_deployer/deployer.rb +28 -6
- data/lib/kuber_kit/service_deployer/strategies/abstract.rb +1 -1
- data/lib/kuber_kit/service_deployer/strategies/kubernetes.rb +9 -4
- data/lib/kuber_kit/service_deployer/strategy_detector.rb +6 -0
- data/lib/kuber_kit/service_reader/action_handler.rb +13 -0
- data/lib/kuber_kit/{service_deployer/service_reader.rb → service_reader/reader.rb} +1 -1
- data/lib/kuber_kit/shell/abstract_shell.rb +4 -0
- data/lib/kuber_kit/shell/{bash_commands.rb → commands/bash_commands.rb} +1 -1
- data/lib/kuber_kit/shell/{docker_commands.rb → commands/docker_commands.rb} +1 -1
- data/lib/kuber_kit/shell/{git_commands.rb → commands/git_commands.rb} +1 -1
- data/lib/kuber_kit/shell/{kubectl_commands.rb → commands/kubectl_commands.rb} +25 -1
- data/lib/kuber_kit/shell/commands/rsync_commands.rb +32 -0
- data/lib/kuber_kit/shell/local_shell.rb +24 -5
- data/lib/kuber_kit/shell/ssh_session.rb +60 -0
- data/lib/kuber_kit/shell/ssh_shell.rb +77 -0
- data/lib/kuber_kit/tools/file_presence_checker.rb +6 -2
- data/lib/kuber_kit/ui/interactive.rb +8 -0
- data/lib/kuber_kit/ui/simple.rb +6 -0
- data/lib/kuber_kit/version.rb +2 -2
- metadata +41 -13
- data/lib/kuber_kit/preprocessing/dir_preprocessor.rb +0 -19
- data/lib/kuber_kit/service_deployer/service_restarter.rb +0 -37
- data/lib/kuber_kit/shell/rsync_commands.rb +0 -20
- data/lib/kuber_kit/tools/files_sync.rb +0 -10
@@ -1,6 +1,7 @@
|
|
1
1
|
class KuberKit::Preprocessing::FilePreprocessor
|
2
2
|
include KuberKit::Import[
|
3
|
-
"preprocessing.text_preprocessor"
|
3
|
+
"preprocessing.text_preprocessor",
|
4
|
+
"shell.bash_commands"
|
4
5
|
]
|
5
6
|
|
6
7
|
PreprocessingError = Class.new(KuberKit::Error)
|
@@ -8,7 +9,7 @@ class KuberKit::Preprocessing::FilePreprocessor
|
|
8
9
|
def compile(shell, source_path, destination_path: nil, context_helper: nil)
|
9
10
|
destination_path ||= source_path
|
10
11
|
|
11
|
-
prepare_destination_dir(destination_path)
|
12
|
+
prepare_destination_dir(shell, destination_path)
|
12
13
|
|
13
14
|
template = shell.read(source_path)
|
14
15
|
content = text_preprocessor.compile(template, context_helper: context_helper)
|
@@ -27,7 +28,7 @@ class KuberKit::Preprocessing::FilePreprocessor
|
|
27
28
|
raise PreprocessingError, "Error while processing template #{source_path}.\r\n#{message}"
|
28
29
|
end
|
29
30
|
|
30
|
-
def prepare_destination_dir(destination_path)
|
31
|
-
|
31
|
+
def prepare_destination_dir(shell, destination_path)
|
32
|
+
bash_commands.mkdir_p(shell, File.dirname(destination_path))
|
32
33
|
end
|
33
34
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class KuberKit::ServiceDeployer::ActionHandler
|
2
|
+
include KuberKit::Import[
|
3
|
+
"service_deployer.deployer",
|
4
|
+
"service_deployer.strategy_detector",
|
5
|
+
"core.service_store",
|
6
|
+
]
|
7
|
+
|
8
|
+
Contract KuberKit::Shell::AbstractShell, Symbol => Any
|
9
|
+
def call(shell, service_name)
|
10
|
+
service = service_store.get_service(service_name)
|
11
|
+
|
12
|
+
strategy_name = strategy_detector.call(service)
|
13
|
+
|
14
|
+
deployer.deploy(shell, service, strategy_name)
|
15
|
+
end
|
16
|
+
end
|
@@ -1,15 +1,37 @@
|
|
1
1
|
class KuberKit::ServiceDeployer::Deployer
|
2
|
+
StrategyNotFoundError = Class.new(KuberKit::NotFoundError)
|
3
|
+
|
2
4
|
include KuberKit::Import[
|
3
|
-
"service_deployer.service_restarter",
|
4
5
|
"core.service_store",
|
6
|
+
"service_deployer.strategies.kubernetes"
|
5
7
|
]
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
9
|
+
def register_strategy(strategy_name, strategy)
|
10
|
+
@@strategies ||= {}
|
11
|
+
|
12
|
+
if !strategy.is_a?(KuberKit::ServiceDeployer::Strategies::Abstract)
|
13
|
+
raise ArgumentError.new("should be an instance of KuberKit::ServiceDeployer::Strategies::Abstract, got: #{strategy.inspect}")
|
14
|
+
end
|
15
|
+
|
16
|
+
@@strategies[strategy_name] = strategy
|
17
|
+
end
|
18
|
+
|
19
|
+
Contract KuberKit::Shell::AbstractShell, KuberKit::Core::Service, Symbol => Any
|
20
|
+
def deploy(shell, service, strategy_name)
|
21
|
+
add_default_strategies
|
10
22
|
|
11
|
-
|
23
|
+
deployer = @@strategies[strategy_name]
|
24
|
+
|
25
|
+
raise StrategyNotFoundError, "Can't find strategy with name #{strategy_name}" if deployer.nil?
|
26
|
+
|
27
|
+
deployer.deploy(shell, service)
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_default_strategies
|
31
|
+
register_strategy(:kubernetes, kubernetes)
|
32
|
+
end
|
12
33
|
|
13
|
-
|
34
|
+
def reset!
|
35
|
+
@@strategies = {}
|
14
36
|
end
|
15
37
|
end
|
@@ -1,18 +1,23 @@
|
|
1
1
|
class KuberKit::ServiceDeployer::Strategies::Kubernetes < KuberKit::ServiceDeployer::Strategies::Abstract
|
2
2
|
include KuberKit::Import[
|
3
|
-
"
|
3
|
+
"service_reader.reader",
|
4
4
|
"shell.kubectl_commands",
|
5
5
|
"configs",
|
6
6
|
]
|
7
7
|
|
8
8
|
Contract KuberKit::Shell::AbstractShell, KuberKit::Core::Service => Any
|
9
|
-
def
|
10
|
-
service_config =
|
9
|
+
def deploy(shell, service)
|
10
|
+
service_config = reader.read(shell, service)
|
11
11
|
config_path = "#{configs.service_config_dir}/#{service.name}.yml"
|
12
12
|
shell.write(config_path, service_config)
|
13
13
|
|
14
14
|
kubeconfig_path = KuberKit.current_configuration.kubeconfig_path
|
15
15
|
kubectl_commands.apply_file(shell, config_path, kubeconfig_path: kubeconfig_path)
|
16
|
-
|
16
|
+
|
17
|
+
deployment_restart_enabled = service.attribute(:deployment_restart_enabled, default: true)
|
18
|
+
deployment_restart_name = service.attribute(:deployment_restart_name, default: service.uri)
|
19
|
+
if deployment_restart_enabled
|
20
|
+
kubectl_commands.rolling_restart(shell, deployment_restart_name, kubeconfig_path: kubeconfig_path)
|
21
|
+
end
|
17
22
|
end
|
18
23
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class KuberKit::ServiceReader::ActionHandler
|
2
|
+
include KuberKit::Import[
|
3
|
+
"service_reader.reader",
|
4
|
+
"core.service_store",
|
5
|
+
]
|
6
|
+
|
7
|
+
Contract KuberKit::Shell::AbstractShell, Symbol => Any
|
8
|
+
def call(shell, service_name)
|
9
|
+
service = service_store.get_service(service_name)
|
10
|
+
|
11
|
+
reader.read(shell, service)
|
12
|
+
end
|
13
|
+
end
|
@@ -17,4 +17,8 @@ class KuberKit::Shell::AbstractShell
|
|
17
17
|
def recursive_list_files(path, name: nil)
|
18
18
|
raise KuberKit::NotImplementedError, "must be implemented"
|
19
19
|
end
|
20
|
+
|
21
|
+
def sync(local_path, remote_path, exclude: nil)
|
22
|
+
raise KuberKit::NotImplementedError, "must be implemented"
|
23
|
+
end
|
20
24
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'shellwords'
|
3
3
|
|
4
|
-
class KuberKit::Shell::KubectlCommands
|
4
|
+
class KuberKit::Shell::Commands::KubectlCommands
|
5
5
|
def apply_file(shell, file_path, kubeconfig_path: nil)
|
6
6
|
command_parts = []
|
7
7
|
if kubeconfig_path
|
@@ -13,6 +13,30 @@ class KuberKit::Shell::KubectlCommands
|
|
13
13
|
shell.exec!(command_parts.join(" "))
|
14
14
|
end
|
15
15
|
|
16
|
+
def exec(shell, pod_name, command, args: nil, kubeconfig_path: nil, interactive: false)
|
17
|
+
command_parts = []
|
18
|
+
|
19
|
+
if kubeconfig_path
|
20
|
+
command_parts << "KUBECONFIG=#{kubeconfig_path}"
|
21
|
+
end
|
22
|
+
|
23
|
+
command_parts << "kubectl exec"
|
24
|
+
|
25
|
+
if args
|
26
|
+
command_parts << args
|
27
|
+
end
|
28
|
+
|
29
|
+
command_parts << pod_name
|
30
|
+
command_parts << "-- #{command}"
|
31
|
+
|
32
|
+
# TODO: investigate how to do it with shell.
|
33
|
+
if interactive
|
34
|
+
system(command_parts.join(" "))
|
35
|
+
else
|
36
|
+
shell.exec!(command_parts.join(" "))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
16
40
|
def rolling_restart(shell, deployment_name, kubeconfig_path: nil)
|
17
41
|
patch_deployment(shell, deployment_name, {
|
18
42
|
spec: {
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class KuberKit::Shell::Commands::RsyncCommands
|
2
|
+
def rsync(shell, source_path, target_path, target_host: nil, exclude: nil, delete: true)
|
3
|
+
# Add a trailing slash to directory to have behavior similar to CP command
|
4
|
+
if path_is_directory?(source_path) && !source_path.end_with?("/")
|
5
|
+
source_path = "#{source_path}/"
|
6
|
+
end
|
7
|
+
|
8
|
+
if target_host
|
9
|
+
destination = "#{target_host}:#{target_path}"
|
10
|
+
else
|
11
|
+
destination = target_path
|
12
|
+
end
|
13
|
+
|
14
|
+
args = [source_path, destination]
|
15
|
+
if exclude
|
16
|
+
Array(exclude).each do |e|
|
17
|
+
args << "--exclude=#{e}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
if delete
|
22
|
+
args << "--delete"
|
23
|
+
end
|
24
|
+
|
25
|
+
shell.exec!(%Q{rsync -a #{args.join(' ')}})
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def path_is_directory?(path)
|
30
|
+
File.directory?(path)
|
31
|
+
end
|
32
|
+
end
|
@@ -3,21 +3,24 @@ require 'fileutils'
|
|
3
3
|
class KuberKit::Shell::LocalShell < KuberKit::Shell::AbstractShell
|
4
4
|
include KuberKit::Import[
|
5
5
|
"tools.logger",
|
6
|
-
"shell.command_counter"
|
6
|
+
"shell.command_counter",
|
7
|
+
"shell.rsync_commands",
|
7
8
|
]
|
8
9
|
|
9
|
-
def exec!(command)
|
10
|
+
def exec!(command, log_command: true)
|
10
11
|
command_number = command_counter.get_number.to_s.rjust(2, "0")
|
11
12
|
|
12
|
-
|
13
|
+
if log_command
|
14
|
+
logger.info("Execute: [#{command_number}]: #{command.to_s.cyan}")
|
15
|
+
end
|
13
16
|
|
14
17
|
result = nil
|
15
18
|
IO.popen(command, err: [:child, :out]) do |io|
|
16
19
|
result = io.read.chomp.strip
|
17
20
|
end
|
18
21
|
|
19
|
-
if result && result != ""
|
20
|
-
logger.info("Finished
|
22
|
+
if result && result != "" && log_command
|
23
|
+
logger.info("Finished [#{command_number}] with result: \n#{result.grey}")
|
21
24
|
end
|
22
25
|
|
23
26
|
if $?.exitstatus != 0
|
@@ -27,6 +30,10 @@ class KuberKit::Shell::LocalShell < KuberKit::Shell::AbstractShell
|
|
27
30
|
result
|
28
31
|
end
|
29
32
|
|
33
|
+
def sync(local_path, remote_path, exclude: nil)
|
34
|
+
rsync_commands.rsync(self, local_path, remote_path, exclude: exclude)
|
35
|
+
end
|
36
|
+
|
30
37
|
def read(file_path)
|
31
38
|
File.read(file_path)
|
32
39
|
end
|
@@ -41,6 +48,18 @@ class KuberKit::Shell::LocalShell < KuberKit::Shell::AbstractShell
|
|
41
48
|
true
|
42
49
|
end
|
43
50
|
|
51
|
+
def delete(file_path)
|
52
|
+
exec!("rm #{file_path}")
|
53
|
+
end
|
54
|
+
|
55
|
+
def file_exists?(file_path)
|
56
|
+
exec!("test -f #{file_path} && echo 'true' || echo 'false'", log_command: false) == 'true'
|
57
|
+
end
|
58
|
+
|
59
|
+
def dir_exists?(dir_path)
|
60
|
+
exec!("test -d #{dir_path} && echo 'true' || echo 'false'", log_command: false) == 'true'
|
61
|
+
end
|
62
|
+
|
44
63
|
def recursive_list_files(path, name: nil)
|
45
64
|
command = %Q{find -L #{path} -type f}
|
46
65
|
command += " -name #{name}" if name
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
|
3
|
+
class KuberKit::Shell::SshSession
|
4
|
+
SshSessionError = Class.new(KuberKit::Error)
|
5
|
+
|
6
|
+
attr_reader :session, :host, :user, :port
|
7
|
+
|
8
|
+
def initialize(host:, user:, port:)
|
9
|
+
@host = host
|
10
|
+
@user = user
|
11
|
+
@port = port
|
12
|
+
@session = Net::SSH.start(host, user, {port: port})
|
13
|
+
end
|
14
|
+
|
15
|
+
def connected?
|
16
|
+
!!@session
|
17
|
+
end
|
18
|
+
|
19
|
+
def disconnect
|
20
|
+
return unless connected?
|
21
|
+
@session.close
|
22
|
+
@session = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def exec!(command)
|
26
|
+
stdout_data = ''
|
27
|
+
stderr_data = ''
|
28
|
+
exit_code = nil
|
29
|
+
channel = session.open_channel do |ch|
|
30
|
+
ch.exec(command) do |ch, success|
|
31
|
+
if !success
|
32
|
+
raise SshSessionError, "Shell command failed: #{command}\r\n#{stdout_data}\r\n#{stderr_data}"
|
33
|
+
end
|
34
|
+
|
35
|
+
channel.on_data do |ch,data|
|
36
|
+
stdout_data += data
|
37
|
+
end
|
38
|
+
|
39
|
+
channel.on_extended_data do |ch,type,data|
|
40
|
+
stderr_data += data
|
41
|
+
end
|
42
|
+
|
43
|
+
channel.on_request('exit-status') do |ch,data|
|
44
|
+
exit_code = data.read_long
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
channel.wait
|
50
|
+
session.loop
|
51
|
+
|
52
|
+
stdout_data = stdout_data.chomp.strip
|
53
|
+
|
54
|
+
if exit_code != 0
|
55
|
+
raise SshSessionError, "Shell command failed: #{command}\r\n#{stdout_data}\r\n#{stderr_data}"
|
56
|
+
end
|
57
|
+
|
58
|
+
stdout_data
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
class KuberKit::Shell::SshShell < KuberKit::Shell::LocalShell
|
4
|
+
include KuberKit::Import[
|
5
|
+
"tools.logger",
|
6
|
+
"shell.command_counter",
|
7
|
+
"shell.rsync_commands",
|
8
|
+
"shell.local_shell"
|
9
|
+
]
|
10
|
+
|
11
|
+
def connect(host:, user:, port:)
|
12
|
+
@ssh_session = KuberKit::Shell::SshSession.new(host: host, user: user, port: port)
|
13
|
+
end
|
14
|
+
|
15
|
+
def connected?
|
16
|
+
@ssh_session && @ssh_session.connected?
|
17
|
+
end
|
18
|
+
|
19
|
+
def disconnect
|
20
|
+
@ssh_session.disconnect if @ssh_session
|
21
|
+
end
|
22
|
+
|
23
|
+
def exec!(command, log_command: true)
|
24
|
+
command_number = command_counter.get_number.to_s.rjust(2, "0")
|
25
|
+
|
26
|
+
if log_command
|
27
|
+
logger.info("#{ssh_session.host.green} > Execute: [#{command_number}]: #{command.to_s.cyan}")
|
28
|
+
end
|
29
|
+
|
30
|
+
result = ssh_session.exec!(command)
|
31
|
+
|
32
|
+
if result && result != "" && log_command
|
33
|
+
logger.info("#{ssh_session.host.green} > Finished [#{command_number}] with result: \n#{result.grey}")
|
34
|
+
end
|
35
|
+
|
36
|
+
result
|
37
|
+
rescue KuberKit::Shell::SshSession::SshSessionError => e
|
38
|
+
raise ShellError.new(e.message)
|
39
|
+
end
|
40
|
+
|
41
|
+
def sync(local_path, remote_path, exclude: nil)
|
42
|
+
rsync_commands.rsync(
|
43
|
+
local_shell, local_path, remote_path,
|
44
|
+
target_host: "#{ssh_session.user}@#{ssh_session.host}",
|
45
|
+
exclude: exclude
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
def read(file_path)
|
50
|
+
exec!("cat #{file_path}")
|
51
|
+
end
|
52
|
+
|
53
|
+
def write(file_path, content)
|
54
|
+
Tempfile.create do |file|
|
55
|
+
file << content
|
56
|
+
file.flush
|
57
|
+
sync(file.path, file_path)
|
58
|
+
end
|
59
|
+
|
60
|
+
logger.info("Created file #{file_path.to_s.cyan}\r\n#{content.grey}")
|
61
|
+
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def ssh_session
|
67
|
+
unless connected?
|
68
|
+
raise ArgumentError, "ssh session is not created, please call #connect"
|
69
|
+
end
|
70
|
+
|
71
|
+
@ssh_session
|
72
|
+
end
|
73
|
+
|
74
|
+
def ensure_directory_exists(file_path)
|
75
|
+
exec!("mkdir -p #{file_path}")
|
76
|
+
end
|
77
|
+
end
|