kuber_kit 0.1.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.
Files changed (118) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +3 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +7 -0
  7. data/Gemfile +8 -0
  8. data/Gemfile.lock +67 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +19 -0
  11. data/Rakefile +6 -0
  12. data/TODO.md +2 -0
  13. data/bin/console +14 -0
  14. data/bin/kit +8 -0
  15. data/example/app_data/service.yml +7 -0
  16. data/example/app_data/test.env +2 -0
  17. data/example/app_data/test.txt +1 -0
  18. data/example/configurations/review.rb +4 -0
  19. data/example/images/app_sources/Dockerfile +6 -0
  20. data/example/images/app_sources/build_context/source.rb +2 -0
  21. data/example/images/app_sources/image.rb +4 -0
  22. data/example/images/ruby/Dockerfile +1 -0
  23. data/example/images/ruby/image.rb +3 -0
  24. data/example/images/ruby_app/Dockerfile +11 -0
  25. data/example/images/ruby_app/build_context/example_file.txt +2 -0
  26. data/example/images/ruby_app/image.rb +15 -0
  27. data/example/images/ruby_app2/Dockerfile +8 -0
  28. data/example/images/ruby_app2/build_context/example_file.txt +2 -0
  29. data/example/images/ruby_app2/image.rb +4 -0
  30. data/example/infrastructure/artifacts.rb +13 -0
  31. data/example/infrastructure/env_files.rb +4 -0
  32. data/example/infrastructure/registries.rb +4 -0
  33. data/example/infrastructure/services.rb +3 -0
  34. data/example/infrastructure/templates.rb +4 -0
  35. data/kuber_kit.gemspec +35 -0
  36. data/lib/kuber_kit.rb +205 -0
  37. data/lib/kuber_kit/actions/configuration_loader.rb +74 -0
  38. data/lib/kuber_kit/actions/env_file_reader.rb +17 -0
  39. data/lib/kuber_kit/actions/image_compiler.rb +56 -0
  40. data/lib/kuber_kit/actions/kubectl_applier.rb +17 -0
  41. data/lib/kuber_kit/actions/service_applier.rb +39 -0
  42. data/lib/kuber_kit/actions/service_reader.rb +17 -0
  43. data/lib/kuber_kit/actions/template_reader.rb +17 -0
  44. data/lib/kuber_kit/artifacts_sync/abstract_artifact_resolver.rb +5 -0
  45. data/lib/kuber_kit/artifacts_sync/artifacts_updater.rb +42 -0
  46. data/lib/kuber_kit/artifacts_sync/git_artifact_resolver.rb +31 -0
  47. data/lib/kuber_kit/artifacts_sync/null_artifact_resolver.rb +7 -0
  48. data/lib/kuber_kit/cli.rb +72 -0
  49. data/lib/kuber_kit/configs.rb +42 -0
  50. data/lib/kuber_kit/container.rb +207 -0
  51. data/lib/kuber_kit/core/artifacts/abstract_artifact.rb +13 -0
  52. data/lib/kuber_kit/core/artifacts/artifact_store.rb +45 -0
  53. data/lib/kuber_kit/core/artifacts/git.rb +22 -0
  54. data/lib/kuber_kit/core/artifacts/local.rb +14 -0
  55. data/lib/kuber_kit/core/configuration.rb +20 -0
  56. data/lib/kuber_kit/core/configuration_definition.rb +67 -0
  57. data/lib/kuber_kit/core/configuration_definition_factory.rb +5 -0
  58. data/lib/kuber_kit/core/configuration_factory.rb +61 -0
  59. data/lib/kuber_kit/core/configuration_store.rb +72 -0
  60. data/lib/kuber_kit/core/context_helper/base_helper.rb +28 -0
  61. data/lib/kuber_kit/core/context_helper/context_helper_factory.rb +23 -0
  62. data/lib/kuber_kit/core/context_helper/image_helper.rb +2 -0
  63. data/lib/kuber_kit/core/context_helper/service_helper.rb +19 -0
  64. data/lib/kuber_kit/core/env_files/abstract_env_file.rb +9 -0
  65. data/lib/kuber_kit/core/env_files/artifact_file.rb +9 -0
  66. data/lib/kuber_kit/core/env_files/env_file_store.rb +45 -0
  67. data/lib/kuber_kit/core/image.rb +39 -0
  68. data/lib/kuber_kit/core/image_definition.rb +85 -0
  69. data/lib/kuber_kit/core/image_definition_factory.rb +5 -0
  70. data/lib/kuber_kit/core/image_factory.rb +40 -0
  71. data/lib/kuber_kit/core/image_store.rb +60 -0
  72. data/lib/kuber_kit/core/registries/abstract_registry.rb +25 -0
  73. data/lib/kuber_kit/core/registries/registry.rb +24 -0
  74. data/lib/kuber_kit/core/registries/registry_store.rb +49 -0
  75. data/lib/kuber_kit/core/service.rb +14 -0
  76. data/lib/kuber_kit/core/service_definition.rb +33 -0
  77. data/lib/kuber_kit/core/service_definition_factory.rb +5 -0
  78. data/lib/kuber_kit/core/service_factory.rb +17 -0
  79. data/lib/kuber_kit/core/service_store.rb +68 -0
  80. data/lib/kuber_kit/core/templates/abstract_template.rb +9 -0
  81. data/lib/kuber_kit/core/templates/artifact_file.rb +9 -0
  82. data/lib/kuber_kit/core/templates/template_store.rb +45 -0
  83. data/lib/kuber_kit/env_file_reader/abstract_env_file_reader.rb +5 -0
  84. data/lib/kuber_kit/env_file_reader/artifact_file_reader.rb +86 -0
  85. data/lib/kuber_kit/env_file_reader/reader.rb +35 -0
  86. data/lib/kuber_kit/extensions/colored_string.rb +43 -0
  87. data/lib/kuber_kit/extensions/contracts.rb +4 -0
  88. data/lib/kuber_kit/extensions/indocker_compat.rb +19 -0
  89. data/lib/kuber_kit/extensions/inspectable.rb +10 -0
  90. data/lib/kuber_kit/image_compiler/compiler.rb +21 -0
  91. data/lib/kuber_kit/image_compiler/image_build_dir_creator.rb +37 -0
  92. data/lib/kuber_kit/image_compiler/image_builder.rb +26 -0
  93. data/lib/kuber_kit/image_compiler/image_dependency_resolver.rb +40 -0
  94. data/lib/kuber_kit/image_compiler/version_tag_builder.rb +5 -0
  95. data/lib/kuber_kit/preprocessing/dir_preprocessor.rb +19 -0
  96. data/lib/kuber_kit/preprocessing/file_preprocessor.rb +33 -0
  97. data/lib/kuber_kit/preprocessing/text_preprocessor.rb +7 -0
  98. data/lib/kuber_kit/service_deployer/service_list_resolver.rb +56 -0
  99. data/lib/kuber_kit/service_deployer/service_reader.rb +20 -0
  100. data/lib/kuber_kit/shell/abstract_shell.rb +20 -0
  101. data/lib/kuber_kit/shell/bash_commands.rb +25 -0
  102. data/lib/kuber_kit/shell/command_counter.rb +19 -0
  103. data/lib/kuber_kit/shell/docker_commands.rb +16 -0
  104. data/lib/kuber_kit/shell/git_commands.rb +28 -0
  105. data/lib/kuber_kit/shell/kubectl_commands.rb +12 -0
  106. data/lib/kuber_kit/shell/local_shell.rb +64 -0
  107. data/lib/kuber_kit/shell/rsync_commands.rb +20 -0
  108. data/lib/kuber_kit/template_reader/abstract_template_reader.rb +5 -0
  109. data/lib/kuber_kit/template_reader/artifact_file_reader.rb +14 -0
  110. data/lib/kuber_kit/template_reader/reader.rb +35 -0
  111. data/lib/kuber_kit/tools/file_presence_checker.rb +25 -0
  112. data/lib/kuber_kit/tools/files_sync.rb +10 -0
  113. data/lib/kuber_kit/tools/logger_factory.rb +34 -0
  114. data/lib/kuber_kit/ui.rb +18 -0
  115. data/lib/kuber_kit/ui/interactive.rb +44 -0
  116. data/lib/kuber_kit/ui/simple.rb +75 -0
  117. data/lib/kuber_kit/version.rb +3 -0
  118. metadata +273 -0
