devkitkat 0.1.21 → 0.1.22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +13 -11
  3. data/README.md +2 -2
  4. data/bin/devkitkat +1 -1
  5. data/devkitkat.gemspec +2 -1
  6. data/lib/devkitkat/command.rb +6 -8
  7. data/lib/devkitkat/config.rb +33 -20
  8. data/lib/devkitkat/processor.rb +48 -0
  9. data/lib/devkitkat/service/concerns/service_initializer.rb +17 -0
  10. data/lib/devkitkat/service/driver/base.rb +23 -0
  11. data/lib/devkitkat/service/driver/docker/container.rb +163 -0
  12. data/lib/devkitkat/service/driver/docker/image.rb +33 -0
  13. data/lib/devkitkat/service/driver/docker.rb +45 -0
  14. data/lib/devkitkat/service/driver/none.rb +19 -0
  15. data/lib/devkitkat/service/executor/logger.rb +22 -0
  16. data/lib/devkitkat/service/executor/scripter.rb +48 -0
  17. data/lib/devkitkat/service/executor/variables.rb +82 -0
  18. data/lib/devkitkat/service/executor.rb +102 -0
  19. data/lib/devkitkat/service/predefined_command/add_example.rb +33 -0
  20. data/lib/devkitkat/service/predefined_command/add_git_ignore.rb +29 -0
  21. data/lib/devkitkat/service/predefined_command/add_script.rb +35 -0
  22. data/lib/devkitkat/service/predefined_command/add_shared_script.rb +21 -0
  23. data/lib/devkitkat/service/predefined_command/base.rb +17 -0
  24. data/lib/devkitkat/service/predefined_command/clean.rb +22 -0
  25. data/lib/devkitkat/service/predefined_command/clone.rb +25 -0
  26. data/lib/devkitkat/service/predefined_command/exec.rb +18 -0
  27. data/lib/devkitkat/service/predefined_command/poop.rb +17 -0
  28. data/lib/devkitkat/service/predefined_command/pull.rb +19 -0
  29. data/lib/devkitkat/service/predefined_command/reconfigure.rb +26 -0
  30. data/lib/devkitkat/service/predefined_command/show_variables.rb +17 -0
  31. data/lib/devkitkat/service.rb +13 -214
  32. data/lib/devkitkat/version.rb +1 -1
  33. data/lib/devkitkat.rb +12 -5
  34. metadata +41 -12
  35. data/docs/READ.md +0 -1
  36. data/docs/config/READ.md +0 -1
  37. data/docs/machine/READ.md +0 -1
  38. data/docs/service/READ.md +0 -1
  39. data/lib/devkitkat/executor/docker.rb +0 -154
  40. data/lib/devkitkat/executor/local.rb +0 -25
  41. data/lib/devkitkat/executor.rb +0 -62
  42. data/lib/devkitkat/main.rb +0 -50
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 45003b239d330518c51308aec36f8f04c18dc36f3599f2c33482ee354bb76f16
4
- data.tar.gz: b09492e98b456d7afd6ba31720509c1c5ae22b7516532443661e2a7efa5132eb
3
+ metadata.gz: 19d4a68e881590b167d09fb7564e53f0e6b337386ad4de76043d4ed5c1d8d3ab
4
+ data.tar.gz: 5d1150ee9dcdd3820b60d8f460da8d69ed9eef314f13626da409d8481e78d7e3
5
5
  SHA512:
