pups 1.0.3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+ require 'shellwords'
3
+
4
+ class Pups::Docker
5
+ class << self
6
+ def generate_env_arguments(config)
7
+ output = []
8
+ config&.each do |k, v|
9
+ if !v.to_s.empty?
10
+ output << "--env #{k}=#{escape_user_string_literal(v)}"
11
+ end
12
+ end
13
+ normalize_output(output)
14
+ end
15
+
16
+ def generate_link_arguments(config)
17
+ output = []
18
+ config&.each do |c|
19
+ output << "--link #{c['link']['name']}:#{c['link']['alias']}"
20
+ end
21
+ normalize_output(output)
22
+ end
23
+
24
+ def generate_expose_arguments(config)
25
+ output = []
26
+ config&.each do |c|
27
+ if c.to_s.include?(":")
28
+ output << "--publish #{c}"
29
+ else
30
+ output << "--expose #{c}"
31
+ end
32
+ end
33
+ normalize_output(output)
34
+ end
35
+
36
+ def generate_volume_arguments(config)
37
+ output = []
38
+ config&.each do |c|
39
+ output << "--volume #{c['volume']['host']}:#{c['volume']['guest']}"
40
+ end
41
+ normalize_output(output)
42
+ end
43
+
44
+ def generate_label_arguments(config)
45
+ output = []
46
+ config&.each do |k, v|
47
+ output << "--label #{k}=#{escape_user_string_literal(v)}"
48
+ end
49
+ normalize_output(output)
50
+ end
51
+
52
+ private
53
+ def escape_user_string_literal(str)
54
+ # We need to escape the following strings as they are more likely to contain
55
+ # special characters than any of the other config variables on a Linux system:
56
+ # - the value side of an environment variable
57
+ # - the value side of a label.
58
+ if str.to_s.include?(" ")
59
+ "\"#{Shellwords.escape(str)}\""
60
+ else
61
+ Shellwords.escape(str)
62
+ end
63
+ end
64
+
65
+ def normalize_output(output)
66
+ if output.empty?
67
+ ""
68
+ else
69
+ output.join(" ")
70
+ end
71
+ end
72
+ end
73
+ end
@@ -1,124 +1,128 @@
1
- require 'timeout'
1
+ # frozen_string_literal: true
2
2
 
3
- class Pups::ExecCommand < Pups::Command
4
- attr_reader :commands, :cd
5
- attr_accessor :background, :raise_on_fail, :stdin, :stop_signal
3
+ require 'timeout'
4
+ require 'English'
6
5
 
7
- def self.terminate_async(opts={})
6
+ module Pups
7
+ class ExecCommand < Pups::Command
8
+ attr_reader :commands, :cd
9
+ attr_accessor :background, :raise_on_fail, :stdin, :stop_signal
8
10
 
9
- return unless defined? @@asyncs
11
+ def self.terminate_async(opts = {})
12
+ return unless defined? @@asyncs
10
13
 
11
- Pups.log.info("Terminating async processes")
14
+ Pups.log.info('Terminating async processes')
12
15
 
13
- @@asyncs.each do |async|
14
- Pups.log.info("Sending #{async[:stop_signal]} to #{async[:command]} pid: #{async[:pid]}")
15
- Process.kill(async[:stop_signal],async[:pid]) rescue nil
16
- end
17
-
18
- @@asyncs.map do |async|
19
- Thread.new do
16
+ @@asyncs.each do |async|
17
+ Pups.log.info("Sending #{async[:stop_signal]} to #{async[:command]} pid: #{async[:pid]}")
20
18
  begin
19
+ Process.kill(async[:stop_signal], async[:pid])
20
+ rescue StandardError
21
+ nil
22
+ end
23
+ end
24
+
25
+ @@asyncs.map do |async|
26
+ Thread.new do
21
27
  Timeout.timeout(opts[:wait] || 10) do
