kuber_kit 0.1.4 → 0.1.9

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +4 -2
  3. data/README.md +16 -3
  4. data/TODO.md +4 -4
  5. data/example/app_data/env_file.yml +10 -0
  6. data/example/configurations/review.rb +2 -1
  7. data/example/images/app_sources/Dockerfile +1 -1
  8. data/example/images/ruby_app/image.rb +4 -4
  9. data/example/infrastructure/build_servers.rb +8 -0
  10. data/example/infrastructure/templates.rb +5 -0
  11. data/example/services/env_file.rb +6 -0
  12. data/kuber_kit.gemspec +1 -0
  13. data/lib/kuber_kit.rb +50 -23
  14. data/lib/kuber_kit/actions/configuration_loader.rb +5 -0
  15. data/lib/kuber_kit/actions/env_file_reader.rb +8 -5
  16. data/lib/kuber_kit/actions/image_compiler.rb +18 -22
  17. data/lib/kuber_kit/actions/kubectl_applier.rb +6 -1
  18. data/lib/kuber_kit/actions/service_deployer.rb +13 -3
  19. data/lib/kuber_kit/actions/service_reader.rb +9 -6
  20. data/lib/kuber_kit/actions/template_reader.rb +5 -0
  21. data/lib/kuber_kit/cli.rb +12 -6
  22. data/lib/kuber_kit/configs.rb +24 -22
  23. data/lib/kuber_kit/container.rb +39 -19
  24. data/lib/kuber_kit/core/artifacts/artifact_store.rb +12 -23
  25. data/lib/kuber_kit/core/build_servers/abstract_build_server.rb +21 -0
  26. data/lib/kuber_kit/core/build_servers/build_server.rb +24 -0
  27. data/lib/kuber_kit/core/build_servers/build_server_store.rb +18 -0
  28. data/lib/kuber_kit/core/configuration.rb +14 -4
  29. data/lib/kuber_kit/core/configuration_definition.rb +22 -1
  30. data/lib/kuber_kit/core/configuration_factory.rb +11 -1
  31. data/lib/kuber_kit/core/configuration_store.rb +14 -24
  32. data/lib/kuber_kit/core/context_helper/base_helper.rb +12 -7
  33. data/lib/kuber_kit/core/context_helper/context_helper_factory.rb +11 -8
  34. data/lib/kuber_kit/core/context_helper/service_helper.rb +9 -4
  35. data/lib/kuber_kit/core/env_files/env_file_store.rb +8 -23
  36. data/lib/kuber_kit/core/image_store.rb +8 -18
  37. data/lib/kuber_kit/core/registries/registry_store.rb +8 -23
  38. data/lib/kuber_kit/core/service.rb +19 -3
  39. data/lib/kuber_kit/core/service_definition.rb +7 -0
  40. data/lib/kuber_kit/core/service_factory.rb +5 -1
  41. data/lib/kuber_kit/core/service_store.rb +13 -23
  42. data/lib/kuber_kit/core/store.rb +48 -0
  43. data/lib/kuber_kit/core/templates/template_store.rb +12 -23
  44. data/lib/kuber_kit/env_file_reader/action_handler.rb +12 -0
  45. data/lib/kuber_kit/env_file_reader/reader.rb +4 -4
  46. data/lib/kuber_kit/env_file_reader/{abstract_env_file_reader.rb → strategies/abstract.rb} +1 -1
  47. data/lib/kuber_kit/env_file_reader/{artifact_file_reader.rb → strategies/artifact_file.rb} +1 -1
  48. data/lib/kuber_kit/image_compiler/action_handler.rb +21 -0
  49. data/lib/kuber_kit/image_compiler/build_server_pool.rb +30 -0
  50. data/lib/kuber_kit/image_compiler/build_server_pool_factory.rb +13 -0
  51. data/lib/kuber_kit/image_compiler/compiler.rb +2 -5
  52. data/lib/kuber_kit/image_compiler/image_build_dir_creator.rb +13 -7
  53. data/lib/kuber_kit/image_compiler/image_dependency_resolver.rb +25 -5
  54. data/lib/kuber_kit/preprocessing/file_preprocessor.rb +5 -4
  55. data/lib/kuber_kit/preprocessing/text_preprocessor.rb +1 -1
  56. data/lib/kuber_kit/service_deployer/action_handler.rb +16 -0
  57. data/lib/kuber_kit/service_deployer/deployer.rb +28 -6
  58. data/lib/kuber_kit/service_deployer/strategies/abstract.rb +1 -1
  59. data/lib/kuber_kit/service_deployer/strategies/kubernetes.rb +9 -4
  60. data/lib/kuber_kit/service_deployer/strategy_detector.rb +6 -0
  61. data/lib/kuber_kit/service_reader/action_handler.rb +13 -0
  62. data/lib/kuber_kit/{service_deployer/service_reader.rb → service_reader/reader.rb} +1 -1
  63. data/lib/kuber_kit/shell/abstract_shell.rb +4 -0
  64. data/lib/kuber_kit/shell/{bash_commands.rb → commands/bash_commands.rb} +1 -1
  65. data/lib/kuber_kit/shell/{docker_commands.rb → commands/docker_commands.rb} +1 -1
  66. data/lib/kuber_kit/shell/{git_commands.rb → commands/git_commands.rb} +1 -1
  67. data/lib/kuber_kit/shell/{kubectl_commands.rb → commands/kubectl_commands.rb} +1 -1
  68. data/lib/kuber_kit/shell/{rsync_commands.rb → commands/rsync_commands.rb} +9 -3
  69. data/lib/kuber_kit/shell/local_shell.rb +24 -5
  70. data/lib/kuber_kit/shell/ssh_session.rb +60 -0
  71. data/lib/kuber_kit/shell/ssh_shell.rb +77 -0
  72. data/lib/kuber_kit/tools/file_presence_checker.rb +6 -2
  73. data/lib/kuber_kit/version.rb +1 -1
  74. metadata +40 -13
  75. data/lib/kuber_kit/preprocessing/dir_preprocessor.rb +0 -19
  76. data/lib/kuber_kit/service_deployer/service_restarter.rb +0 -37
  77. data/lib/kuber_kit/tools/files_sync.rb +0 -10