6
- metadata.gz: f0ca0b9427f4176b02c8e15d8df4a2a12a2b4e1f265cfb6cae875ac4d422e33407ea2eb503285c47015f5df76245f352a59f7bc5cfa35fea3a886b86595aec8f
7
- data.tar.gz: 2ffb303ad8506c435fda82b563e26c03877e4eaa20a4ebf4c84a43314041daf969b6406b4a132886a48df6f254ef8c217c4e897e418c6af7af7bacdca7762c8b
6
+ metadata.gz: eee0fd0f5ab8ebea39250f607ec29871f2f860305d601a1c161caf87c5b97066a6f26cecc019bd88797e9a6210b7a678b1eac793422d308b40fdfba2ce5a669b
7
+ data.tar.gz: 5c133cd5b4d58a45711ee7192b39980a0519e5d786377674d2399ff0f43b57f8dc4cb72044ba2c0b3bfb535d6e38b47f72ca4e19158a3c04845e885aa4918eac
data/Gemfile.lock CHANGED
@@ -1,36 +1,37 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- devkitkat (0.1.21)
4
+ devkitkat (0.1.22)
5
5
  activesupport (~> 6.0.0)
6
6
  colorize (~> 0.8.1)
7
7
  docker-api (~> 1.34.2)
8
8
  parallel (~> 1.17.0)
9
9
  pry (~> 0.12.2)
10
10
  pry-nav
11
+ require_all (~> 3.0.0)
11
12
  ruby-progressbar (~> 1.10.1)
12
13
 
13
14
  GEM
14
15
  remote: https://rubygems.org/
15
16
  specs:
16
- activesupport (6.0.0)
17
+ activesupport (6.0.2.2)
17
18
  concurrent-ruby (~> 1.0, >= 1.0.2)
18
19
  i18n (>= 0.7, < 2)
19
20
  minitest (~> 5.1)
20
21
  tzinfo (~> 1.1)
21
- zeitwerk (~> 2.1, >= 2.1.8)
22
+ zeitwerk (~> 2.2)
22
23
  coderay (1.1.2)
23
24
  colorize (0.8.1)
24
- concurrent-ruby (1.1.5)
25
+ concurrent-ruby (1.1.6)
25
26
  diff-lcs (1.3)
26
27
  docker-api (1.34.2)
27
28
  excon (>= 0.47.0)
28
29
  multi_json
29
- excon (0.68.0)
30
- i18n (1.7.0)
30
+ excon (0.73.0)
31
+ i18n (1.8.2)
31
32
  concurrent-ruby (~> 1.0)
32
33
  method_source (0.9.2)
33
- minitest (5.12.2)
34
+ minitest (5.14.0)
34
35
  multi_json (1.14.1)
35
36
  parallel (1.17.0)
36
37
  pry (0.12.2)
@@ -38,7 +39,8 @@ GEM
38
39
  method_source (~> 0.9.0)
39
40
  pry-nav (0.3.0)
40
41
  pry (>= 0.9.10, < 0.13.0)
41
- rake (10.5.0)
42
+ rake (12.3.3)
43
+ require_all (3.0.0)
42
44
  rspec (3.8.0)
43
45
  rspec-core (~> 3.8.0)
44
46
  rspec-expectations (~> 3.8.0)
@@ -56,9 +58,9 @@ GEM
56
58
  rspec (>= 3.0)
57
59
  ruby-progressbar (1.10.1)
58
60
  thread_safe (0.3.6)
59
- tzinfo (1.2.5)
61
+ tzinfo (1.2.7)
60
62
  thread_safe (~> 0.1)
61
- zeitwerk (2.2.0)
63
+ zeitwerk (2.3.0)
62
64
 
63
65
  PLATFORMS
64
66
  ruby
@@ -66,7 +68,7 @@ PLATFORMS
66
68
  DEPENDENCIES
67
69
  bundler (~> 1.17)
68
70
  devkitkat!
69
- rake (~> 10.0)
71
+ rake (~> 12.3.3)
70
72
  rspec (~> 3.0)
71
73
  rspec-temp_dir (~> 1.1.0)
72
74
 
data/README.md CHANGED
@@ -264,11 +264,11 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
264
264
 
265
265
  **Execute only fast tests**
266
266
 
267
- `bundle exec rspec /home/shinya/workspace/devkitkat/spec/devkitkat_spec.rb -t ~slow`
267
+ `bundle exec rspec -t ~slow`
268
268
 
269
269
  **Execute only slow tests**
270
270
 