@@ -0,0 +1,4 @@
1
+ class Object
2
+ include Contracts::Core
3
+ include Contracts::Builtin
4
+ end
@@ -0,0 +1,19 @@
1
+ # Aliases for compatibility with Indocker
2
+ module KuberKit
3
+ module Registries
4
+ Local = KuberKit::Core::Registries::Registry
5
+ Remote = KuberKit::Core::Registries::Registry
6
+ end
7
+
8
+ module Repositories
9
+ NoSync = KuberKit::Core::Artifacts::Local
10
+ Git = KuberKit::Core::Artifacts::Git
11
+ end
12
+
13
+ class << self
14
+ def build_configuration(configuration_name)
15
+ define_configuration(configuration_name)
16
+ end
17
+ end
18
+ end
19
+ Indocker = KuberKit
@@ -0,0 +1,10 @@
1
+ module KuberKit::Extensions::Inspectable
2
+ def inspect
3
+ data = {}
4
+ instance_variables.each do |variable|
5
+ data[variable.to_s.gsub('@', '').to_sym] = instance_variable_get(variable)
6
+ end
7
+
8
+ "#{self.class.to_s}<#{data.inspect}>"
9
+ end
10
+ end
@@ -0,0 +1,21 @@
1
+ class KuberKit::ImageCompiler::Compiler
2
+ include KuberKit::Import[
3
+ "image_compiler.image_build_dir_creator",
4
+ "image_compiler.image_builder",
5
+ "core.context_helper_factory",
6
+ "core.image_store"
7
+ ]
8
+
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
+
13
+ image_build_dir = File.join(builds_dir, image.name.to_s)
14
+
15
+ context_helper = context_helper_factory.build_image_context(shell)
16
+ image_build_dir_creator.create(shell, image, image_build_dir, context_helper: context_helper)
17
+
18
+ image_builder.build(shell, image, image_build_dir, context_helper: context_helper, args: [])
19
+ image_build_dir_creator.cleanup(shell, image_build_dir)
20
+ end
21
+ end
@@ -0,0 +1,37 @@
1
+ class KuberKit::ImageCompiler::ImageBuildDirCreator
2
+ include KuberKit::Import[
3
+ "preprocessing.dir_preprocessor",
4
+ "preprocessing.file_preprocessor",
5
+ "shell.bash_commands",
6
+ "configs"
7
+ ]
8
+
9
+ Contract KuberKit::Shell::AbstractShell, KuberKit::Core::Image, String, KeywordArgs[
10
+ context_helper: Maybe[Any]
11
+ ] => Any
12
+ def create(shell, image, build_dir, context_helper: nil)
13
+ bash_commands.rm_rf(shell, build_dir)
14
+ bash_commands.mkdir_p(shell, build_dir)
15
+
16
+ if image.build_context_dir
17
+ dir_preprocessor.compile(
18
+ shell, image.build_context_dir, build_dir,
19
+ context_helper: context_helper
20
+ )
21
+ end
22
+
23
+ target_dockerfile = File.join(build_dir, configs.image_dockerfile_name)
24
+ file_preprocessor.compile(
25
+ shell, image.dockerfile_path,
26
+ destination_path: target_dockerfile,
27
+ context_helper: context_helper
28
+ )
29
+
30
+ docker_ignore_content = configs.docker_ignore_list.join("\r\n")
31
+ shell.write(File.join(build_dir, '.dockerignore'), docker_ignore_content)
32
+ end
33
+
34
+ def cleanup(shell, build_dir)
35
+ bash_commands.rm_rf(shell, build_dir)
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ class KuberKit::ImageCompiler::ImageBuilder
2
+ include KuberKit::Import[
3
+ "shell.docker_commands",
4
+ "image_compiler.version_tag_builder"
5
+ ]
6
+
7
+ Contract KuberKit::Shell::AbstractShell, KuberKit::Core::Image, String, KeywordArgs[
8
+ args: Maybe[Any],
9
+ context_helper: Maybe[KuberKit::Core::ContextHelper]
10
+ ] => Any
11
+ def build(shell, image, build_dir, context_helper: nil, args: [])
12
+ image.before_build_callback.call(context_helper, build_dir) if image.before_build_callback
13
+
14
+ docker_commands.build(shell, build_dir, ["-t=#{image.registry_url}"])
15
+
16
+ version_tag = version_tag_builder.get_version
17
+ docker_commands.tag(shell, image.registry_url, version_tag)
18
+
19
+ if image.registry.remote?
20
+ docker_commands.tag(shell, image.registry_url, image.remote_registry_url)
21
+ docker_commands.push(shell, image.remote_registry_url)
22
+ end
23
+
24
+ image.after_build_callback.call(context_helper, build_dir) if image.after_build_callback
25
+ end
26
+ end
@@ -0,0 +1,40 @@
1
+ class KuberKit::ImageCompiler::ImageDependencyResolver
2
+ CircularDependencyError = Class.new(KuberKit::Error)
3
+ DependencyNotFoundError = Class.new(KuberKit::NotFoundError)
4
+
5
+ include KuberKit::Import[
6
+ "core.image_store"
7
+ ]
8
+
9
+ Contract Any, KeywordArgs[
10
+ resolved: Optional[ArrayOf[Symbol]]
11
+ ] => Any
12
+ def get_next(image_names, resolved: [])
13
+ deps = Array(image_names).map { |i| get_recursive_deps(i) }.flatten.uniq
14
+
15
+ ready_to_resolve = deps.select do |dep_name|
16
+ unresolved_deps = get_deps(dep_name) - resolved
17
+ unresolved_deps.empty?
18
+ end
19
+ ready_to_resolve - resolved
20
+ end
21
+
22
+ def get_recursive_deps(image_name, dependency_tree: [])
23
+ deps = get_deps(image_name)
24
+
25
+ if dependency_tree.include?(image_name)
26
+ raise CircularDependencyError, "Circular dependency found for #{image_name}. Dependency tree: #{dependency_tree.inspect}"
27
+ end
28
+
29
+ child_deps = []
30
+ deps.each do |i|
31
+ child_deps += get_recursive_deps(i, dependency_tree: dependency_tree + [image_name])
32
+ end
33
+
34
+ (deps + child_deps).uniq
35
+ end
36
+
37
+ def get_deps(image_name)
38
+ image_store.get_definition(image_name).dependencies
39
+ end
40
+ end
@@ -0,0 +1,5 @@
1
+ class KuberKit::ImageCompiler::VersionTagBuilder
2
+ def get_version
3
+ Time.now.strftime("%Y%m%d.%H%M%S")
4
+ end
5
+ end
@@ -0,0 +1,19 @@
1
+ class KuberKit::Preprocessing::DirPreprocessor
2
+ include KuberKit::Import[
3
+ "preprocessing.file_preprocessor",
4
+ "shell.bash_commands"
5
+ ]
6
+
7
+ def compile(shell, source_dir, destination_dir, context_helper: nil)
8
+ shell.recursive_list_files(source_dir).each do |source_file_path|
9
+ relative_path = source_file_path.sub(source_dir, '')
10
+ destination_file_path = File.join(destination_dir, relative_path)
11
+
12
+ file_preprocessor.compile(
13
+ shell, source_file_path,
14
+ destination_path: destination_file_path,
15
+ context_helper: context_helper
16
+ )
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,33 @@
1
+ class KuberKit::Preprocessing::FilePreprocessor
2
+ include KuberKit::Import[
3
+ "preprocessing.text_preprocessor"
4
+ ]
5
+
6
+ PreprocessingError = Class.new(KuberKit::Error)
7
+
8
+ def compile(shell, source_path, destination_path: nil, context_helper: nil)
9
+ destination_path ||= source_path
10
+
11
+ prepare_destination_dir(destination_path)
12
+
13
+ template = shell.read(source_path)
14
+ content = text_preprocessor.compile(template, context_helper: context_helper)
15
+
16
+ is_content_changed = template != content
17
+ if !is_content_changed && source_path == destination_path
18
+ return false
19
+ end
20
+
21
+ shell.write(destination_path, content)
22
+
23
+ return true
24
+ rescue Exception => e
25
+ message = "#{e.message}\r\n"
26
+ message += e.backtrace.select{|l| l.include?("(erb)") }.join("\r\n")
27
+ raise PreprocessingError, "Error while processing template #{source_path}.\r\n#{message}"
28
+ end
29
+
30
+ def prepare_destination_dir(destination_path)
31
+ FileUtils.mkdir_p(File.dirname(destination_path))
32
+ end
33
+ end
@@ -0,0 +1,7 @@
1
+ require 'erb'
2
+
3
+ class KuberKit::Preprocessing::TextPreprocessor
4
+ def compile(template, context_helper: nil)
5
+ ERB.new(template).result(context_helper&.get_binding)
6
+ end
7
+ end
@@ -0,0 +1,56 @@
1
+ class KuberKit::ServiceDeployer::ServiceListResolver
2
+ include KuberKit::Import[
3
+ "core.service_store"
4
+ ]
5
+
6
+ Contract KeywordArgs[
7
+ services: Optional[ArrayOf[String]],
8
+ tags: Optional[ArrayOf[String]]
9
+ ] => ArrayOf[String]
10
+ def resolve(services: [], tags: [])
11
+ all_definitions = service_store.all_definitions.values
12
+
13
+ included_services, excluded_services = split_by_inclusion(services)
14
+ included_tags, excluded_tags = split_by_inclusion(tags)
15
+
16
+ included_definitions = all_definitions.select do |definition|
17
+ service_name = definition.service_name.to_s
18
+ service_tags = definition.to_service_attrs.tags.map(&:to_s)
19
+
20
+ matches_any?([service_name], included_services) ||
21
+ matches_any?(service_tags, included_tags)
22
+ end
23
+
24
+ included_definitions = included_definitions.reject do |definition|
25
+ service_name = definition.service_name.to_s
26
+ service_tags = definition.to_service_attrs.tags.map(&:to_s)
27
+
28
+ matches_any?([service_name], excluded_services) ||
29
+ matches_any?(service_tags, excluded_tags)
30
+ end
31
+
32
+ included_definitions.map(&:service_name).map(&:to_s)
33
+ end
34
+
35
+ Contract Array => Array
36
+ def split_by_inclusion(array)
37
+ excluded, included = array.partition{|e| e.start_with?('-') }
38
+
39
+ excluded.map!{ |item| item.gsub(/^\-/, "") }
40
+
41
+ [included, excluded]
42
+ end
43
+
44
+ Contract String, String => Bool
45
+ def matches_name?(name, pattern)
46
+ regexp = Regexp.new("\\A" + pattern.gsub("*", "[a-z\_0-9]+") + "\\z")
47
+ name.match?(regexp)
48
+ end
49
+
50
+ Contract ArrayOf[String], ArrayOf[String] => Bool
51
+ def matches_any?(names, patterns)
52
+ patterns.any? do |pattern|
53
+ names.any? { |name| matches_name?(name, pattern) }
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,20 @@
1
+ class KuberKit::ServiceDeployer::ServiceReader
2
+ include KuberKit::Import[
3
+ "core.template_store",
4
+ "core.context_helper_factory",
5
+ "template_reader.reader",
6
+ "preprocessing.text_preprocessor"
7
+ ]
8
+
9
+ def read(shell, service)
10
+ template = template_store.get(service.template_name)
11
+
12
+ context_helper = context_helper_factory.build_service_context(shell, service)
13
+
14
+ template = reader.read(shell, template)
15
+
16
+ result = text_preprocessor.compile(template, context_helper: context_helper)
17
+
18
+ result
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ class KuberKit::Shell::AbstractShell
2
+ ShellError = Class.new(KuberKit::Error)
3
+ DirNotFoundError = Class.new(ShellError)
4
+
5
+ def exec!(command)
6
+ raise KuberKit::NotImplementedError, "must be implemented"
7
+ end
8
+
9
+ def read(file_path)
10
+ raise KuberKit::NotImplementedError, "must be implemented"
11
+ end
12
+
13
+ def write(file_path, content)
14
+ raise KuberKit::NotImplementedError, "must be implemented"
15
+ end
16
+
17
+ def recursive_list_files(path, name: nil)
18
+ raise KuberKit::NotImplementedError, "must be implemented"
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ class KuberKit::Shell::BashCommands
2
+ def rm(shell, path)
3
+ shell.exec!(%Q{rm "#{path}"})
4
+ end
5
+
6
+ def rm_rf(shell, path)
7
+ shell.exec!(%Q{rm -rf "#{path}"})
8
+ end
9
+
10
+ def cp(shell, source_path, dest_path)
11
+ shell.exec!(%Q{cp "#{source_path}" "#{dest_path}"})
12
+ end
13
+
14
+ def cp_r(shell, source_path, dest_path)
15
+ shell.exec!(%Q{cp -r "#{source_path}" "#{dest_path}"})
16
+ end
17
+
18
+ def mkdir(shell, path)
19
+ shell.exec!(%Q{mkdir "#{path}"})
20
+ end
21
+
22
+ def mkdir_p(shell, path)
23
+ shell.exec!(%Q{mkdir -p "#{path}"})
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ class KuberKit::Shell::CommandCounter
2
+ def initialize
3
+ @mutex = Mutex.new
4
+ end
5
+
6
+ def get_number
7
+ @mutex.synchronize do
8
+ @@number ||= 0
9
+ @@number += 1
10
+ @@number
11
+ end
12
+ end
13
+
14
+ def reset!
15
+ @mutex.synchronize do
16
+ @@number = 0
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+ class KuberKit::Shell::DockerCommands
2
+ def build(shell, build_dir, args = [])
3
+ default_args = ["--rm=true"]
4
+ args_list = (default_args + args).join(" ")
5
+
6
+ shell.exec!(%Q{docker build #{build_dir} #{args_list}})
7
+ end
8
+
9
+ def tag(shell, image_name, tag_name)
10
+ shell.exec!(%Q{docker tag #{image_name} #{tag_name}})
11
+ end
12
+
13
+ def push(shell, tag_name)
14
+ shell.exec!(%Q{docker push #{tag_name}})
15
+ end
16
+ end
@@ -0,0 +1,28 @@
1
+ class KuberKit::Shell::GitCommands
2
+ def get_remote_url(shell, git_repo_path, remote_name: "origin")
3
+ shell.exec! [
4
+ "cd #{git_repo_path}",
5
+ "git config --get remote.#{remote_name}.url",
6
+ ].join(" && ")
7
+ rescue KuberKit::Shell::AbstractShell::ShellError
8
+ return nil
9
+ end
10
+
11
+ def download_repo(shell, remote_url:, path:, branch:)
12
+ shell.exec! [
13
+ "rm -rf #{path}",
14
+ "mkdir -p #{path}",
15
+ "git clone -b #{branch} --depth 1 #{remote_url} #{path}",
16
+ ].join(" && ")
17
+ end
18
+
19
+ def force_pull_repo(shell, path:, branch:)
20
+ shell.exec! [
21
+ "cd #{path}",
22
+ "git add .",
23
+ "git reset HEAD --hard",
24
+ "git checkout #{branch}",
25
+ "git pull --force",
26
+ ].join(" && ")
27
+ end
28
+ end
@@ -0,0 +1,12 @@
1
+ class KuberKit::Shell::KubectlCommands
2
+ def apply_file(shell, file_path, kubecfg_path: nil)
3
+ command_parts = []
4
+ if kubecfg_path
5
+ command_parts << "KUBECFG=#{kubecfg_path}"
6
+ end
7
+
8
+ command_parts << "kubectl apply -f #{file_path}"
9
+
10
+ shell.exec!(command_parts.join(" "))
11
+ end
12
+ end