@@ -0,0 +1,48 @@
1
+ class KuberKit::Core::Store
2
+ NotFoundError = Class.new(KuberKit::NotFoundError)
3
+ AlreadyAddedError = Class.new(KuberKit::Error)
4
+
5
+ attr_reader :object_class_name
6
+
7
+ def initialize(object_class_name)
8
+ @object_class_name = object_class_name
9
+ end
10
+
11
+ def add(item_name, item)
12
+ unless item.is_a?(object_class_name)
13
+ raise ArgumentError.new("#{self.object_class_name}: should be an instance of #{object_class_name}, got: #{item.inspect}")
14
+ end
15
+
16
+ unless items[item_name].nil?
17
+ raise AlreadyAddedError, "#{self.object_class_name}: item with name #{item_name} was already added"
18
+ end
19
+
20
+ items[item_name] = item
21
+ end
22
+
23
+ def get(item_name)
24
+ item = items[item_name]
25
+
26
+ if item.nil?
27
+ raise NotFoundError, "#{self.object_class_name}: item '#{item_name}' not found"
28
+ end
29
+
30
+ item
31
+ end
32
+
33
+ def items
34
+ @items ||= {}
35
+ end
36
+
37
+ def reset!
38
+ @items = {}
39
+ end
40
+
41
+ def size
42
+ items.count
43
+ end
44
+
45
+ def exists?(name)
46
+ !items[name].nil?
47
+ end
48
+ end
@@ -1,19 +1,6 @@
1
1
  class KuberKit::Core::Templates::TemplateStore
2
- NotFoundError = Class.new(KuberKit::NotFoundError)
3
- AlreadyAddedError = Class.new(KuberKit::Error)
4
-
5
2
  def add(template)
6
- @@templates ||= {}
7
-
8
- if !template.is_a?(KuberKit::Core::Templates::AbstractTemplate)
9
- raise ArgumentError.new("should be an instance of KuberKit::Core::Templates::AbstractTemplate, got: #{template.inspect}")
10
- end
11
-
12
- unless @@templates[template.name].nil?
13
- raise AlreadyAddedError, "template #{template.name} was already added"
14
- end
15
-
16
- @@templates[template.name] = template
3
+ store.add(template.name, template)
17
4
  end