271
- `bundle exec rspec /home/shinya/workspace/devkitkat/spec/devkitkat_spec.rb -t slow`
271
+ `bundle exec rspec -t slow`
272
272
 
273
273
  ## Contributing
274
274
 
data/bin/devkitkat CHANGED
@@ -3,4 +3,4 @@ require 'bundler/setup' if ENV['DEVKITKAT_DEBUG']
3
3
  require "devkitkat"
4
4
  require 'pry'
5
5
 
6
- Devkitkat::Main.new.execute
6
+ Devkitkat::Main.execute
data/devkitkat.gemspec CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.require_paths = ["lib"]
25
25
 
26
26
  spec.add_development_dependency "bundler", "~> 1.17"
27
- spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "rake", "~> 12.3.3"
28
28
  spec.add_development_dependency "rspec", "~> 3.0"
29
29
  spec.add_development_dependency "rspec-temp_dir", "~> 1.1.0"
30
30
  spec.add_runtime_dependency "pry", "~> 0.12.2"
@@ -34,4 +34,5 @@ Gem::Specification.new do |spec|
34
34
  spec.add_runtime_dependency "ruby-progressbar", "~> 1.10.1"
35
35
  spec.add_runtime_dependency "colorize", "~> 0.8.1"
36
36
  spec.add_runtime_dependency "docker-api", "~> 1.34.2"
37
+ spec.add_runtime_dependency "require_all", "~> 3.0.0"
37
38
  end
@@ -1,3 +1,5 @@
1
+ require 'optparse'
2
+
1
3
  module Devkitkat
2
4
  class Command
3
5
  attr_reader :options, :script, :target, :args
@@ -28,18 +30,14 @@ module Devkitkat
28
30
  options[:quiet]
29
31
  end
30
32
 
31
- def tmp_dir
32
- File.join(kit_root, '.devkitkat')
33
- end
34
-
35
- def create_tmp_dir
36
- FileUtils.mkdir_p(tmp_dir)
37
- end
38
-
39
33
  def kit_root
40
34
  options[:root_path] || Dir.pwd
41
35
  end
42
36
 
37
+ def name
38
+ script.tr('-', '_')
39
+ end
40
+
43
41
  private
44
42
 
45
43
  def option_parser
@@ -1,9 +1,10 @@
1
+ require 'yaml'
2
+
1
3
  module Devkitkat
2
4
  class Config
3
5
  DEVKITKAT_FILE_NAME = '.devkitkat.yml'
4
6
  HIDDEN_SERVICES = %w[system]
5
7
  DEFAULT_APPLICATION_NAME = 'devkitkat'
6
- DEFAULT_IMAGE = 'registry.gitlab.com/dosuken123/thin-gdk/thin-gdk-monolith:master'
7
8
 
8
9
  attr_reader :devkitkat_yml, :kit_root
9
10
 
@@ -18,34 +19,46 @@ module Devkitkat
18
19
 
19
20
  def resolve!(target, exclude: nil)
20
21
  services = if target.nil? || target == 'system'
21
- %w[system]
22
- elsif target == 'all'
23
- all_services
24
- elsif group = find_group(target)
25
- services_for_group(group)
26
- elsif services = find_comma_separated_services(target)
22
+ %w[system]
23
+ elsif target == 'all'
24
+ all_services
25
+ elsif group = find_group(target)
26
+ services_for_group(group)
27
+ elsif services = find_comma_separated_services(target)
27
28
  services
28
- elsif service = find_service(target)
29
- [service]
30
- else
31
- raise_error(target)
32
- end
29
+ elsif service = find_service(target)
30
+ [service]
31
+ else
32
+ raise_error(target)
33
+ end
33
34
 
34
35
  services = services - exclude if exclude
35
36
 
36
37
  services
37
38
  end
38
39
 