22
- Process.wait(async[:pid]) rescue nil
28
+ Process.wait(async[:pid])
29
+ rescue StandardError
30
+ nil
23
31
  end
24
32
  rescue Timeout::Error
25
33
  Pups.log.info("#{async[:command]} pid:#{async[:pid]} did not terminate cleanly, forcing termination!")
26
34
  begin
27
- Process.kill("KILL",async[:pid])
35
+ Process.kill('KILL', async[:pid])
28
36
  Process.wait(async[:pid])
29
37
  rescue Errno::ESRCH
30
38
  rescue Errno::ECHILD
31
39
  end
32
-
33
40
  end
34
- end
35
- end.each(&:join)
36
-
37
- end
38
-
39
- def self.from_hash(hash, params)
40
- cmd = new(params, hash["cd"])
41
-
42
- case c = hash["cmd"]
43
- when String then cmd.add(c)
44
- when Array then c.each{|i| cmd.add(i)}
41
+ end.each(&:join)
45
42
  end
46
43
 
47
- cmd.background = hash["background"]
48
- cmd.stop_signal = hash["stop_signal"] || "TERM"
49
- cmd.raise_on_fail = hash["raise_on_fail"] if hash.key? "raise_on_fail"
50
- cmd.stdin = interpolate_params(hash["stdin"], params)
44
+ def self.from_hash(hash, params)
45
+ cmd = new(params, hash['cd'])
51
46
 
52
- cmd
53
- end
47
+ case c = hash['cmd']
48
+ when String then cmd.add(c)
49
+ when Array then c.each { |i| cmd.add(i) }
50
+ end
54
51
 
55
- def self.from_str(str, params)
56
- cmd = new(params)
57
- cmd.add(str)
58
- cmd
59
- end
52
+ cmd.background = hash['background']
53
+ cmd.stop_signal = hash['stop_signal'] || 'TERM'
54
+ cmd.raise_on_fail = hash['raise_on_fail'] if hash.key? 'raise_on_fail'
55
+ cmd.stdin = interpolate_params(hash['stdin'], params)
60
56
 
61
- def initialize(params, cd = nil)
62
- @commands = []
63
- @params = params
64
- @cd = interpolate_params(cd)
65
- @raise_on_fail = true
66
- end
57
+ cmd
58
+ end
67
59
 
68
- def add(cmd)
69
- @commands << process_params(cmd)
70
- end
60
+ def self.from_str(str, params)
61
+ cmd = new(params)
62
+ cmd.add(str)
63
+ cmd
64
+ end
71
65
 
72
- def run
73
- commands.each do |command|
74
- Pups.log.info("> #{command}")
75
- pid = spawn(command)
76
- Pups.log.info(@result.readlines.join("\n")) if @result
77
- pid
66
+ def initialize(params, cd = nil)
67
+ @commands = []
68
+ @params = params
69
+ @cd = interpolate_params(cd)
70
+ @raise_on_fail = true
78
71
  end
79
- rescue
80
- raise if @raise_on_fail
81
- end
82
72
 
83
- def spawn(command)
84
- if background
85
- pid = Process.spawn(command)
86
- (@@asyncs ||= []) << {pid: pid, command: command, stop_signal: (stop_signal || "TERM")}
87
- Thread.new do
88
- begin
89
- Process.wait(pid)
90
- rescue Errno::ECHILD
91
- # already exited so skip
92
- end
93
- @@asyncs.delete_if{|async| async[:pid] == pid}
94
- end
95
- return pid
73
+ def add(cmd)
74
+ @commands << process_params(cmd)
96
75
  end
97
76
 
98
- IO.popen(command, "w+") do |f|
99
- if stdin
100
- # need a way to get stdout without blocking
101
- Pups.log.info(stdin)
102
- f.write stdin
103
- f.close
104
- else
105
- Pups.log.info(f.readlines.join)
77
+ def run
78
+ commands.each do |command|
79
+ Pups.log.info("> #{command}")
80
+ pid = spawn(command)
81
+ Pups.log.info(@result.readlines.join("\n")) if @result
82
+ pid
106
83
  end