18
5
 
19
6
  def get(template_name)
@@ -24,14 +11,7 @@ class KuberKit::Core::Templates::TemplateStore
24
11
  end
25
12
 
26
13
  def get_global(template_name)
27
- @@templates ||= {}
28
- template = @@templates[template_name]
29
-
30
- if template.nil?
31
- raise NotFoundError, "template '#{template_name}' not found"
32
- end
33
-
34
- template
14
+ store.get(template_name)
35
15
  end
36
16
 
37
17
  def get_from_configuration(template_name)
@@ -40,6 +20,15 @@ class KuberKit::Core::Templates::TemplateStore
40
20
  end
41
21
 
42
22
  def reset!
43
- @@templates = {}
23
+ store.reset!
24
+ end
25
+
26
+ def exists?(template_name)
27
+ store.exists?(template_name)
44
28
  end
29
+
30
+ private
31
+ def store
32
+ @@store ||= KuberKit::Core::Store.new(KuberKit::Core::Templates::AbstractTemplate)
33
+ end
45
34
  end
@@ -0,0 +1,12 @@
1
+ class KuberKit::EnvFileReader::ActionHandler
2
+ include KuberKit::Import[
3
+ "env_file_reader.reader",
4
+ "core.env_file_store",
5
+ ]
6
+
7
+ Contract KuberKit::Shell::AbstractShell, Symbol => Any
8
+ def call(shell, env_file_name)
9
+ env_file = env_file_store.get(env_file_name)
10
+ reader.read(shell, env_file)
11
+ end
12
+ end
@@ -2,14 +2,14 @@ class KuberKit::EnvFileReader::Reader
2
2
  ReaderNotFoundError = Class.new(KuberKit::NotFoundError)
3
3
 
4
4
  include KuberKit::Import[
5
- "env_file_reader.artifact_file_reader",
5
+ "env_file_reader.strategies.artifact_file",
6
6
  ]
7
7
 
8
8
  def use_reader(env_file_reader, env_file_class:)
9
9
  @@readers ||= {}
10
10
 
11
- if !env_file_reader.is_a?(KuberKit::EnvFileReader::AbstractEnvFileReader)
12
- raise ArgumentError.new("should be an instance of KuberKit::EnvFileReader::AbstractEnvFileReader, got: #{env_file_reader.inspect}")
11
+ if !env_file_reader.is_a?(KuberKit::EnvFileReader::Strategies::Abstract)
12
+ raise ArgumentError.new("should be an instance of KuberKit::EnvFileReader::Strategies::Abstract, got: #{env_file_reader.inspect}")
13
13
  end
14
14
 
15
15
  @@readers[env_file_class] = env_file_reader
@@ -26,7 +26,7 @@ class KuberKit::EnvFileReader::Reader
26
26
  end
27
27
 
28
28
  def add_default_readers
29
- use_reader(artifact_file_reader, env_file_class: KuberKit::Core::EnvFiles::ArtifactFile)
29
+ use_reader(artifact_file, env_file_class: KuberKit::Core::EnvFiles::ArtifactFile)
30
30
  end
31
31
 
32
32
  def reset!
@@ -1,4 +1,4 @@
1
- class KuberKit::EnvFileReader::AbstractEnvFileReader
1
+ class KuberKit::EnvFileReader::Strategies::Abstract
2
2
  def read(shell, env_file)
3
3
  raise KuberKit::NotImplementedError, "must be implemented"
4
4
  end
@@ -1,4 +1,4 @@
1
- class KuberKit::EnvFileReader::ArtifactFileReader < KuberKit::EnvFileReader::AbstractEnvFileReader
1
+ class KuberKit::EnvFileReader::Strategies::ArtifactFile < KuberKit::EnvFileReader::Strategies::Abstract
2
2
  include KuberKit::Import[
3
3
  "core.artifact_store"
4
4
  ]
