devkitkat 0.1.0

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