omnibus 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +1 -0
  4. data/.yardopts +7 -0
  5. data/CHANGELOG.md +3 -0
  6. data/Gemfile +9 -0
  7. data/LICENSE +201 -0
  8. data/NOTICE +9 -0
  9. data/README.md +186 -0
  10. data/Rakefile +7 -0
  11. data/bin/makeself-header.sh +401 -0
  12. data/bin/makeself.sh +407 -0
  13. data/bin/omnibus +11 -0
  14. data/lib/omnibus.rb +280 -0
  15. data/lib/omnibus/build_version.rb +281 -0
  16. data/lib/omnibus/builder.rb +323 -0
  17. data/lib/omnibus/clean_tasks.rb +30 -0
  18. data/lib/omnibus/cli.rb +35 -0
  19. data/lib/omnibus/cli/application.rb +136 -0
  20. data/lib/omnibus/cli/base.rb +112 -0
  21. data/lib/omnibus/cli/build.rb +66 -0
  22. data/lib/omnibus/cli/cache.rb +60 -0
  23. data/lib/omnibus/config.rb +186 -0
  24. data/lib/omnibus/exceptions.rb +54 -0
  25. data/lib/omnibus/fetcher.rb +184 -0
  26. data/lib/omnibus/fetchers.rb +22 -0
  27. data/lib/omnibus/fetchers/git_fetcher.rb +212 -0
  28. data/lib/omnibus/fetchers/net_fetcher.rb +191 -0
  29. data/lib/omnibus/fetchers/path_fetcher.rb +65 -0
  30. data/lib/omnibus/fetchers/s3_cache_fetcher.rb +42 -0
  31. data/lib/omnibus/health_check.rb +260 -0
  32. data/lib/omnibus/library.rb +70 -0
  33. data/lib/omnibus/overrides.rb +69 -0
  34. data/lib/omnibus/project.rb +566 -0
  35. data/lib/omnibus/reports.rb +99 -0
  36. data/lib/omnibus/s3_cacher.rb +136 -0
  37. data/lib/omnibus/software.rb +430 -0
  38. data/lib/omnibus/templates/Berksfile.erb +3 -0
  39. data/lib/omnibus/templates/Gemfile.erb +4 -0
  40. data/lib/omnibus/templates/README.md.erb +102 -0
  41. data/lib/omnibus/templates/Vagrantfile.erb +95 -0
  42. data/lib/omnibus/templates/gitignore.erb +8 -0
  43. data/lib/omnibus/templates/omnibus.rb.example.erb +5 -0
  44. data/lib/omnibus/templates/package_scripts/makeselfinst.erb +27 -0
  45. data/lib/omnibus/templates/package_scripts/postinst.erb +17 -0
  46. data/lib/omnibus/templates/package_scripts/postrm.erb +9 -0
  47. data/lib/omnibus/templates/project.rb.erb +21 -0
  48. data/lib/omnibus/templates/software/c-example.rb.erb +42 -0
  49. data/lib/omnibus/templates/software/erlang-example.rb.erb +38 -0
  50. data/lib/omnibus/templates/software/ruby-example.rb.erb +24 -0
  51. data/lib/omnibus/util.rb +61 -0
  52. data/lib/omnibus/version.rb +20 -0
  53. data/omnibus.gemspec +34 -0
  54. data/spec/build_version_spec.rb +228 -0
  55. data/spec/data/overrides/bad_line.overrides +3 -0
  56. data/spec/data/overrides/good.overrides +5 -0
  57. data/spec/data/overrides/with_dupes.overrides +4 -0
  58. data/spec/data/software/erchef.rb +40 -0
  59. data/spec/overrides_spec.rb +114 -0
  60. data/spec/software_spec.rb +71 -0
  61. data/spec/spec_helper.rb +28 -0
  62. metadata +239 -0
