devkitkat 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.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "devkitkat"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require "pry"
11
+ Pry.start
12
+
13
+ # require "irb"
14
+ # IRB.start(__FILE__)
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require "bundler/setup"
3
+ require "devkitkat"
4
+ require 'pry'
5
+
6
+ Devkitkat::Main.new.execute
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+
3
+ bundle exec rspec -t ~slow
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,37 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "devkitkat/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "devkitkat"
8
+ spec.version = Devkitkat::VERSION
9
+ spec.authors = ["Shinya Maeda"]
10
+ spec.email = ["shinya@gitlab.com"]
11
+
12
+ spec.summary = "Make micro services easy"
13
+ spec.description = "Make micro services easy"
14
+ spec.homepage = "https://gitlab.com/dosuken123/devkitkat"
15
+ spec.license = "MIT"
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ end
22
+ spec.bindir = "bin"
23
+ spec.executables = "devkitkat"
24
+ spec.require_paths = ["lib"]
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.17"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "rspec", "~> 3.0"
29
+ spec.add_development_dependency "rspec-temp_dir", "~> 1.1.0"
30
+ spec.add_development_dependency "pry", "~> 0.12.2"
31
+ spec.add_development_dependency "pry-nav"
32
+ spec.add_runtime_dependency "activesupport", "~> 6.0.0"
33
+ spec.add_runtime_dependency "parallel", "~> 1.17.0"
34
+ spec.add_runtime_dependency "ruby-progressbar", "~> 1.10.1"
35
+ spec.add_runtime_dependency "colorize", "~> 0.8.1"
36
+ spec.add_runtime_dependency "docker-api", "~> 1.34.2"
37
+ end
@@ -0,0 +1 @@
1
+ # Devkitkat
@@ -0,0 +1 @@
1
+ # Config
@@ -0,0 +1 @@
1
+ # Machine
@@ -0,0 +1 @@
1
+ # Service
@@ -0,0 +1,14 @@
1
+ require "devkitkat/version"
2
+ require "devkitkat/main"
3
+ require "devkitkat/executor"
4
+ require "devkitkat/command"
5
+ require "devkitkat/config"
6
+ require "devkitkat/service"
7
+ require 'yaml'
8
+ require 'optparse'
9
+ require 'parallel'
10
+ require 'colorize'
11
+ require 'active_support/core_ext/array/conversions'
12
+
13
+ module Devkitkat
14
+ end
@@ -0,0 +1,65 @@
1
+ module Devkitkat
2
+ class Command
3
+ attr_reader :options, :script, :target, :args
4
+
5
+ def initialize
6
+ @options = {}
7
+
8
+ OptionParser.new do |opts|
9
+ opts.banner = "Usage: devkitkat <script> <target> [options]"
10
+
11
+ opts.on("-p", "--path PATH", "The root path of the .devkitkat.yml") do |v|
12
+ options[:root_path] = v
13
+ end
14
+
15
+ opts.on("-e", "--exclude SERVICE", "Exclude serviced from the specified target") do |v|
16
+ options[:exclude] ||= []
17
+ options[:exclude] << v
18
+ end
19
+
20
+ opts.on("-e", "--env-var VARIABLE", "additional environment variables") do |v|
21
+ options[:variables] ||= {}
22
+ options[:variables].merge!(Hash[*v.split('=')])
23
+ end
24
+
25
+ opts.on("-d", "--depth DEPTH", "Git depth for pull/fetch") do |v|
26
+ options[:git_depth] = v
27
+ end
28
+
29
+ opts.on("-r", "--remote REMOTE", "Git remote") do |v|
30
+ options[:git_remote] = v
31
+ end
32
+
33
+ opts.on("-b", "--branch BRANCH", "Git branch") do |v|
34
+ options[:git_branch] = v
35
+ end
36
+
37
+ opts.on("-t", "--tty", "TTY mode. In this mode, log won't be emitted.") do |v|
38
+ options[:tty] = v
39
+ end
40
+ end.parse!
41
+
42
+ @script, @target, *@args = ARGV
43
+ end
44
+
45
+ def tty?
46
+ options[:tty]
47
+ end
48
+
49
+ def variables
50
+ options[:variables]
51
+ end
52
+
53
+ def tmp_dir
54
+ File.join(kit_root, 'tmp')
55
+ end
56
+
57
+ def create_tmp_dir
58
+ FileUtils.mkdir_p(tmp_dir)
59
+ end
60
+
61
+ def kit_root
62
+ Dir.pwd # TODO: root_path
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,87 @@
1
+ module Devkitkat
2
+ class Config
3
+ DEVKITKAT_FILE_NAME = '.devkitkat.yml'
4
+ HIDDEN_SERVICES = %w[system]
5
+
6
+ attr_reader :devkitkat_yml, :kit_root
7
+
8
+ def initialize(kit_root)
9
+ @kit_root = kit_root
10
+ @devkitkat_yml = load_config
11
+ end
12
+
13
+ def all_services
14
+ services + HIDDEN_SERVICES
15
+ end
16
+
17
+ def resolve!(target, exclude: nil)
18
+ services = if target.nil? || target == 'system'
19
+ %w[system]
20
+ elsif target == 'all'
21
+ all_services
22
+ elsif group = find_group(target)
23
+ services_for_group(group)
24
+ elsif service = find_service(target)
25
+ [service]
26
+ else
27
+ raise ArgumentError, "The target name #{target} couldn't be resolved"
28
+ end
29
+
30
+ services = services - exclude if exclude
31
+
32
+ services
33
+ end
34
+
35
+ def environment_type
36
+ devkitkat_yml.dig('environment', 'type') || 'local'
37
+ end
38
+
39
+ def environment_image
40
+ devkitkat_yml.dig('environment', 'image')
41
+ end
42
+
43
+ def application
44
+ devkitkat_yml.fetch('application', '')
45
+ end
46
+
47
+ def variables
48
+ devkitkat_yml.fetch('variables', {})
49
+ end
50
+
51
+ def service_hash(name)
52
+ devkitkat_yml.dig('services', name) || {}
53
+ end
54
+
55
+ private
56
+
57
+ def services
58
+ devkitkat_yml['services']&.keys || []
59
+ end
60
+
61
+ def groups
62
+ devkitkat_yml['groups']&.keys || []
63
+ end
64
+
65
+ def services_for_group(group)
66
+ devkitkat_yml.dig('groups', group) || []
67
+ end
68
+
69
+ def find_group(target)
70
+ groups.find { |group| group == target }
71
+ end
72
+
73
+ def find_service(target)
74
+ services.find { |service| service == target }
75
+ end
76
+
77
+ def load_config
78
+ File.read(config_path).yield_self do |content|
79
+ YAML.load(content)
80
+ end
81
+ end
82
+
83
+ def config_path
84
+ File.join(kit_root, DEVKITKAT_FILE_NAME)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,63 @@
1
+ require "devkitkat/executor/docker"
2
+ require "devkitkat/executor/local"
3
+
4
+ module Devkitkat
5
+ class Executor
6
+ attr_reader :service, :scripts
7
+
8
+ delegate :config, :command, to: :service
9
+ delegate :prepare, :cleanup, to: :executor
10
+
11
+ SCRIPT_HEADER = <<-EOS
12
+ #!/bin/bash
13
+ set -e
14
+ EOS
15
+
16
+ def initialize(service)
17
+ @service = service
18
+ delete_script_file
19
+ end
20
+
21
+ def write(cmd)
22
+ ensure_script_file
23
+
24
+ File.open(script_file, 'a') do |stream|
25
+ stream.write(cmd + "\n")
26
+ end
27
+ end
28
+
29
+ def commit
30
+ executor.commit(script_file)
31
+ ensure
32
+ delete_script_file
33
+ end
34
+
35
+ private
36
+
37
+ def executor
38
+ @executor ||= klass.new(service)
39
+ end
40
+
41
+ def klass
42
+ Object.const_get("Devkitkat::Executor::#{config.environment_type.capitalize}")
43
+ end
44
+
45
+ def script_file
46
+ File.join(command.tmp_dir, "script-#{service.name}-#{command.script}")
47
+ end
48
+
49
+ def ensure_script_file
50
+ create_script_file unless File.exist?(script_file)
51
+ end
52
+
53
+ def create_script_file
54
+ command.create_tmp_dir
55
+ File.write(script_file, SCRIPT_HEADER)
56
+ File.chmod(0777, script_file)
57
+ end
58
+
59
+ def delete_script_file
60
+ FileUtils.rm_f(script_file)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,97 @@
1
+ require 'active_support/core_ext/module/delegation'
2
+ require 'docker'
3
+
4
+ module Devkitkat
5
+ class Executor
6
+ class Docker
7
+ attr_reader :service, :script_file
8
+
9
+ delegate :config, :command, to: :service
10
+
11
+ def initialize(service)
12
+ @service = service
13
+ end
14
+
15
+ def prepare
16
+ start_container
17
+ end
18
+
19
+ def cleanup
20
+ stop_container
21
+ end
22
+
23
+ def commit(script_file)
24
+ @script_file = script_file
25
+
26
+ rewrite_root_path!
27
+ new_path = script_path_in_container
28
+
29
+ container.exec([new_path])
30
+ end
31
+
32
+ private
33
+
34
+ def rewrite_root_path!
35
+ content = File.read(script_file)
36
+ new_content = content.gsub(command.kit_root, root_in_container)
37
+ File.write(script_file, new_content)
38
+ end
39
+
40
+ def script_path_in_container
41
+ relative_path = script_file.delete_prefix(command.kit_root)
42
+ File.join(root_in_container, relative_path)
43
+ end
44
+
45
+ def log_path_in_container
46
+ relative_path = service.log_path.delete_prefix(command.kit_root)
47
+ File.join(root_in_container, relative_path)
48
+ end
49
+
50
+ def docker_image
51
+ config.environment_image
52
+ end
53
+
54
+ def container
55
+ @container ||= ::Docker::Container.create(container_parameter)
56
+ end
57
+
58
+ def container_parameter
59
+ # TODO: Speicfy users otherwise the created files are owned by root
60
+ params = {
61
+ 'Cmd' => %w[tail -f],
62
+ 'Image' => docker_image,
63
+ 'name' => service.container_name,
64
+ 'HostConfig' => {
65
+ 'Binds' => ["#{command.kit_root}:#{root_in_container}"]
66
+ }
67
+ }
68
+
69
+ if service.port
70
+ params.deep_merge!(
71
+ 'ExposedPorts' => { "#{service.port}/tcp" => {} },
72
+ 'HostConfig' => {
73
+ 'PortBindings' => {
74
+ "#{service.port}/tcp" => [{ 'HostPort' => service.port.to_s }]
75
+ }
76
+ }
77
+ )
78
+ end
79
+
80
+ params
81
+ end
82
+
83
+ def root_in_container
84
+ "/devkitkat"
85
+ end
86
+
87
+ def start_container
88
+ container.start
89
+ end
90
+
91
+ def stop_container
92
+ container.stop
93
+ container.remove
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,25 @@
1
+ module Devkitkat
2
+ class Executor
3
+ class Local
4
+ attr_reader :service
5
+
6
+ delegate :config, :command, to: :service
7
+
8
+ def initialize(service)
9
+ @service = service
10
+ end
11
+
12
+ def prepare
13
+ # no-op
14
+ end
15
+
16
+ def cleanup
17
+ # no-op
18
+ end
19
+
20
+ def commit(script_file)
21
+ system(script_file)
22
+ end
23
+ end
24
+ end
25
+ end