kamaze-docker_image 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +6 -0
  3. data/.yardopts +16 -0
  4. data/lib/kamaze-docker_image.rb +24 -0
  5. data/lib/kamaze/docker_image.rb +223 -0
  6. data/lib/kamaze/docker_image/command.rb +79 -0
  7. data/lib/kamaze/docker_image/concern.rb +22 -0
  8. data/lib/kamaze/docker_image/concern/containers.rb +75 -0
  9. data/lib/kamaze/docker_image/concern/docker.rb +36 -0
  10. data/lib/kamaze/docker_image/concern/executable.rb +24 -0
  11. data/lib/kamaze/docker_image/concern/readable_attrs.rb +48 -0
  12. data/lib/kamaze/docker_image/concern/setup.rb +98 -0
  13. data/lib/kamaze/docker_image/concern/setup/config.rb +35 -0
  14. data/lib/kamaze/docker_image/loader.rb +61 -0
  15. data/lib/kamaze/docker_image/loader/context.rb +57 -0
  16. data/lib/kamaze/docker_image/loader/helper.rb +99 -0
  17. data/lib/kamaze/docker_image/loader/tasks.rb +22 -0
  18. data/lib/kamaze/docker_image/loader/tasks/build.rb +23 -0
  19. data/lib/kamaze/docker_image/loader/tasks/exec.rb +19 -0
  20. data/lib/kamaze/docker_image/loader/tasks/push.rb +19 -0
  21. data/lib/kamaze/docker_image/loader/tasks/restart.rb +21 -0
  22. data/lib/kamaze/docker_image/loader/tasks/run.rb +19 -0
  23. data/lib/kamaze/docker_image/loader/tasks/ssh.rb +19 -0
  24. data/lib/kamaze/docker_image/loader/tasks/start.rb +19 -0
  25. data/lib/kamaze/docker_image/loader/tasks/stop.rb +19 -0
  26. data/lib/kamaze/docker_image/runner.rb +126 -0
  27. data/lib/kamaze/docker_image/runner/storage.rb +78 -0
  28. data/lib/kamaze/docker_image/ssh.rb +143 -0
  29. data/lib/kamaze/docker_image/version.rb +13 -0
  30. data/lib/kamaze/docker_image/version.yml +17 -0
  31. metadata +99 -0
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2017-2021 Dimitri Arrigoni <dimitri@arrigoni.me>
4
+ # License GPLv3+: GNU GPL version 3 or later
5
+ # <http://www.gnu.org/licenses/gpl.html>.
6
+ # This is free software: you are free to change and redistribute it.
7
+ # There is NO WARRANTY, to the extent permitted by law.
8
+
9
+ require_relative 'helper'
10
+
11
+ # @type [Kamaze::DockerImage::Loader::Context] self
12
+ self.singleton_class.__send__(:define_method, :helper) do
13
+ Kamaze::DockerImage::Loader::Helper.new(image)
14
+ end
15
+
16
+ # tasks --------------------------------------------------------------
17
+
18
+ Pathname.new(__dir__).join('tasks').tap do |path|
19
+ Dir.glob(path.join('*.rb')).map { |fp| Pathname.new(fp) }.each do |file|
20
+ self.instance_eval(file.read, file.to_s, 1)
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2017-2021 Dimitri Arrigoni <dimitri@arrigoni.me>
4
+ # License GPLv3+: GNU GPL version 3 or later
5
+ # <http://www.gnu.org/licenses/gpl.html>.
6
+ # This is free software: you are free to change and redistribute it.
7
+ # There is NO WARRANTY, to the extent permitted by law.
8
+
9
+ # @type [Kamaze::DockerImage::Loader::Helper] helper
10
+
11
+ if image.available_commands.include?(:build)
12
+ desc 'Build image'
13
+
14
+ task helper.appoint(:build), [:cached] do |task, args|
15
+ autoload(:YAML, 'yaml')
16
+
17
+ YAML.safe_load(args.key?(:cached) ? args[:cached] : 'true').tap do |cached|
18
+ helper.call(task, args) { cached ? image.build : image.rebuild }
19
+ end
20
+
21
+ task.reenable
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2017-2021 Dimitri Arrigoni <dimitri@arrigoni.me>
4
+ # License GPLv3+: GNU GPL version 3 or later
5
+ # <http://www.gnu.org/licenses/gpl.html>.
6
+ # This is free software: you are free to change and redistribute it.
7
+ # There is NO WARRANTY, to the extent permitted by law.
8
+
9
+ # @type [Kamaze::DockerImage::Loader::Helper] helper
10
+
11
+ if image.available_commands.include?(:exec)
12
+ desc 'Run a command in a running container'
13
+
14
+ task helper.appoint(:exec), [:command] do |task, args|
15
+ helper.call(task, args) { image.exec(args[:command]) }
16
+
17
+ task.reenable
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2017-2021 Dimitri Arrigoni <dimitri@arrigoni.me>
4
+ # License GPLv3+: GNU GPL version 3 or later
5
+ # <http://www.gnu.org/licenses/gpl.html>.
6
+ # This is free software: you are free to change and redistribute it.
7
+ # There is NO WARRANTY, to the extent permitted by law.
8
+
9
+ # @type [Kamaze::DockerImage::Loader::Helper] helper
10
+
11
+ if image.available_commands.include?(:push)
12
+ desc 'Push image'
13
+
14
+ task helper.appoint(:push) do |task|
15
+ helper.call(task) { image.push }
16
+
17
+ task.reenable
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2017-2021 Dimitri Arrigoni <dimitri@arrigoni.me>
4
+ # License GPLv3+: GNU GPL version 3 or later
5
+ # <http://www.gnu.org/licenses/gpl.html>.
6
+ # This is free software: you are free to change and redistribute it.
7
+ # There is NO WARRANTY, to the extent permitted by law.
8
+
9
+ # @type [Kamaze::DockerImage::Loader::Helper] helper
10
+
11
+ %i[start stop].map { |v| image.available_commands.include?(v) }.uniq.tap do |v|
12
+ if v == [true]
13
+ desc 'Restart container'
14
+
15
+ task helper.appoint(:restart) do |task|
16
+ helper.call(task) { image.restart }
17
+
18
+ task.reenable
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2017-2021 Dimitri Arrigoni <dimitri@arrigoni.me>
4
+ # License GPLv3+: GNU GPL version 3 or later
5
+ # <http://www.gnu.org/licenses/gpl.html>.
6
+ # This is free software: you are free to change and redistribute it.
7
+ # There is NO WARRANTY, to the extent permitted by law.
8
+
9
+ # @type [Kamaze::DockerImage::Loader::Helper] helper
10
+
11
+ if image.available_commands.include?(:run)
12
+ desc 'Run a command in a new container'
13
+
14
+ task helper.appoint(:run), [:command] do |task, args|
15
+ helper.call(task, args) { image.run(args[:command]) }
16
+
17
+ task.reenable
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2017-2021 Dimitri Arrigoni <dimitri@arrigoni.me>
4
+ # License GPLv3+: GNU GPL version 3 or later
5
+ # <http://www.gnu.org/licenses/gpl.html>.
6
+ # This is free software: you are free to change and redistribute it.
7
+ # There is NO WARRANTY, to the extent permitted by law.
8
+
9
+ # @type [Kamaze::DockerImage::Loader::Helper] helper
10
+
11
+ if image.ssh.enabled?
12
+ desc 'Connect to container using Secure Shell (SSH)'
13
+
14
+ task helper.appoint(:ssh), [:command] do |task, args|
15
+ helper.call(task) { image.ssh.call(args[:command]) }
16
+
17
+ task.reenable
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2017-2021 Dimitri Arrigoni <dimitri@arrigoni.me>
4
+ # License GPLv3+: GNU GPL version 3 or later
5
+ # <http://www.gnu.org/licenses/gpl.html>.
6
+ # This is free software: you are free to change and redistribute it.
7
+ # There is NO WARRANTY, to the extent permitted by law.
8
+
9
+ # @type [Kamaze::DockerImage::Loader::Helper] helper
10
+
11
+ if image.available_commands.include?(:start)
12
+ desc 'Start container as %<run_as>s' % { run_as: image.run_as }
13
+
14
+ task helper.appoint(:start) do |task|
15
+ helper.call(task) { image.start }
16
+
17
+ task.reenable
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2017-2021 Dimitri Arrigoni <dimitri@arrigoni.me>
4
+ # License GPLv3+: GNU GPL version 3 or later
5
+ # <http://www.gnu.org/licenses/gpl.html>.
6
+ # This is free software: you are free to change and redistribute it.
7
+ # There is NO WARRANTY, to the extent permitted by law.
8
+
9
+ # @type [Kamaze::DockerImage::Loader::Helper] helper
10
+
11
+ if image.available_commands.include?(:stop)
12
+ desc 'Stop container'
13
+
14
+ task helper.appoint(:stop) do |task|
15
+ helper.call(task) { image.stop }
16
+
17
+ task.reenable
18
+ end
19
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2017-2021 Dimitri Arrigoni <dimitri@arrigoni.me>
4
+ # License GPLv3+: GNU GPL version 3 or later
5
+ # <http://www.gnu.org/licenses/gpl.html>.
6
+ # This is free software: you are free to change and redistribute it.
7
+ # There is NO WARRANTY, to the extent permitted by law.
8
+
9
+ require_relative '../docker_image'
10
+
11
+ # Runner provide methods to execute image related actions
12
+ #
13
+ # @see #actions
14
+ # @see Kamaze::DockerImage::Concern::Setup#default_commands
15
+ class Kamaze::DockerImage::Runner
16
+ include Kamaze::DockerImage::Concern::Containers
17
+
18
+ # noinspection RubyConstantNamingConvention
19
+ Command = Kamaze::DockerImage::Command
20
+
21
+ # Available actions
22
+ #
23
+ # Actions registrable on ``image``.
24
+ # @see Runner#actions
25
+ ACTIONS = %i[restart start stop exec run build push rm rebuild].sort
26
+
27
+ autoload :Storage, "#{__dir__}/runner/storage"
28
+
29
+ # @param [Kamaze::DockerImage] image
30
+ #
31
+ # @see Kamaze::DockerImage::Concern::Setup#default_commands
32
+ def initialize(image)
33
+ @config = image.to_h.reject! { |k| k == :commands }.freeze
34
+ @commands = Storage[image.commands].tap do |store|
35
+ store.config = @config
36
+ end
37
+
38
+ @commands.freeze
39
+ end
40
+
41
+ # Build image
42
+ def build(&block)
43
+ command(:build).run(&block)
44
+ end
45
+
46
+ # Push image
47
+ def push(&block)
48
+ command(:push).run(&block)
49
+ end
50
+
51
+ # Build image (do not use cache)
52
+ def rebuild(&block)
53
+ command(:rebuild).run(&block)
54
+ end
55
+
56
+ def run(extra = nil, &block)
57
+ command(:run, extra).run(&block)
58
+ end
59
+
60
+ def exec(extra = nil, &block)
61
+ default = config.fetch(:exec_command)
62
+ extra ||= default
63
+
64
+ command(:exec, extra).run(&block)
65
+ end
66
+
67
+ def start(&block)
68
+ command(:start).run(&block) unless running?
69
+ end
70
+
71
+ def stop(&block)
72
+ command(:stop).run(&block) if started?
73
+ end
74
+
75
+ def rm(&block)
76
+ command(:rm).run(&block) if started?
77
+ end
78
+
79
+ def restart(&block)
80
+ stop(&block)
81
+ rm(&block)
82
+ start(&block)
83
+ end
84
+
85
+ # @return [Array<Symbol>]
86
+ def actions
87
+ ACTIONS
88
+ end
89
+
90
+ # Denote container is started.
91
+ #
92
+ # @return [Boolean]
93
+ def started?
94
+ # !fetch_containers(config.fetch(:run_as)).empty?
95
+ config.fetch(:run_as).yield_self { |name| !containers[name].nil? }
96
+ end
97
+
98
+ # Denote container is running.
99
+ #
100
+ # @return [Boolean]
101
+ def running?
102
+ # !fetch_containers(config.fetch(:run_as), :running).empty?
103
+ config.fetch(:run_as).yield_self { |name| containers[name]&.running? }
104
+ end
105
+
106
+ protected
107
+
108
+ # @return [Hash]
109
+ attr_reader :config
110
+
111
+ # Get commands
112
+ #
113
+ # @return [Hash]
114
+ attr_reader :commands
115
+
116
+ # Generate command.
117
+ #
118
+ # @param [String|Symbol] name
119
+ # @param [String|nil] extra
120
+ # @return [Command]
121
+ def command(name, extra = nil)
122
+ command = commands.fetch(name.to_sym)
123
+
124
+ Command.new(command, config, extra)
125
+ end
126
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2017-2021 Dimitri Arrigoni <dimitri@arrigoni.me>
4
+ # License GPLv3+: GNU GPL version 3 or later
5
+ # <http://www.gnu.org/licenses/gpl.html>.
6
+ # This is free software: you are free to change and redistribute it.
7
+ # There is NO WARRANTY, to the extent permitted by law.
8
+
9
+ require_relative '../runner'
10
+
11
+ # Store commands to be executed.
12
+ #
13
+ # Command is shaped (formatted) on retrieval, using ``config``.
14
+ #
15
+ # @see #shape
16
+ # @see #config=
17
+ class Kamaze::DockerImage::Runner::Storage < Hash
18
+ include(Kamaze::DockerImage::Concern::Executable)
19
+
20
+ # @param [Hash] config
21
+ def config=(config)
22
+ @config = config.clone.freeze
23
+ end
24
+
25
+ # @return [Hash]
26
+ def config
27
+ @config.to_h
28
+ end
29
+
30
+ # Retrieves the value object corresponding to the key object.
31
+ #
32
+ # @return [Array<String>]
33
+ def [](key)
34
+ self.fetch(key)
35
+ rescue KeyError
36
+ super
37
+ end
38
+
39
+ # Returns a value from the hash for the given key.
40
+ #
41
+ # @raise [KeyError]
42
+ # @return [Array<String>]
43
+ def fetch(key)
44
+ key = key.to_sym
45
+ val = super
46
+
47
+ val ? shape(val) : val
48
+ end
49
+
50
+ protected
51
+
52
+ # Get executable
53
+ #
54
+ # @raise [Cliver::Dependency::NotFound]
55
+ # @return [String]
56
+ def executable
57
+ config[:docker_bin] || super
58
+ end
59
+
60
+ # Format given command
61
+ #
62
+ # @param [Array] command
63
+ # @return [Array<String>]
64
+ def shape(command)
65
+ # rubocop:disable Style/TernaryParentheses
66
+ h = {
67
+ opt_it: ($stdout.tty? and $stderr.tty?) ? '-it' : nil,
68
+ }
69
+ # rubocop:enable Style/TernaryParentheses
70
+
71
+ [executable]
72
+ .push(*command)
73
+ .map(&:to_s)
74
+ .map { |w| w % config.merge(h) }
75
+ .map { |w| w.to_s.empty? ? nil : w }
76
+ .compact
77
+ end
78
+ end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2017-2021 Dimitri Arrigoni <dimitri@arrigoni.me>
4
+ # License GPLv3+: GNU GPL version 3 or later
5
+ # <http://www.gnu.org/licenses/gpl.html>.
6
+ # This is free software: you are free to change and redistribute it.
7
+ # There is NO WARRANTY, to the extent permitted by law.
8
+
9
+ require_relative '../docker_image'
10
+ autoload :YAML, 'yaml'
11
+ autoload :Pathname, 'pathname'
12
+ autoload :Shellwords, 'shellwords'
13
+ autoload :Timeout, 'timeout'
14
+ autoload :Cliver, 'cliver'
15
+
16
+ # Runner provide methods to connect into image using ssh.
17
+ #
18
+ # Sample of use:
19
+ #
20
+ # ```ruby
21
+ # require 'kamaze/docker_image'
22
+ #
23
+ # ssh = Kamaze::DockerImage::SSH.new(run_as: 'kamaze_sample_image')
24
+ # ```
25
+ class Kamaze::DockerImage::SSH < Hash
26
+ include Kamaze::DockerImage::Concern::Containers
27
+
28
+ # noinspection RubyConstantNamingConvention
29
+ Command = Kamaze::DockerImage::Command
30
+
31
+ # @return [Hash]
32
+ attr_reader :config
33
+
34
+ # @param [Kamaze::DockerImage] image
35
+ #
36
+ # @see Kamaze::DockerImage::Concern::Setup#default_commands
37
+ def initialize(image)
38
+ defaults.merge(image.to_h[:ssh].to_h).tap do |ssh|
39
+ @config = image.to_h.merge(ssh: ssh).freeze
40
+ end.each { |k, v| self[k] = v }
41
+ end
42
+
43
+ # Connect to ssh (executing optional command ``cmd``).
44
+ #
45
+ # @param [Array<String|Object>] cmd
46
+ # @raise [Errno::ENONET]
47
+ #
48
+ # @see #command
49
+ def call(cmd = nil, &block)
50
+ network? ? wait : (raise Errno::ENONET)
51
+ rescue Timeout::Error
52
+ nil
53
+ ensure
54
+ command(cmd).run(&block)
55
+ end
56
+
57
+ # Wait until ssh is available.
58
+ #
59
+ # @return [self]
60
+ def wait
61
+ Timeout.timeout(config.fetch(:ssh).fetch(:timeout)) do
62
+ loop do
63
+ command(config.fetch(:ssh).fetch(:test)).tap do |command|
64
+ if command.execute
65
+ return block_given? ? yield(self) : self
66
+ end
67
+
68
+ sleep(0.5)
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ # Get defaults for config.
75
+ #
76
+ # @return [Hash{Symbol => Object}]
77
+ def defaults
78
+ YAML.safe_load(Pathname.new(__dir__).join('ssh.yml').read, [Symbol])
79
+ end
80
+
81
+ # @return [Command]
82
+ def command(cmd = nil)
83
+ cmd = Shellwords.split(cmd) if cmd.is_a?(String)
84
+
85
+ config.fetch(:ssh).fetch(:command)
86
+ .map { |w| w % params }
87
+ .push(*cmd.to_a)
88
+ .tap { |command| return Command.new(command, config) }
89
+ end
90
+
91
+ # Params used to shape command.
92
+ #
93
+ # @return [Hash{Symbol => Object}]
94
+ def params
95
+ # rubocop:disable Style/TernaryParentheses
96
+ {
97
+ executable: executable,
98
+ port: config.fetch(:ssh).fetch(:port),
99
+ user: config.fetch(:ssh).fetch(:user),
100
+ host: network.fetch(0),
101
+ opt_pty: ($stdout.tty? and $stderr.tty?) ? '-t' : '-T',
102
+ }
103
+ # rubocop:enable Style/TernaryParentheses
104
+ end
105
+
106
+ # Get absolute path for executable.
107
+ #
108
+ # @return [String|Object]
109
+ def executable
110
+ config.fetch(:ssh).fetch(:executable).tap do |executable|
111
+ Cliver.detect(executable).tap do |s|
112
+ return (s || executable).freeze
113
+ end
114
+ end
115
+ end
116
+
117
+ # Denote SSH is enabled.
118
+ #
119
+ # @return [Boolean]
120
+ def enabled?
121
+ config.fetch(:ssh)[:enabled]
122
+ end
123
+
124
+ # Get ip addresses.
125
+ #
126
+ # @return [Array<String>]
127
+ def network
128
+ # container = fetch_containers(config.fetch(:run_as), [:running])[0]
129
+ containers[config.fetch(:run_as)].yield_self do |container|
130
+ return [] if container.nil?
131
+ return [] unless container.running?
132
+
133
+ container.info
134
+ .fetch('NetworkSettings').fetch('Networks')
135
+ .values.keep_if { |v| v.to_h['IPAddress'] }.map { |v| v['IPAddress'] }.compact
136
+ end
137
+ end
138
+
139
+ # @return [Boolean]
140
+ def network?
141
+ !network.empty?
142
+ end
143
+ end