kuber_kit 0.1.4 → 0.1.9

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