@@ -0,0 +1,21 @@
1
+ class KuberKit::ImageCompiler::ActionHandler
2
+ include KuberKit::Import[
3
+ "image_compiler.compiler",
4
+ "core.image_store",
5
+ "configs",
6
+ ]
7
+
8
+ Contract KuberKit::Shell::AbstractShell, Symbol, String => Any
9
+ def call(shell, image_name, build_id)
10
+ image = image_store.get_image(image_name)
11
+
12
+ compile_dir = generate_compile_dir(build_id: build_id)
13
+
14
+ compiler.compile(shell, image, compile_dir)
15
+ end
16
+
17
+ private
18
+ def generate_compile_dir(build_id:)
19
+ File.join(configs.image_compile_dir, build_id)
20
+ end
21
+ end
@@ -0,0 +1,30 @@
1
+ class KuberKit::ImageCompiler::BuildServerPool
2
+ attr_reader :ssh_shells, :local_shell
3
+
4
+ def initialize(local_shell:, build_servers:, ssh_shell_class:)
5
+ @local_shell = local_shell
6
+ @ssh_shell_class = ssh_shell_class
7
+ @ssh_shells = connect_to_ssh_shells(build_servers)
8
+ end
9
+
10
+ def get_shell
11
+ if @ssh_shells.any?
12
+ @ssh_shells.sample
13
+ else
14
+ @local_shell
15
+ end
16
+ end
17
+
18
+ def disconnect_all
19
+ @ssh_shells.each(&:disconnect)
20
+ end
21
+
22
+ private
23
+ def connect_to_ssh_shells(build_servers)
24
+ build_servers.map do |bs|
25
+ shell = @ssh_shell_class.new
26
+ shell.connect(host: bs.host, user: bs.user, port: bs.port)
27
+ shell
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ class KuberKit::ImageCompiler::BuildServerPoolFactory
2
+ include KuberKit::Import[
3
+ "shell.local_shell",
4
+ ]
5
+
6
+ def create(ssh_shell_class: KuberKit::Shell::SshShell)
7
+ KuberKit::ImageCompiler::BuildServerPool.new(
8
+ local_shell: local_shell,
9
+ build_servers: KuberKit.current_configuration.build_servers,
10
+ ssh_shell_class: ssh_shell_class,
11
+ )
12
+ end
13
+ end
@@ -3,13 +3,10 @@ class KuberKit::ImageCompiler::Compiler
3
3
  "image_compiler.image_build_dir_creator",
4
4
  "image_compiler.image_builder",
5
5
  "core.context_helper_factory",
6
- "core.image_store"
7
6
  ]
8
7
 
9
- Contract KuberKit::Shell::AbstractShell, Symbol, String => Any
10
- def compile(shell, image_name, builds_dir)
11
- image = image_store.get_image(image_name)
12
-
8
+ Contract KuberKit::Shell::AbstractShell, KuberKit::Core::Image, String => Any
9
+ def compile(shell, image, builds_dir)
13
10
  image_build_dir = File.join(builds_dir, image.name.to_s)
14
11
 
15
12
  context_helper = context_helper_factory.build_image_context(shell)
@@ -1,8 +1,8 @@
1
1
  class KuberKit::ImageCompiler::ImageBuildDirCreator
2
2
  include KuberKit::Import[
3
- "preprocessing.dir_preprocessor",
4
3
  "preprocessing.file_preprocessor",
5
4
  "shell.bash_commands",
5
+ "shell.local_shell",
6
6
  "configs"
7
7
  ]
8
8
 
@@ -14,16 +14,22 @@ class KuberKit::ImageCompiler::ImageBuildDirCreator
14
14
  bash_commands.mkdir_p(shell, build_dir)
15
15
 
16
16
  if image.build_context_dir
17
- dir_preprocessor.compile(
18
- shell, image.build_context_dir, build_dir,
19
- context_helper: context_helper
20
- )
17
+ # Sync build context and then preprocess
18
+ shell.sync(image.build_context_dir, build_dir)
19
+
20
+ shell.recursive_list_files(build_dir).each do |file_path|
21
+ file_preprocessor.compile(
22
+ shell, file_path,
23
+ context_helper: context_helper
24
+ )
25
+ end
21
26
  end
22
27
 
28
+ # Sync dockerfile and then preprocess
23
29
  target_dockerfile = File.join(build_dir, configs.image_dockerfile_name)