@@ -0,0 +1,65 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2012 Opscode, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ module Omnibus
19
+
20
+ # Fetcher implementation for projects on the filesystem
21
+ class PathFetcher < Fetcher
22
+
23
+ name :path
24
+
25
+ def initialize(software)
26
+ @name = software.name
27
+ @source = software.source
28
+ @project_dir = software.project_dir
29
+ @version = software.version
30
+ end
31
+
32
+ def description
33
+ s=<<-E
34
+ source path: #{@source[:path]}
35
+ local location: #{@project_dir}
36
+ E
37
+ end
38
+
39
+ def rsync
40
+ if OHAI.platform == "windows"
41
+ # Robocopy's return code is 1 if it succesfully copies over the
42
+ # files and 0 if the files are already existing at the destination
43
+ sync_cmd = "robocopy #{@source[:path]}\\ #{@project_dir}\\ /MIR /S"
44
+ shell = Mixlib::ShellOut.new(sync_cmd, :returns => [0, 1])
45
+ else
46
+ sync_cmd = "rsync --delete -a #{@source[:path]}/ #{@project_dir}/"
47
+ shell = Mixlib::ShellOut.new(sync_cmd)
48
+ end
49
+ shell.run_command
50
+ end
51
+
52
+ def clean
53
+ # Here, clean will do the same as fetch: reset source to pristine state
54
+ rsync
55
+ end
56
+
57
+ def fetch
58
+ rsync
59
+ end
60
+
61
+ def fetch_required?
62
+ true
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,42 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2012 Opscode, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'omnibus/fetcher'
19
+ require 'omnibus/s3_cacher'
20
+
21
+ module Omnibus
22
+ class S3CacheFetcher < NetFetcher
23
+ include SoftwareS3URLs
24
+
25
+ name :s3cache
26
+
27
+ def initialize(software)
28
+ @software = software
29
+ super
30
+ end
31
+
32
+ def fetch
33
+ log "S3 Cache enabled, #{name} will be fetched from S3 cache"
34
+ super
35
+ end
36
+
37
+ def source_uri
38
+ URI.parse(url_for(@software))
39
+ end
40
+ end
41
+ end
42
+
@@ -0,0 +1,260 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2012 Opscode, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ module Omnibus
19
+ class HealthCheck
20
+
21
+ WHITELIST_LIBS = [
22
+ /ld-linux/,
23
+ /libc\.so/,
24
+ /libcrypt\.so/,
25
+ /libdl/,
26
+ /libfreebl\d\.so/,
27
+ /libgcc_s\.so/,
28
+ /libm\.so/,
29
+ /libnsl\.so/,
30
+ /libpthread/,
31
+ /libresolv\.so/,
32
+ /librt\.so/,
33
+ /libstdc\+\+\.so/,
34
+ /libutil\.so/,
35
+ /linux-vdso.+/,
36
+ /linux-gate\.so/,
37
+ ]
38
+
39
+ SOLARIS_WHITELIST_LIBS = [
40
+ /libaio\.so/,
41
+ /libavl\.so/,
42
+ /libcrypt_[di]\.so/,
43
+ /libcrypto.so/,
44
+ /libcurses\.so/,
45
+ /libdoor\.so/,
46
+ /libgen\.so/,
47
+ /libmd5\.so/,
48
+ /libmd\.so/,
49
+ /libmp\.so/,
50
+ /libscf\.so/,
51
+ /libsec\.so/,
52
+ /libsocket\.so/,
53
+ /libssl.so/,
54
+ /libthread.so/,
55
+ /libuutil\.so/,
56
+ /libz.so/,
57
+ # solaris 11 libraries:
58
+ /libc\.so\.1/,
59
+ /libm\.so\.2/,
60
+ /libdl\.so\.1/,
61
+ /libnsl\.so\.1/,
62
+ /libpthread\.so\.1/,
63
+ /librt\.so\.1/,
64
+ /libcrypt\.so\.1/,
65
+ /libgdbm\.so\.3/,
66
+ # solaris 9 libraries:
67
+ /libm\.so\.1/,
68
+ /libc_psr\.so\.1/,
69
+ /s9_preload\.so\.1/,
70
+ ]
71
+
72
+ MAC_WHITELIST_LIBS = [
73
+ /libobjc\.A\.dylib/,
74
+ /libSystem\.B\.dylib/,
75
+ /CoreFoundation/,
76
+ /Tcl$/,
77
+ /Cocoa$/,
78
+ /Carbon$/,
79
+ /IOKit$/,
80
+ /Tk$/,
81
+ /libutil\.dylib/,
82
+ /libffi\.dylib/,
83
+ /libncurses\.5\.4\.dylib/,
84
+ /libiconv/
85
+ ]
86
+
87
+ WHITELIST_FILES = [
88
+ /jre\/bin\/javaws/,
89
+ /jre\/bin\/policytool/,
90
+ /jre\/lib/,
91
+ /jre\/plugin/,
92
+ ]
93
+
94
+ def self.log(msg)
95
+ puts "[health_check] #{msg}"
96
+ end
97
+
98
+ def self.run(install_dir)
99
+ if OHAI.platform == "mac_os_x"
100
+ bad_libs = health_check_otool(install_dir)
101
+ else
102
+ bad_libs = health_check_ldd(install_dir)
103
+ end
104
+
105
+ unresolved = []
106
+ unreliable = []
107
+ detail = []
108
+
109
+ if bad_libs.keys.length > 0
110
+ bad_libs.each do |name, lib_hash|
111
+ lib_hash.each do |lib, linked_libs|
112
+ linked_libs.each do |linked, count|
113
+ if linked =~ /not found/
114
+ unresolved << lib unless unresolved.include? lib
115
+ else
116
+ unreliable << linked unless unreliable.include? linked
117
+ end
118
+ detail << "#{name}|#{lib}|#{linked}|#{count}"
119
+ end
120
+ end
121
+ end
122
+ log "*** Health Check Failed, Summary follows:"
123
+ bad_omnibus_libs, bad_omnibus_bins = bad_libs.keys.partition { |k| k.include? "embedded/lib" }
124
+ log "*** The following Omnibus-built libraries have unsafe or unmet dependencies:"
125
+ bad_omnibus_libs.each { |lib| log " --> #{lib}" }
126
+ log "*** The following Omnibus-built binaries have unsafe or unmet dependencies:"
127
+ bad_omnibus_bins.each { |bin| log " --> #{bin}" }
128
+ if unresolved.length > 0
129
+ log "*** The following requirements could not be resolved:"
130
+ unresolved.each { |lib| log " --> #{lib}"}
131
+ end
132
+ if unreliable.length > 0
133
+ log "*** The following libraries cannot be guaranteed to be on target systems:"
134
+ unreliable.each { |lib| log " --> #{lib}"}
135
+ end
136
+ log "*** The precise failures were:"
137
+ detail.each do |line|
138
+ item, dependency, location, count = line.split('|')
139
+ reason = location =~ /not found/ ? "Unresolved dependency" : "Unsafe dependency"
140
+ log " --> #{item}"
141
+ log " DEPENDS ON: #{dependency}"
142
+ log " COUNT: #{count}"
143
+ log " PROVIDED BY: #{location}"
144
+ log " FAILED BECAUSE: #{reason}"
145
+ end
146
+ raise "Health Check Failed"
147
+ end
148
+ end
149
+
150
+ def self.health_check_otool(install_dir)
151
+ otool_cmd = "find #{install_dir}/ -type f | egrep '\.(dylib|bundle)$' | xargs otool -L > otool.out 2>/dev/null"
152
+ log "Executing `#{otool_cmd}`"
153
+ shell = Mixlib::ShellOut.new(otool_cmd, :timeout => 3600)
154
+ shell.run_command
155
+
156
+ otool_output = File.read('otool.out')
157
+
158
+ current_library = nil
159
+ bad_libs = {}
160
+
161
+ otool_output.each_line do |line|
162
+ case line
163
+ when /^(.+):$/
164
+ current_library = $1
165
+ when /^\s+(.+) \(.+\)$/
166
+ linked = $1
167
+ name = File.basename(linked)
168
+ bad_libs = check_for_bad_library(install_dir, bad_libs, current_library, name, linked)
169
+ end
170
+ end
171
+
172
+ File.delete('otool.out')
173
+
174
+ bad_libs
175
+ end
176
+
177
+ def self.check_for_bad_library(install_dir, bad_libs, current_library, name, linked)
178
+ safe = nil
179
+
180
+ whitelist_libs = case OHAI.platform
181
+ when 'mac_os_x'
182
+ MAC_WHITELIST_LIBS
183
+ when 'solaris2'
184
+ SOLARIS_WHITELIST_LIBS
185
+ else
186
+ WHITELIST_LIBS
187
+ end
188
+ whitelist_libs.each do |reg|
189
+ safe ||= true if reg.match(name)
190
+ end
191
+ WHITELIST_FILES.each do |reg|
192
+ safe ||= true if reg.match(current_library)
193
+ end
194
+
195
+ log " --> Dependency: #{name}" if ARGV[0] == "verbose"
196
+ log " --> Provided by: #{linked}" if ARGV[0] == "verbose"
197
+
198
+ if !safe && linked !~ Regexp.new(install_dir)
199
+ log " -> FAILED: #{current_library} has unsafe dependencies" if ARGV[0] == "verbose"
200
+ bad_libs[current_library] ||= {}
201
+ bad_libs[current_library][name] ||= {}
202
+ if bad_libs[current_library][name].has_key?(linked)
203
+ bad_libs[current_library][name][linked] += 1
204
+ else
205
+ bad_libs[current_library][name][linked] = 1
206
+ end
207
+ else
208
+ log " -> PASSED: #{name} is either whitelisted or safely provided." if ARGV[0] == "verbose"
209
+ end
210
+
211
+ bad_libs
212
+ end
213
+
214
+ def self.health_check_ldd(install_dir)
215
+ #
216
+ # ShellOut has GC turned off during execution, so when we're
217
+ # executing extremely long commands with lots of output, we
218
+ # should be mindful that the string concatentation for building
219
+ # #stdout will hurt memory usage drastically
220
+ #
221
+ ldd_cmd = "find #{install_dir}/ -type f | xargs ldd > ldd.out 2>/dev/null"
222
+ log "Executing `#{ldd_cmd}`"
223
+ shell = Mixlib::ShellOut.new(ldd_cmd, :timeout => 3600)
224
+ shell.run_command
225
+
226
+ ldd_output = File.read('ldd.out')
227
+
228
+ current_library = nil
229
+ bad_libs = {}
230
+
231
+ ldd_output.each_line do |line|
232
+ case line
233
+ when /^(.+):$/
234
+ current_library = $1
235
+ log "*** Analysing dependencies for #{current_library}" if ARGV[0] == "verbose"
236
+ when /^\s+(.+) \=\>\s+(.+)( \(.+\))?$/
237
+ name = $1
238
+ linked = $2
239
+ bad_libs = check_for_bad_library(install_dir, bad_libs, current_library, name, linked)
240
+ when /^\s+(.+) \(.+\)$/
241
+ next
242
+ when /^\s+statically linked$/
243
+ next
244
+ when /^\s+libjvm.so/
245
+ next
246
+ when /^\s+libjava.so/
247
+ next
248
+ when /^\s+libmawt.so/
249
+ next
250
+ when /^\s+not a dynamic executable$/ # ignore non-executable files
251
+ else
252
+ log "*** Line did not match for #{current_library}\n#{line}"
253
+ end
254
+ end
255
+
256
+ File.delete('ldd.out')
257
+ bad_libs
258
+ end
259
+ end
260
+ end
@@ -0,0 +1,70 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2012 Opscode, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ module Omnibus
19
+ #
20
+ # Used to generate the manifest of all software components with versions
21
+ class Library
22
+
23
+ def initialize
24
+ @projects = []
25
+ @components = []
26
+ end
27
+
28
+ def component_added(component)
29
+ @components << component
30
+ end
31
+
32
+ def version_map(project)
33
+ @components.select {|c| c.project == project}.inject({}) {|map, component|
34
+ map[component.name] = if component.given_version
35
+ {:version => component.version,
36
+ :given_version => component.given_version,
37
+ :overridden => component.overridden?,
38
+ :version_guid => component.version_guid}
39
+ else
40
+ ## Components without a version are
41
+ ## pieces of the omnibus project
42
+ ## itself, and so don't really fit
43
+ ## with the concept of overrides
44
+ v = {:version => project.build_version}
45
+ if project.build_version.respond_to?(:git_sha)
46
+ v[:version_guid] = "git:#{project.build_version.git_sha}"
47
+ end
48
+ v
49
+ end
50
+ map
51
+ }
52
+ end
53
+
54
+ def select(*args, &block)
55
+ @components.select(*args, &block)
56
+ end
57
+
58
+
59
+ end
60
+
61
+ def self.library
62
+ @library ||= Library.new
63
+ end
64
+
65
+ def self.component_added(*args)
66
+ library.component_added(*args)
67
+ end
68
+
69
+ end
70
+
@@ -0,0 +1,69 @@
1
+ require 'pp'
2
+
3
+ module Omnibus
4
+ module Overrides
5
+
6
+ DEFAULT_OVERRIDE_FILE_NAME = "omnibus.overrides"
7
+
8
+ # Parses a file of override information into a Hash.
9
+ #
10
+ # Each line of the file must be of the form
11
+ #
12
+ #
13
+ # <package_name> <version>
14
+ #
15
+ # where the two pieces of data are separated by whitespace.
16
+ #
17
+ # @param file [String] the path to an overrides file
18
+ # @return [Hash, nil]
19
+ def self.parse_file(file)
20
+ if file
21
+ File.readlines(file).inject({}) do |acc, line|
22
+ info = line.split
23
+
24
+ unless info.count == 2
25
+ raise ArgumentError, "Invalid overrides line: '#{line.chomp}'"
26
+ end
27
+
28
+ package, version = info
29
+
30
+ if acc[package]
31
+ raise ArgumentError, "Multiple overrides present for '#{package}' in overrides file #{file}!"
32
+ end
33
+
34
+ acc[package] = version
35
+ acc
36
+ end
37
+ else
38
+ nil
39
+ end
40
+ end
41
+
42
+ # Return the full path to an overrides file, or +nil+ if no such
43
+ # file exists.
44
+ def self.resolve_override_file
45
+ file = ENV['OMNIBUS_OVERRIDE_FILE'] || DEFAULT_OVERRIDE_FILE_NAME
46
+ path = File.expand_path(file)
47
+ File.exist?(path) ? path : nil
48
+ end
49
+
50
+ # Return a hash of override information. If no such information
51
+ # can be found, the hash will be empty
52
+ #
53
+ # @return [Hash]
54
+ def self.overrides
55
+ file = resolve_override_file
56
+ overrides = parse_file(file)
57
+
58
+ if overrides
59
+ puts "********************************************************************************"
60
+ puts "Using Overrides from #{Omnibus::Overrides.resolve_override_file}"
61
+ pp overrides
62
+ puts "********************************************************************************"
63
+ end
64
+
65
+ overrides || {}
66
+ end
67
+
68
+ end
69
+ end