39
- def environment_type
40
- if devkitkat_yml.key?('image')
41
- 'docker'
42
- else
43
- 'local'
44
- end
40
+ def machine_driver
41
+ devkitkat_yml.dig('machine', 'driver') || 'none'
42
+ end
43
+
44
+ def machine_location
45
+ devkitkat_yml.dig('machine', 'location') || 'local'
46
+ end
47
+
48
+ def machine_image
49
+ devkitkat_yml.dig('machine', 'image')
50
+ end
51
+
52
+ def machine_extra_hosts
53
+ devkitkat_yml.dig('machine', 'extra_hosts')
54
+ end
55
+
56
+ def machine_network_mode
57
+ devkitkat_yml.dig('machine', 'network_mode')
45
58
  end
46
59
 
47
- def image
48
- devkitkat_yml.fetch('image', DEFAULT_IMAGE)
60
+ def extra_write_accesses
61
+ devkitkat_yml.dig('machine', 'extra_write_accesses')
49
62
  end
50
63
 
51
64
  def application
@@ -0,0 +1,48 @@
1
+ require 'parallel'
2
+
3
+ module Devkitkat
4
+ class Processor
5
+ attr_reader :services, :config, :command
6
+
7
+ def initialize(services, command, config)
8
+ @services = services
9
+ @command = command
10
+ @config = config
11
+ end
12
+
13
+ def execute
14
+ results = []
15
+
16
+ print_log_paths
17
+
18
+ if services.count == 1
19
+ # If the target is only one, it could be console access (TTY)
20
+ # so we can't run in parallel.
21
+ results << services.first.execute
22
+ else
23
+ results = Parallel.map(services, progress: 'Executing', in_processes: 8) do |service|
24
+ service.execute.tap do |success|
25
+ raise Parallel::Kill unless success
26
+ end
27
+ end
28
+ end
29
+
30
+ results&.all? { |result| result == true } || terminate_process_group!
31
+ end
32
+
33
+ private
34
+
35
+ def terminate_process_group!
36
+ pgid = Process.getpgid(Process.pid)
37
+
38
+ Process.kill('TERM', -pgid)
39
+ end
40
+
41
+ def print_log_paths
42
+ return if command.interactive? || command.quiet?
43
+
44
+ log_paths = services.map(&:log_path)
45
+ puts %Q{See the log at \n#{log_paths.join("\n")}}
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,17 @@
1
+ require 'active_support/core_ext/module/delegation'
2
+
3
+ module Devkitkat
4
+ class Service
5
+ module Concerns
6
+ module ServiceInitializer
7
+ attr_reader :service
8
+
9
+ delegate :config, :command, to: :service
10
+
11
+ def initialize(service)
12
+ @service = service
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ module Devkitkat
2
+ class Service
3
+ class Driver
4
+ class Base
5
+ include Concerns::ServiceInitializer
6
+
7
+ PreparationError = Class.new(StandardError)
8
+
9
+ def prepare
10
+ raise NotImplementedError
11
+ end
12
+
13
+ def cleanup
14
+ raise NotImplementedError
15
+ end
16
+
17
+ def execute(script_file)
18
+ raise NotImplementedError
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,163 @@
1
+ module Devkitkat
2
+ class Service
3
+ class Driver
4
+ class Docker < Base
5
+ class Container
6
+ include Concerns::ServiceInitializer
7
+
8
+ ROOT_IN_CONTAINER = '/devkitkat'
9
+
10
+ attr_reader :container
11
+
12
+ def start
13
+ if @container = find
14
+ container.start
15
+ else
16
+ @container = create
17
+ container.start
18
+ create_host_user
19
+ end
20
+ end
21
+
22
+ def stop
23
+ raise 'Container has not started yet' unless container
24
+
25
+ container.stop
26
+ end
27
+
28
+ def exec(cmds, params = {})
29
+ params.merge!(user: user_name)
30
+ safe_exec(cmds, params)
31
+ end
32
+
33
+ def exec_as_host(cmds, params = {})
34
+ safe_exec(cmds, params)
35
+ end
36
+
37
+ private
38
+
39
+ def safe_exec(cmds, params)
40
+ params.merge!(wait: 604800) # Default timeout is 1 minute, so setting 1 week instead
41
+ stdout_messages, stderr_messages, exit_code =
42
+ container.exec(cmds, params)
43
+
44
+ if exit_code != 0 || command.debug?
45
+ puts "#{self.class.name} - #{__callee__}: stdout_messages: #{stdout_messages} stderr_messages: #{stderr_messages} exit_code: #{exit_code}"
46
+ end
47
+
48
+ exit_code == 0 ? true : false
49
+ rescue ::Docker::Error::ConflictError => e
50
+ puts "#{self.class.name} - #{__callee__}: #{e.message}"
51
+ false
52
+ end
53
+
54
+ def image
55
+ config.machine_image
56
+ end
57
+
58
+ def name
59
+ @name ||=
60
+ "#{config.application}-#{service.name}-#{Digest::SHA1.hexdigest(command.kit_root)[8..12]}"
61
+ end
62
+
63
+ def find
64
+ ::Docker::Container.get(name)
65
+ rescue ::Docker::Error::NotFoundError
66
+ nil
67
+ end
68
+
69
+ def create
70
+ ::Docker::Container.create(create_parameter)
71
+ end
72
+
73
+ def create_parameter
74
+ params = {
75
+ 'Cmd' => %w[tail -f],
76
+ 'Image' => image,
77
+ 'name' => name,
78
+ 'HostConfig' => {
79
+ 'Binds' => all_mounts
80
+ }
81
+ }
82
+
83
+ if service.port
84
+ params.deep_merge!(
85
+ 'ExposedPorts' => { "#{service.port}/tcp" => {} },
86
+ 'HostConfig' => {
87
+ 'PortBindings' => {
88
+ "#{service.port}/tcp" => [{ 'HostPort' => service.port.to_s }]
89
+ }
90
+ }
91
+ )
92
+ end
93
+
94
+ if config.machine_extra_hosts
95
+ params.deep_merge!('HostConfig' => { 'ExtraHosts' => config.machine_extra_hosts })
96
+ end
97
+
98
+ if config.machine_network_mode
99
+ params.deep_merge!('HostConfig' => { 'NetworkMode' => config.machine_network_mode })
100
+ end
101
+
102
+ params
103
+ end
104
+
105
+ def all_mounts
106
+ config.all_services.map do |service_name|
107
+ service = Service.new(service_name, config, command)
108
+
109
+ FileUtils.mkdir_p(service.dir)
110
+
111
+ if self.service.name == service_name || service.system? || allowed_by_extra_write_accesses?(service)
112
+ "#{service.dir}:#{ROOT_IN_CONTAINER}/services/#{service.name}"
113
+ else
114
+ "#{service.dir}:#{ROOT_IN_CONTAINER}/services/#{service.name}:ro"
115
+ end
116
+ end
117
+ end
118
+
119
+ def allowed_by_extra_write_accesses?(service)
120
+ config.extra_write_accesses&.any? do |access|
121
+ from, _, to = access.split(':')
122
+ self.service.name == from && service.name == to
123
+ end
124
+ end
125
+
126
+ def user_name
127
+ 'devkitkat'
128
+ end
129
+
130
+ def group_id
131
+ @group_id ||= `id -u`.delete("\n")
132
+ end
133
+
134
+ def user_id
135
+ @user_id ||= `id -g`
136
+ end
137
+
138
+ def create_host_user
139
+ prepare!(['addgroup', '--gid', group_id, user_name])
140
+
141
+ prepare!(['adduser',
142
+ '--uid', user_id,
143
+ '--gid', group_id,
144
+ '--shell', '/bin/bash',
145
+ '--home', ROOT_IN_CONTAINER,
146
+ '--gecos', '',
147
+ '--disabled-password',
148
+ user_name])
149
+
150
+ prepare!(['chown', "#{user_name}:#{user_name}", ROOT_IN_CONTAINER])
151
+ prepare!(['chown', '-R', "#{user_name}:#{user_name}", "#{ROOT_IN_CONTAINER}/services/#{service.name}"])
152
+ end
153
+
154
+ def prepare!(cmds, params = {})
155
+ unless exec_as_host(cmds, params)
156
+ raise Driver::Base::PreparationError, "Failed to execute command in container. cmds: #{cmds}"
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,33 @@
1
+ module Devkitkat
2
+ class Service
3
+ class Driver
4
+ class Docker < Base
5
+ class Image
6
+ include Concerns::ServiceInitializer
7
+
8
+ def pull
9
+ image_exist? || pull_image
10
+ end
11
+
12
+ private
13
+
14
+ def pull_image
15
+ puts "Pulling image #{image}..."
16
+ ::Docker::Image.create('fromImage' => image)
17
+ puts "Pulled image #{image}..."
18
+ end
19
+
20
+ def image_exist?
21
+ ::Docker::Image.get(image)
22
+ rescue
23
+ false
24
+ end
25
+
26
+ def image
27
+ config.machine_image
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,45 @@
1
+ require 'docker'
2
+ require_relative 'docker/container'
3
+ require_relative 'docker/image'
4
+
5
+ module Devkitkat
6
+ class Service
7
+ class Driver
8
+ class Docker < Base
9
+ def prepare
10
+ image.pull
11
+ container.start
12
+ end
13
+
14
+ def execute(script_file)
15
+ new_path = rewrite_root_path!(script_file)
16
+
17
+ container.exec([new_path])
18
+ end
19
+
20
+ def cleanup
21
+ container.stop
22
+ end
23
+
24
+ private
25
+
26
+ def rewrite_root_path!(script_file)
27
+ content = File.read(script_file)
28
+ new_content = content.gsub(command.kit_root, Container::ROOT_IN_CONTAINER)
29
+ File.write(script_file, new_content)
30
+
31
+ relative_path = script_file.delete_prefix(command.kit_root)
32
+ File.join(Container::ROOT_IN_CONTAINER, relative_path)
33
+ end
34
+
35
+ def container
36
+ @container ||= Container.new(service)
37
+ end
38
+
39
+ def image
40
+ @image ||= Image.new(service)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,19 @@
1
+ module Devkitkat
2
+ class Service
3
+ class Driver
4
+ class None < Base
5
+ def prepare
6
+ # no-op
7
+ end
8
+
9
+ def cleanup
10
+ # no-op
11
+ end
12
+
13
+ def execute(script_file)
14
+ system(script_file)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ module Devkitkat
2
+ class Service
3
+ class Executor
4
+ class Logger
5
+ include Concerns::ServiceInitializer
6
+
7
+ def to_script
8
+ "exec > #{service.log_path} 2>&1"
9
+ end
10
+
11
+ def available?
12
+ !command.interactive?
13
+ end
14
+
15
+ def new_file
16
+ FileUtils.rm_f(service.log_path)
17
+ FileUtils.mkdir_p(service.log_dir)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,48 @@
1
+ module Devkitkat
2
+ class Service
3
+ class Executor
4
+ class Scripter
5
+ include Concerns::ServiceInitializer
6
+
7
+ SCRIPT_HEADER = <<~EOS
8
+ #!/bin/bash
9
+ EOS
10
+
11
+ def file_path
12
+ File.join(service.dir, "script-#{service.name}-#{command.script}")
13
+ end
14
+
15
+ def new_file
16
+ delete_file
17
+ create_file
18
+
19
+ yield
20
+ ensure
21
+ delete_file
22
+ end
23
+
24
+ def write(cmd)
25
+ File.open(file_path, 'a') do |stream|
26
+ stream.write(cmd + "\n")
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def create_file
33
+ ensure_service_root_dir
34
+ File.write(file_path, SCRIPT_HEADER)
35
+ File.chmod(0777, file_path)
36
+ end
37
+
38
+ def delete_file
39
+ FileUtils.rm_f(file_path)
40
+ end
41
+
42
+ def ensure_service_root_dir
43
+ FileUtils.mkdir_p(service.root_dir)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end