between_meals 0.0.1 → 0.0.2

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,197 @@
1
+ # vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
2
+
3
+ require 'json'
4
+ require 'fileutils'
5
+ require 'digest/md5'
6
+ require 'between_meals/util'
7
+
8
+ module BetweenMeals
9
+ # Knife does not have a usable API for using it as a lib
10
+ # This could be possibly refactored to touch its internals
11
+ # instead of shelling out
12
+ class Knife
13
+ include BetweenMeals::Util
14
+
15
+ def initialize(opts = {})
16
+ @logger = opts[:logger] || nil
17
+ @user = opts[:user] || ENV['USER']
18
+ @home = opts[:home] || ENV['HOME']
19
+ @host = opts[:host] || 'localhost'
20
+ @port = opts[:port] || 4000
21
+ @config = opts[:config] ||
22
+ "#{@home}/.chef/knife-#{@user}-taste-tester.rb"
23
+ @knife = opts[:bin] || 'knife'
24
+ @pem = opts[:pem] ||
25
+ "#{@home}/.chef/#{@user}-taste-tester.pem"
26
+ @role_dir = opts[:role_dir]
27
+ @cookbook_dirs = opts[:cookbook_dirs]
28
+ @databag_dir = opts[:databag_dir]
29
+ @checksum_dir = opts[:checksum_dir]
30
+ @client_key =
31
+ File.expand_path("#{@home}/.chef/#{@user}-taste-tester.pem")
32
+ end
33
+
34
+ def role_upload_all
35
+ roles = File.join(@role_dir, '*.rb')
36
+ exec!("#{@knife} role from file #{roles} -c #{@config}", @logger)
37
+ end
38
+
39
+ def role_upload(roles)
40
+ if roles.any?
41
+ roles = roles.map { |x| File.join(@role_dir, "#{x.name}.rb") }.join(' ')
42
+ exec!("#{@knife} role from file #{roles} -c #{@config}", @logger)
43
+ end
44
+ end
45
+
46
+ def role_delete(roles)
47
+ if roles.any?
48
+ roles.each do |role|
49
+ exec!(
50
+ "#{@knife} role delete #{role.name} --yes -c #{@config}", @logger
51
+ )
52
+ end
53
+ end
54
+ end
55
+
56
+ def cookbook_upload_all
57
+ exec!("#{@knife} cookbook upload -a -c #{@config}", @logger)
58
+ end
59
+
60
+ def cookbook_upload(cookbooks)
61
+ if cookbooks.any?
62
+ cookbooks = cookbooks.map { |x| x.name }.join(' ')
63
+ exec!("#{@knife} cookbook upload #{cookbooks} -c #{@config}", @logger)
64
+ end
65
+ end
66
+
67
+ def cookbook_delete(cookbooks)
68
+ if cookbooks.any?
69
+ cookbooks.each do |cookbook|
70
+ exec!("#{@knife} cookbook delete #{cookbook.name}" +
71
+ " --purge --yes -c #{@config}", @logger)
72
+ end
73
+ end
74
+ end
75
+
76
+ def databag_upload_all
77
+ glob = File.join(@databag_dir, '*', '*.json')
78
+ items = Dir.glob(glob).map do |file|
79
+ BetweenMeals::Changes::Databag.new(
80
+ { :status => :modified, :path => file }, @databag_dir
81
+ )
82
+ end
83
+ databag_upload(items)
84
+ end
85
+
86
+ def databag_upload(databags)
87
+ if databags.any?
88
+ databags.group_by { |x| x.name }.each do |dbname, dbs|
89
+ create_databag_if_missing(dbname)
90
+ dbitems = dbs.map do |x|
91
+ File.join(@databag_dir, dbname, "#{x.item}.json")
92
+ end.join(' ')
93
+ exec!("#{@knife} data bag from file #{dbname} #{dbitems}", @logger)
94
+ end
95
+ end
96
+ end
97
+
98
+ def databag_delete(databags)
99
+ if databags.any?
100
+ databags.group_by { |x| x.name }.each do |dbname, dbs|
101
+ dbs.each do |db|
102
+ exec!("#{@knife} data bag delete #{dbname} #{db.item}" +
103
+ " --yes -c #{@config}", @logger)
104
+ end
105
+ delete_databag_if_empty(dbname)
106
+ end
107
+ end
108
+ end
109
+
110
+ def write_user_config
111
+ cfg = <<-BLOCK
112
+ user = ENV['USER']
113
+ log_level :info
114
+ log_location STDOUT
115
+ node_name user
116
+ chef_server_url "http://#{@host}:#{@port}"
117
+ cache_type 'BasicFile'
118
+ client_key '#{@client_key}'
119
+ cache_options(:path => File.expand_path("#{@checksum_dir}"))
120
+ cookbook_path [
121
+ BLOCK
122
+ @cookbook_dirs.each do |dir|
123
+ cfg << " \"#{dir}\",\n"
124
+ end
125
+ cfg << "]\n"
126
+ unless File.directory?(File.dirname(@config))
127
+ Dir.mkdir(File.dirname(@config), 0755)
128
+ end
129
+ if !File.exists?(@config) ||
130
+ ::Digest::MD5.hexdigest(cfg) !=
131
+ ::Digest::MD5.hexdigest(File.read(@config))
132
+ @logger.info("Generating #{@config}")
133
+ File.write(@config, cfg)
134
+ end
135
+
136
+ # Won't work with shorter keys
137
+ pem = <<-BLOCK
138
+ -----BEGIN PRIVATE KEY-----
139
+ MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCs4Ih8+R/2hcYS
140
+ tccwJHd0cXHcUibC2wGYmRwf1fKxXLADvfuLRVBHOI5Hgd/ZXF70dowC5mDQ03gr
141
+ ouk8e7RL72MCKzPuG2V92sh/FnyKhkNsHCOEKaRRiP9lHVbZkS9LEotCKF7eOkL0
142
+ SVkGWx8pVZzrOFmhZgHaOFJ2/2t1irUTRFqTikrRsP2KvnhHdDlnnbUumZWxSuEN
143
+ oN6aSAQEOkKbEOLSn/EIMzEb2jtks7L7wkRErajH094jGoZbQvLiwRHDeM0C9uG7
144
+ 2sdQ45BG9EQOCdBzy1We5keqtJbXBcpwuBa0d1nQZIsGxnDb88+Kmh9h6k9/WmYN
145
+ zEQEeSSdAgMBAAECggEATFWQru4p6ObEwTo2y9EuVeJJzmkP6HZfzAu/WWdVFG/C
146
+ 4MQgsCxY+DnGyVhViVq6KuO1iwpCsbLOmyYCKszMncMESs7czUSXmezjHwrEzz3d
147
+ w3zhSdhBUCdX7kP4N3VeFp4Hk5zT1viO2+MPRjkyF0RQV6S4HwY1xy+baiP6RRnS
148
+ dGhUYsdz6fjxSkYEQy3/xHm9VLT6ZDV4pN2aA+LOFeveHKcnOjKFCBy4WzkO6fvj
149
+ 6H3jghxsHXoL7loCHfi9WX3xKjeXG/NjGbUfTH8P7IldUPha+ru/e8W/P+jjE1os
150
+ VkScWt08Vu6iTl1EkYeFxOMtSDZxeXNnDkPI0iDQgQKBgQDUMFYncQcZ4uLIfoZq
151
+ B+Lx7WJGlIwulOdobnraTd5lsrBHj1sC1+/f4cBSpJwUij2l3GdmmnOpuFAof5eu
152
+ mrBGu++5jy+0eIeT5O2d30O8GOBryJ+oAKI2/BPVCmM8d986wl5Esauycb++O7UO
153
+ RhpZFOCKbFvlNjhg+CdlvHSl7QKBgQDQkkvpnE//yWmhCPg27n7w3bTg5QPNrzTO
154
+ pF2iwvLK4XjRceTeW3P4f42HONzJNnmt5TexM9NbdE9g/exA5uNt59ZB5FeFiKAu
155
+ NmVXbmswPX6R/dlyidqzz1guGrL04e0dZehHZBNDr5Sio8IBjMWrpDIxjDJqEwUa
156
+ 4qCu4e6jcQKBgQDN0FTAzRFmOnxenNsj3aJzpx27+DpAtI4A7aicNwuQ+VGjF5nf
157
+ mDRDpGU3xBLgmXZSewaQrx+hb/XQUnJ+Ge0BrylHg2tyUbav7U3N49F/kWGdKmwy
158
+ OOsfCkLyUbEP5fXQuNdXKj6wR0UE8EUeI0FLRsTFf3VjTsRAynLsa295wQKBgAo3
159
+ QDSfDWQP73aNw+qc3+bYVSW20erfLAz7DAMO3WmGha5sj7M8c3+2b64x4M6SNn+H
160
+ /KRXT4DpP4IWrd238WfOtTXhA1BtErtwuqH/rIxeVra74kyz59xqyXzond9UuZJ5
161
+ DVmB01e7X+Jfdv8wb/YqQrMelNGRQOzCMPCf7FphAoGAbUh5HzNF2aciQJGA6Qk8
162
+ zvgEHqbS0/QkJGOZ+UifPRanTDuGYQkPdHHOER4UghbM+Kz5rZbBicJ3bCyNOsah
163
+ IAMAEpsWX2s2A6phgMCx7kH6wMmoZn3hb7Thh9+PfR8Jtp2/7k+ibCeF4gEWUCs5
164
+ 6wX4GR84dwyhG80yd4TP8Qo=
165
+ -----END PRIVATE KEY-----
166
+ BLOCK
167
+
168
+ unless File.exists?(@pem)
169
+ @logger.info("Generating #{@pem}")
170
+ File.write(@pem, pem)
171
+ end
172
+ end
173
+
174
+ private
175
+
176
+ def create_databag_if_missing(databag)
177
+ s = Mixlib::ShellOut.new("#{@knife} data bag list" +
178
+ " --format json -c #{@config}").run_command
179
+ s.error!
180
+ db = JSON.load(s.stdout)
181
+ unless db.include?(databag)
182
+ exec!("#{@knife} data bag create #{databag} -c #{@config}", @logger)
183
+ end
184
+ end
185
+
186
+ def delete_databag_if_empty(databag)
187
+ s = Mixlib::ShellOut.new("#{@knife} data bag show #{databag}" +
188
+ " --format json -c #{@config}").run_command
189
+ s.error!
190
+ db = JSON.load(s.stdout)
191
+ if db.empty?
192
+ exec!("#{@knife} data bag delete #{databag} --yes -c #{@config}",
193
+ @logger)
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,204 @@
1
+ # vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
2
+
3
+ require 'rugged'
4
+ require 'mixlib/shellout'
5
+ require 'between_meals/changeset'
6
+
7
+ module BetweenMeals
8
+ # Local checkout wrapper
9
+ class Repo
10
+ # Git provider
11
+ class Git < BetweenMeals::Repo
12
+ def setup
13
+ if File.exists?(File.expand_path(@repo_path))
14
+ @repo = Rugged::Repository.new(File.expand_path(@repo_path))
15
+ else
16
+ @repo = nil
17
+ end
18
+ @bin = 'git'
19
+ end
20
+
21
+ def exists?
22
+ @repo && !@repo.empty?
23
+ end
24
+
25
+ def head_rev
26
+ @repo.head.target.oid
27
+ end
28
+
29
+ def last_msg
30
+ @repo.head.target.message
31
+ end
32
+
33
+ def last_msg=(msg)
34
+ @repo.head.target.amend(
35
+ {
36
+ :message => msg,
37
+ :update_ref => 'HEAD',
38
+ }
39
+ )
40
+ end
41
+
42
+ def last_author
43
+ @repo.head.target.to_hash[:author]
44
+ end
45
+
46
+ def head_parents
47
+ @repo.head.target.parents
48
+ end
49
+
50
+ def checkout(url)
51
+ s = Mixlib::ShellOut.new(
52
+ "#{@bin} clone #{url} #{@repo} #{@repo_path}"
53
+ ).run_command
54
+ s.error!
55
+ @repo = Rugged::Repository.new(File.expand_path(@repo_path))
56
+ end
57
+
58
+ # Return files changed between two revisions
59
+ def changes(start_ref, end_ref)
60
+ check_refs(start_ref, end_ref)
61
+ s = Mixlib::ShellOut.new(
62
+ "#{@bin} diff --name-status #{start_ref} #{end_ref}",
63
+ :cwd => File.expand_path(@repo_path)
64
+ )
65
+ s.run_command.error!
66
+ begin
67
+ parse_status(s.stdout).compact
68
+ rescue => e
69
+ # We've seen some weird non-reproducible failures here
70
+ @logger.error(
71
+ 'Something went wrong. Please please report this output.'
72
+ )
73
+ @logger.error(e)
74
+ s.stdout.lines.each do |line|
75
+ @logger.error(line.strip)
76
+ end
77
+ exit(1)
78
+ end
79
+ end
80
+
81
+ def update
82
+ cmd = Mixlib::ShellOut.new(
83
+ "#{@bin} pull --rebase", :cwd => File.expand_path(@repo_path)
84
+ )
85
+ cmd.run_command
86
+ if cmd.exitstatus != 0
87
+ @logger.error('Something went wrong with git!')
88
+ @logger.error(cmd.stdout)
89
+ fail
90
+ end
91
+ cmd.stdout
92
+ end
93
+
94
+ # Return all files
95
+ def files
96
+ @repo.index.map { |x| { :path => x[:path], :status => :created } }
97
+ end
98
+
99
+ def status
100
+ cmd = Mixlib::ShellOut.new(
101
+ "#{@bin} status --porcelain 2>&1",
102
+ :cwd => File.expand_path(@repo_path)
103
+ )
104
+ cmd.run_command
105
+ if cmd.exitstatus != 0
106
+ @logger.error('Something went wrong with git!')
107
+ @logger.error(cmd.stdout)
108
+ fail
109
+ end
110
+ cmd.stdout
111
+ end
112
+
113
+ private
114
+
115
+ def check_refs(start_ref, end_ref)
116
+ unless @repo.exists?(start_ref)
117
+ fail Changeset::ReferenceError
118
+ end
119
+ unless end_ref.nil?
120
+ unless @repo.exists?(end_ref)
121
+ fail Changeset::ReferenceError
122
+ end
123
+ end
124
+ end
125
+
126
+ def parse_status(changes)
127
+ # man git-diff-files
128
+ # Possible status letters are:
129
+ #
130
+ # A: addition of a file
131
+ # C: copy of a file into a new one
132
+ # D: deletion of a file
133
+ # M: modification of the contents or mode of a file
134
+ # R: renaming of a file
135
+ # T: change in the type of the file
136
+ # U: file is unmerged (you must complete the merge before it can
137
+ # be committed)
138
+ # X: "unknown" change type (most probably a bug, please report it)
139
+
140
+ # rubocop:disable MultilineBlockChain
141
+ changes.lines.map do |line|
142
+ case line
143
+ when /^A\s+(\S+)$/
144
+ # A path
145
+ {
146
+ :status => :modified,
147
+ :path => Regexp.last_match(1)
148
+ }
149
+ when /^C(?:\d*)\s+(\S+)\s+(\S+)/
150
+ # C<numbers> path1 path2
151
+ {
152
+ :status => :modified,
153
+ :path => Regexp.last_match(2)
154
+ }
155
+ when /^D\s+(\S+)$/
156
+ # D path
157
+ {
158
+ :status => :deleted,
159
+ :path => Regexp.last_match(1)
160
+ }
161
+ when /^M(?:\d*)\s+(\S+)$/
162
+ # M<numbers> path
163
+ {
164
+ :status => :modified,
165
+ :path => Regexp.last_match(1)
166
+ }
167
+ when /^R(?:\d*)\s+(\S+)\s+(\S+)/
168
+ # R<numbers> path1 path2
169
+ [
170
+ {
171
+ :status => :deleted,
172
+ :path => Regexp.last_match(1)
173
+ },
174
+ {
175
+ :status => :modified,
176
+ :path => Regexp.last_match(2)
177
+ }
178
+ ]
179
+ when /^T\s+(\S+)$/
180
+ # T path
181
+ [
182
+ {
183
+ :status => :deleted,
184
+ :path => Regexp.last_match(1)
185
+ },
186
+ {
187
+ :status => :modified,
188
+ :path => Regexp.last_match(1)
189
+ }
190
+ ]
191
+ else
192
+ fail 'No match'
193
+ end
194
+ end.flatten.map do |x|
195
+ {
196
+ :status => x[:status],
197
+ :path => x[:path].sub("#{@repo_path}/", '')
198
+ }
199
+ end
200
+ # rubocop:enable MultilineBlockChain
201
+ end
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,121 @@
1
+ # vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
2
+
3
+ require 'between_meals/repo'
4
+ require 'between_meals/changeset'
5
+ require 'mixlib/shellout'
6
+
7
+ module BetweenMeals
8
+ # Local checkout wrapper
9
+ class Repo
10
+ # SVN implementation
11
+ class Svn < BetweenMeals::Repo
12
+ def setup
13
+ @bin = 'svn'
14
+ end
15
+
16
+ def exists?
17
+ # this shuold be better
18
+ Dir.exists?(@repo_path)
19
+ end
20
+
21
+ def head_rev
22
+ s = Mixlib::ShellOut.new("#{@bin} info #{@repo_path}").run_command
23
+ s.error!
24
+ s.stdout.each_line do |line|
25
+ m = line.match(/Last Changed Rev: (\d+)$/)
26
+ return m[1] if m
27
+ end
28
+ end
29
+
30
+ def latest_revision
31
+ s = Mixlib::ShellOut.new("#{@bin} info #{@repo_path}").run_command
32
+ s.error!
33
+ s.stdout.each do |line|
34
+ m = line.match(/Revision: (\d+)$/)
35
+ return m[1] if m
36
+ end
37
+ end
38
+
39
+ def checkout(url)
40
+ s = Mixlib::ShellOut.new(
41
+ "#{@bin} co --ignore-externals #{url} #{@repo_path}").run_command
42
+ s.error!
43
+ end
44
+
45
+ # Return files changed between two revisions
46
+ def changes(start_ref, end_ref)
47
+ check_refs(start_ref, end_ref)
48
+ s = Mixlib::ShellOut.new(
49
+ "#{@bin} diff -r #{start_ref}:#{end_ref} --summarize #{@repo_path}")
50
+ s.run_command.error!
51
+ @logger.info("Diff between #{start_ref} and #{end_ref}")
52
+ s.stdout.lines.map do |line|
53
+ m = line.match(/^(\w)\w?\s+(\S+)$/)
54
+ fail "Could not parse line: #{line}" unless m
55
+
56
+ {
57
+ :status => m[1] == 'D' ? :deleted : :modified,
58
+ :path => m[2].sub("#{@repo_path}/", '')
59
+ }
60
+ end
61
+ end
62
+
63
+ def update
64
+ cleanup
65
+ revert
66
+ up
67
+ end
68
+
69
+ # Return all files
70
+ def files
71
+ s = Mixlib::ShellOut.new("#{@bin} ls --depth infinity #{@repo_path}")
72
+ s.run_command
73
+ s.error!
74
+ s.stdout.split("\n").map do |x|
75
+ { :path => x, :status => :created }
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ def run(cmd)
82
+ Mixlib::ShellOut.new(cmd).run_command.error!
83
+ end
84
+
85
+ def revert
86
+ run("#{@bin} revert -R #{@repo_path}")
87
+ end
88
+
89
+ def up
90
+ run("#{@bin} update #{@repo_path}")
91
+ end
92
+
93
+ def cleanup
94
+ run("#{@bin} cleanup #{@repo_path}")
95
+ end
96
+
97
+ def first_revision
98
+ 0
99
+ end
100
+
101
+ private
102
+
103
+ def check_refs(start_ref, end_ref)
104
+ s = Mixlib::ShellOut.new(
105
+ "#{@bin} info -r #{start_ref}",
106
+ :cwd => @repo_path
107
+ ).run_command
108
+ s.error!
109
+ if end_ref
110
+ s = Mixlib::ShellOut.new(
111
+ "#{@bin} info -r #{end_ref}",
112
+ :cwd => @repo_path
113
+ ).run_command
114
+ s.error!
115
+ end
116
+ rescue
117
+ raise Changeset::ReferenceError
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,113 @@
1
+ # vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
2
+
3
+ require 'mixlib/shellout'
4
+
5
+ module BetweenMeals
6
+ # Local checkout wrapper
7
+ class Repo
8
+ attr_reader :repo_path
9
+ attr_writer :bin
10
+
11
+ def initialize(repo_path, logger)
12
+ @repo_path = repo_path
13
+ @logger = logger
14
+ @repo = nil
15
+ @bin = nil
16
+ setup
17
+ rescue
18
+ @logger.warn("Unable to read repo from #{File.expand_path(repo_path)}")
19
+ exit(1)
20
+ end
21
+
22
+ def self.get(type, repo_path, logger)
23
+ case type
24
+ when 'svn'
25
+ require 'between_meals/repo/svn'
26
+ BetweenMeals::Repo::Svn.new(repo_path, logger)
27
+ when 'git'
28
+ require 'between_meals/repo/git'
29
+ BetweenMeals::Repo::Git.new(repo_path, logger)
30
+ else
31
+ fail "Do not know repo type #{type}"
32
+ end
33
+ end
34
+
35
+ def exists?
36
+ fail 'Not implemented'
37
+ end
38
+
39
+ def status
40
+ fail 'Not implemented'
41
+ end
42
+
43
+ def setup
44
+ fail 'Not implemented'
45
+ end
46
+
47
+ def head_rev
48
+ fail 'Not implemented'
49
+ end
50
+
51
+ def head_msg
52
+ fail 'Not implemented'
53
+ end
54
+
55
+ def head_msg=
56
+ fail 'Not implemented'
57
+ end
58
+
59
+ def head_parents
60
+ fail 'Not implemented'
61
+ end
62
+
63
+ def latest_revision
64
+ fail 'Not implemented'
65
+ end
66
+
67
+ def create(_url)
68
+ fail 'Not implemented'
69
+ end
70
+
71
+ # Return files changed between two revisions
72
+ def changes(_start_ref, _end_ref)
73
+ fail 'Not implemented'
74
+ end
75
+
76
+ def update
77
+ fail 'Not implemented'
78
+ end
79
+
80
+ # Return all files
81
+ def files
82
+ fail 'Not implemented'
83
+ end
84
+
85
+ def latest_revision
86
+ fail 'Not implemented'
87
+ end
88
+
89
+ def head
90
+ fail 'Not implemented'
91
+ end
92
+
93
+ def checkout
94
+ fail 'Not implemented'
95
+ end
96
+
97
+ def update
98
+ fail 'Not implemented'
99
+ end
100
+
101
+ def last_author
102
+ fail 'Not implemented'
103
+ end
104
+
105
+ def last_msg
106
+ fail 'Not implemented'
107
+ end
108
+
109
+ def last_msg=
110
+ fail 'Not implemented'
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,74 @@
1
+ # vim: syntax=ruby:expandtab:shiftwidth=2:softtabstop=2:tabstop=2
2
+
3
+ require 'colorize'
4
+ require 'socket'
5
+ require 'timeout'
6
+
7
+ module BetweenMeals
8
+ # A set of simple utility functions used throughout BetweenMeals
9
+ #
10
+ # Feel freeo to use... note that if you pass in a logger once
11
+ # you don't need to again, but be safe and always pass one in. :)
12
+
13
+ # Util classes need class vars :)
14
+ # rubocop:disable ClassVars
15
+ module Util
16
+ @@logger = nil
17
+
18
+ def time(logger = nil)
19
+ @@logger = logger if logger
20
+ t0 = Time.now
21
+ yield
22
+ info("Executed in #{format('%.2f', Time.now - t0)}s")
23
+ end
24
+
25
+ def exec!(command, logger = nil)
26
+ @@logger = logger if logger
27
+ c = execute(command)
28
+ c.error!
29
+ return c.status.exitstatus, c.stdout
30
+ end
31
+
32
+ def exec(command, logger = nil)
33
+ @@logger = logger if logger
34
+ c = execute(command)
35
+ return c.status.exitstatus, c.stdout
36
+ end
37
+
38
+ private
39
+
40
+ def info(msg)
41
+ @@logger.info(msg) if @@logger
42
+ end
43
+
44
+ def execute(command)
45
+ info("Running: #{command}")
46
+ c = Mixlib::ShellOut.new(command)
47
+ c.run_command
48
+ c.stdout.lines.each do |line|
49
+ info("STDOUT: #{line.strip}")
50
+ end
51
+ c.stderr.lines.each do |line|
52
+ info("STDERR: #{line.strip.red}")
53
+ end
54
+ return c
55
+ end
56
+
57
+ def port_open?(port)
58
+ begin
59
+ Timeout.timeout(1) do
60
+ begin
61
+ s = TCPSocket.new('localhost', port)
62
+ s.close
63
+ return true
64
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
65
+ return false
66
+ end
67
+ end
68
+ rescue Timeout::Error
69
+ return false
70
+ end
71
+ return false
72
+ end
73
+ end
74
+ end