omnibus 1.0.0

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.
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