30
+ shell.sync(image.dockerfile_path, target_dockerfile)
24
31
  file_preprocessor.compile(
25
- shell, image.dockerfile_path,
26
- destination_path: target_dockerfile,
32
+ shell, target_dockerfile,
27
33
  context_helper: context_helper
28
34
  )
29
35
 
@@ -3,20 +3,40 @@ class KuberKit::ImageCompiler::ImageDependencyResolver
3
3
  DependencyNotFoundError = Class.new(KuberKit::NotFoundError)
4
4
 
5
5
  include KuberKit::Import[
6
- "core.image_store"
6
+ "core.image_store",
7
+ "configs"
7
8
  ]
9
+
10
+ Contract Or[Symbol, ArrayOf[Symbol]], Proc => Any
11
+ def each_with_deps(image_names, &block)
12
+ compile_limit = configs.compile_simultaneous_limit
13
+
14
+ resolved_dependencies = []
15
+ next_dependencies = get_next(image_names, limit: compile_limit)
16
+
17
+ while (next_dependencies - resolved_dependencies).any?
18
+ block.call(next_dependencies)
19
+ resolved_dependencies += next_dependencies
20
+ next_dependencies = get_next(image_names, resolved: resolved_dependencies, limit: compile_limit)
21
+ end
22
+
23
+ block.call(image_names - resolved_dependencies)
24
+ end
8
25
 
9
- Contract Any, KeywordArgs[
10
- resolved: Optional[ArrayOf[Symbol]]
26
+ Contract Or[Symbol, ArrayOf[Symbol]], KeywordArgs[
27
+ resolved: Optional[ArrayOf[Symbol]],
28
+ limit: Optional[Maybe[Num]]
11
29
  ] => Any
12
- def get_next(image_names, resolved: [])
30
+ def get_next(image_names, resolved: [], limit: nil)
13
31
  deps = Array(image_names).map { |i| get_recursive_deps(i) }.flatten.uniq
14
32
 
15
33
  ready_to_resolve = deps.select do |dep_name|
16
34
  unresolved_deps = get_deps(dep_name) - resolved
17
35
  unresolved_deps.empty?
18
36
  end
19
- ready_to_resolve - resolved
37
+ unresolved_deps = ready_to_resolve - resolved
38
+ unresolved_deps = unresolved_deps.take(limit) if limit
39
+ unresolved_deps
20
40
  end
21
41
 
22
42
  def get_recursive_deps(image_name, dependency_tree: [])
@@ -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
- FileUtils.mkdir_p(File.dirname(destination_path))
31
+ def prepare_destination_dir(shell, destination_path)
32
+ bash_commands.mkdir_p(shell, File.dirname(destination_path))
32
33
  end
33
34
  end
@@ -2,6 +2,6 @@ require 'erb'
2
2
 
3
3
  class KuberKit::Preprocessing::TextPreprocessor
4
4
  def compile(template, context_helper: nil)
5
- ERB.new(template).result(context_helper&.get_binding)
5
+ ERB.new(template, nil, '-').result(context_helper&.get_binding)
6
6
  end
7
7
  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
- Contract KuberKit::Shell::AbstractShell, Symbol => Any
8
- def deploy(shell, service_name)
9
- service = service_store.get_service(service_name)
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
- strategy_name = KuberKit.current_configuration.deploy_strategy
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
- service_restarter.restart(shell, service, strategy_name)
34
+ def reset!
35
+ @@strategies = {}
14
36
  end
15
37
  end
@@ -1,5 +1,5 @@
1
1
  class KuberKit::ServiceDeployer::Strategies::Abstract
2
- def restart(shell, service)
2
+ def deploy(shell, service)
3
3
  raise KuberKit::NotImplementedError, "must be implemented"
4
4
  end
5
5
  end
@@ -1,18 +1,23 @@
1
1
  class KuberKit::ServiceDeployer::Strategies::Kubernetes < KuberKit::ServiceDeployer::Strategies::Abstract
2
2
  include KuberKit::Import[
3
- "service_deployer.service_reader",
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 restart(shell, service)
10
- service_config = service_reader.read(shell, service)
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
- kubectl_commands.rolling_restart(shell, service.uri, kubeconfig_path: kubeconfig_path)
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