84
+ rescue StandardError
85
+ raise if @raise_on_fail
107
86
  end
108
87
 
109
- unless $? == 0
110
- err = Pups::ExecError.new("#{command} failed with return #{$?.inspect}")
111
- err.exit_code = $?.exitstatus
112
- raise err
113
- end
88
+ def spawn(command)
89
+ if background
90
+ pid = Process.spawn(command)
91
+ (@@asyncs ||= []) << { pid: pid, command: command, stop_signal: (stop_signal || 'TERM') }
92
+ Thread.new do
93
+ begin
94
+ Process.wait(pid)
95
+ rescue Errno::ECHILD
96
+ # already exited so skip
97
+ end
98
+ @@asyncs.delete_if { |async| async[:pid] == pid }
99
+ end
100
+ return pid
101
+ end
114
102
 
115
- nil
103
+ IO.popen(command, 'w+') do |f|
104
+ if stdin
105
+ # need a way to get stdout without blocking
106
+ Pups.log.info(stdin)
107
+ f.write stdin
108
+ f.close
109
+ else
110
+ Pups.log.info(f.readlines.join)
111
+ end
112
+ end
116
113
 
117
- end
114
+ unless $CHILD_STATUS == 0
115
+ err = Pups::ExecError.new("#{command} failed with return #{$CHILD_STATUS.inspect}")
116
+ err.exit_code = $CHILD_STATUS.exitstatus
117
+ raise err
118
+ end
118
119
 
119
- def process_params(cmd)
120
- processed = interpolate_params(cmd)
121
- @cd ? "cd #{cd} && #{processed}" : processed
122
- end
120
+ nil
121
+ end
123
122
 
123
+ def process_params(cmd)
124
+ processed = interpolate_params(cmd)
125
+ @cd ? "cd #{cd} && #{processed}" : processed
126
+ end
127
+ end
124
128
  end
@@ -1,41 +1,37 @@
1
- class Pups::FileCommand < Pups::Command
2
- attr_accessor :path, :contents, :params, :type, :chmod, :chown
1
+ # frozen_string_literal: true
3
2
 
4
- def self.from_hash(hash, params)
5
- command = new
6
- command.path = hash["path"]
7
- command.contents = hash["contents"]
8
- command.chmod = hash["chmod"]
9
- command.chown = hash["chown"]
10
- command.params = params
3
+ module Pups
4
+ class FileCommand < Pups::Command
5
+ attr_accessor :path, :contents, :params, :type, :chmod, :chown
11
6
 
12
- command
13
- end
7
+ def self.from_hash(hash, params)
8
+ command = new
9
+ command.path = hash['path']
10
+ command.contents = hash['contents']
11
+ command.chmod = hash['chmod']
12
+ command.chown = hash['chown']
13
+ command.params = params
14
14
 
15
- def initialize
16
- @params = {}
17
- @type = :bash
18
- end
15
+ command
16
+ end
19
17
 
20
- def params=(p)
21
- @params = p
22
- end
18
+ def initialize
19
+ @params = {}
20
+ @type = :bash
21
+ end
23
22
 
24
- def run
25
- path = interpolate_params(@path)
23
+ attr_writer :params
26
24
 
27
- `mkdir -p #{File.dirname(path)}`
28
- File.open(path, "w") do |f|
29
- f.write(interpolate_params(contents))
30
- end
31
- if @chmod
32
- `chmod #{@chmod} #{path}`
33
- end
34
- if @chown
35
- `chown #{@chown} #{path}`
25
+ def run
26
+ path = interpolate_params(@path)
27
+
28
+ `mkdir -p #{File.dirname(path)}`
29
+ File.open(path, 'w') do |f|
30
+ f.write(interpolate_params(contents))
31
+ end
32
+ `chmod #{@chmod} #{path}` if @chmod
33
+ `chown #{@chown} #{path}` if @chown
34
+ Pups.log.info("File > #{path} chmod: #{@chmod} chown: #{@chown}")
36
35
  end
