kuber_kit 0.1.0

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