37
- Pups.log.info("File > #{path} chmod: #{@chmod} chown: #{@chown}")
38
36
  end
39
-
40
37
  end
41
-
@@ -1,48 +1,50 @@
1
- class Pups::MergeCommand < Pups::Command
2
- attr_reader :filename
3
- attr_reader :merge_hash
4
-
5
- def self.from_str(command, params)
6
- new(command,params)
7
- end
8
-
9
- def self.parse_command(command)
10
- split = command.split(" ")
11
- raise ArgumentError.new("Invalid merge command #{command}") unless split[-1][0] == "$"
12
-
13
- [split[0..-2].join(" ") , split[-1][1..-1]]
14
- end
15
-
16
- def initialize(command, params)
17
- @params = params
18
-
19
- filename, target_param = Pups::MergeCommand.parse_command(command)
20
- @filename = interpolate_params(filename)
21
- @merge_hash = params[target_param]
22
- end
23
-
24
- def run
25
- merged = self.class.deep_merge(YAML.load_file(@filename), @merge_hash)
26
- File.open(@filename,"w"){|f| f.write(merged.to_yaml) }
27
- Pups.log.info("Merge: #{@filename} with: \n#{@merge_hash.inspect}")
1
+ # frozen_string_literal: true
2
+
3
+ module Pups
4
+ class MergeCommand < Pups::Command
5
+ attr_reader :filename, :merge_hash
6
+
7
+ def self.from_str(command, params)
8
+ new(command, params)
9
+ end
10
+
11
+ def self.parse_command(command)
12
+ split = command.split(' ')
13
+ raise ArgumentError, "Invalid merge command #{command}" unless split[-1][0] == '$'
14
+
15
+ [split[0..-2].join(' '), split[-1][1..-1]]
16
+ end
17
+
18
+ def initialize(command, params)
19
+ @params = params
20
+
21
+ filename, target_param = Pups::MergeCommand.parse_command(command)
22
+ @filename = interpolate_params(filename)
23
+ @merge_hash = params[target_param]
24
+ end
25
+
26
+ def run
27
+ merged = self.class.deep_merge(YAML.load_file(@filename), @merge_hash)
28
+ File.open(@filename, 'w') { |f| f.write(merged.to_yaml) }
29
+ Pups.log.info("Merge: #{@filename} with: \n#{@merge_hash.inspect}")
30
+ end
31
+
32
+ def self.deep_merge(first, second, *args)
33
+ args ||= []
34
+ merge_arrays = args.include? :merge_arrays
35
+
36
+ merger = proc { |_key, v1, v2|
37
+ if v1.is_a?(Hash) && v2.is_a?(Hash)
38
+ v1.merge(v2, &merger)
39
+ elsif v1.is_a?(Array) && v2.is_a?(Array)
40
+ merge_arrays ? v1 + v2 : v2
41
+ elsif v2.is_a?(NilClass)
42
+ v1
43
+ else
44
+ v2
45
+ end
46
+ }
47
+ first.merge(second, &merger)
48
+ end
28
49
  end
29
-
30
- def self.deep_merge(first,second, *args)
31
- args ||= []
32
- merge_arrays = args.include? :merge_arrays
33
-
34
- merger = proc { |key, v1, v2|
35
- if Hash === v1 && Hash === v2
36
- v1.merge(v2, &merger)
37
- elsif Array === v1 && Array === v2
38
- merge_arrays ? v1 + v2 : v2
39
- elsif NilClass === v2
40
- v1
41
- else
42
- v2
43
- end
44
- }
45
- first.merge(second, &merger)
46
- end
47
-
48
50
  end