Ticket #317: provider_pkgdmg.patch
| File provider_pkgdmg.patch, 17.1 kB (added by mccune.jeff@gmail.com, 2 years ago) |
|---|
-
lib/puppet/provider/package/pkgdmg.rb
old new 1 # Jeff McCune <mccune.jeff@gmail.com> 2 # Mac OS X Package Installer which handles .pkg and .mpkg 3 # bundles inside an Apple Disk Image. 4 # 5 # Motivation: DMG files provide a true HFS file system 6 # and are easier to manage and .pkg bundles. 7 # 8 # Note: the 'apple' Provider checks for the package name 9 # in /L/Receipts. Since we install multiple pkg's from a single 10 # source, we treat the source .pkg.dmg file as the package name. 11 # As a result, we store installed .pkg.dmg file names 12 # in /var/db/.puppet_pkgdmg_installed_<name> 13 14 # require 'ruby-debug' 15 # Debugger.start 16 17 Puppet::Type.type(:package).provide :pkgdmg do 18 desc "Package management based on Apple's Installer.app and DiskUtility.app" 19 20 confine :exists => "/Library/Receipts" 21 commands :installer => "/usr/sbin/installer" 22 commands :hdiutil => "/usr/bin/hdiutil" 23 24 # JJM We store a cookie for each installed .pkg.dmg in /var/db 25 def self.listbyname 26 Dir.entries("/var/db").find_all { |f| 27 f =~ /^\.puppet_pkgdmg_installed_/ 28 }.collect { |f| 29 name = f.sub(/^\.puppet_pkgdmg_installed_/, '') 30 yield name if block_given? 31 32 name 33 } 34 end 35 36 def self.list 37 listbyname.collect do |name| 38 Puppet.type(:package).installedpkg( 39 :name => name, 40 :provider => :pkgdmg, 41 :ensure => :installed 42 ) 43 end 44 end 45 46 def self.installpkg(source, name, orig_source) 47 installer "-pkg '#{source}' -target /" 48 File.open("/var/db/.puppet_pkgdmg_installed_#{name}", "w") do |t| 49 t.print "name: '#{name}'\n" 50 t.print "source: '#{orig_source}'\n" 51 end 52 end 53 54 def self.installpkgdmg(source, name) 55 unless source =~ /\.dmg$/i 56 self.fail "Mac OS X PKG DMG's must specificy a source string ending in .dmg" 57 end 58 require 'open-uri' 59 require 'puppet/util/plist' 60 open(source) do |dmg| 61 cmd = "/usr/bin/hdiutil mount -plist -nobrowse -readonly -mountrandom /tmp #{dmg.path}" 62 IO.popen(cmd) do |pipe| 63 xml_str = pipe.read 64 ptable = Plist::parse_xml xml_str 65 # JJM Filter out all mount-paths into a single array, discard the rest. 66 mounts = ptable['system-entities'].collect { |entity| 67 entity['mount-point'] 68 }.select { |mountloc|; mountloc } 69 mounts.each do |fspath| 70 Dir.entries(fspath).select { |f| 71 f =~ /\.m{0,1}pkg$/i 72 }.each { |pkg| 73 installpkg ("#{fspath}/#{pkg}", name, source) 74 } 75 end 76 hdiutil "eject '#{mounts[0]}'" 77 end 78 end 79 end 80 81 def query 82 if FileTest.exists?("/var/db/.puppet_pkgdmg_installed_#{@model[:name]}") 83 return {:name => @model[:name], :ensure => :present} 84 else 85 return nil 86 end 87 end 88 89 def install 90 source = nil 91 unless source = @model[:source] 92 self.fail "Mac OS X PKG DMG's must specify a package source." 93 end 94 unless name = @model[:name] 95 self.fail "Mac OS X PKG DMG's must specify a package name." 96 end 97 self.class.installpkgdmg(source,name) 98 end 99 end -
lib/puppet/util/plist/generator.rb
old new 1 #--########################################################### 2 # Copyright 2006, Ben Bleything <ben@bleything.net> and # 3 # Patrick May <patrick@hexane.org> # 4 # # 5 # Distributed under the MIT license. # 6 ############################################################## 7 #++ 8 # See Plist::Emit. 9 module Plist 10 # === Create a plist 11 # You can dump an object to a plist in one of two ways: 12 # 13 # * <tt>Plist::Emit.dump(obj)</tt> 14 # * <tt>obj.to_plist</tt> 15 # * This requires that you mixin the <tt>Plist::Emit</tt> module, which is already done for +Array+ and +Hash+. 16 # 17 # The following Ruby classes are converted into native plist types: 18 # Array, Bignum, Date, DateTime, Fixnum, Float, Hash, Integer, String, Symbol, Time, true, false 19 # * +Array+ and +Hash+ are both recursive; their elements will be converted into plist nodes inside the <array> and <dict> containers (respectively). 20 # * +IO+ (and its descendants) and +StringIO+ objects are read from and their contents placed in a <data> element. 21 # * User classes may implement +to_plist_node+ to dictate how they should be serialized; otherwise the object will be passed to <tt>Marshal.dump</tt> and the result placed in a <data> element. 22 # 23 # For detailed usage instructions, refer to USAGE[link:files/docs/USAGE.html] and the methods documented below. 24 module Emit 25 # Helper method for injecting into classes. Calls <tt>Plist::Emit.dump</tt> with +self+. 26 def to_plist(envelope = true) 27 return Plist::Emit.dump(self, envelope) 28 end 29 30 # Helper method for injecting into classes. Calls <tt>Plist::Emit.save_plist</tt> with +self+. 31 def save_plist(filename) 32 Plist::Emit.save_plist(self, filename) 33 end 34 35 # The following Ruby classes are converted into native plist types: 36 # Array, Bignum, Date, DateTime, Fixnum, Float, Hash, Integer, String, Symbol, Time 37 # 38 # Write us (via RubyForge) if you think another class can be coerced safely into one of the expected plist classes. 39 # 40 # +IO+ and +StringIO+ objects are encoded and placed in <data> elements; other objects are <tt>Marshal.dump</tt>'ed unless they implement +to_plist_node+. 41 # 42 # The +envelope+ parameters dictates whether or not the resultant plist fragment is wrapped in the normal XML/plist header and footer. Set it to false if you only want the fragment. 43 def self.dump(obj, envelope = true) 44 output = plist_node(obj) 45 46 output = wrap(output) if envelope 47 48 return output 49 end 50 51 # Writes the serialized object's plist to the specified filename. 52 def self.save_plist(obj, filename) 53 File.open(filename, 'wb') do |f| 54 f.write(obj.to_plist) 55 end 56 end 57 58 private 59 def self.plist_node(element) 60 output = '' 61 62 if element.respond_to? :to_plist_node 63 output << element.to_plist_node 64 else 65 case element 66 when Array 67 if element.empty? 68 output << "<array/>\n" 69 else 70 output << tag('array') { 71 element.collect {|e| plist_node(e)} 72 } 73 end 74 when Hash 75 if element.empty? 76 output << "<dict/>\n" 77 else 78 inner_tags = [] 79 80 element.keys.sort.each do |k| 81 v = element[k] 82 inner_tags << tag('key', CGI::escapeHTML(k.to_s)) 83 inner_tags << plist_node(v) 84 end 85 86 output << tag('dict') { 87 inner_tags 88 } 89 end 90 when true, false 91 output << "<#{element}/>\n" 92 when Time 93 output << tag('date', element.utc.strftime('%Y-%m-%dT%H:%M:%SZ')) 94 when Date # also catches DateTime 95 output << tag('date', element.strftime('%Y-%m-%dT%H:%M:%SZ')) 96 when String, Symbol, Fixnum, Bignum, Integer, Float 97 output << tag(element_type(element), CGI::escapeHTML(element.to_s)) 98 when IO, StringIO 99 element.rewind 100 contents = element.read 101 # note that apple plists are wrapped at a different length then 102 # what ruby's base64 wraps by default. 103 # I used #encode64 instead of #b64encode (which allows a length arg) 104 # because b64encode is b0rked and ignores the length arg. 105 data = "\n" 106 Base64::encode64(contents).gsub(/\s+/, '').scan(/.{1,68}/o) { data << $& << "\n" } 107 output << tag('data', data) 108 else 109 output << comment( 'The <data> element below contains a Ruby object which has been serialized with Marshal.dump.' ) 110 data = "\n" 111 Base64::encode64(Marshal.dump(element)).gsub(/\s+/, '').scan(/.{1,68}/o) { data << $& << "\n" } 112 output << tag('data', data ) 113 end 114 end 115 116 return output 117 end 118 119 def self.comment(content) 120 return "<!-- #{content} -->\n" 121 end 122 123 def self.tag(type, contents = '', &block) 124 out = nil 125 126 if block_given? 127 out = IndentedString.new 128 out << "<#{type}>" 129 out.raise_indent 130 131 out << block.call 132 133 out.lower_indent 134 out << "</#{type}>" 135 else 136 out = "<#{type}>#{contents.to_s}</#{type}>\n" 137 end 138 139 return out.to_s 140 end 141 142 def self.wrap(contents) 143 output = '' 144 145 output << '<?xml version="1.0" encoding="UTF-8"?>' + "\n" 146 output << '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">' + "\n" 147 output << '<plist version="1.0">' + "\n" 148 149 output << contents 150 151 output << '</plist>' + "\n" 152 153 return output 154 end 155 156 def self.element_type(item) 157 return case item 158 when String, Symbol: 'string' 159 when Fixnum, Bignum, Integer: 'integer' 160 when Float: 'real' 161 else 162 raise "Don't know about this data type... something must be wrong!" 163 end 164 end 165 private 166 class IndentedString #:nodoc: 167 attr_accessor :indent_string 168 169 @@indent_level = 0 170 171 def initialize(str = "\t") 172 @indent_string = str 173 @contents = '' 174 end 175 176 def to_s 177 return @contents 178 end 179 180 def raise_indent 181 @@indent_level += 1 182 end 183 184 def lower_indent 185 @@indent_level -= 1 if @@indent_level > 0 186 end 187 188 def <<(val) 189 if val.is_a? Array 190 val.each do |f| 191 self << f 192 end 193 else 194 # if it's already indented, don't bother indenting further 195 unless val =~ /\A#{@indent_string}/ 196 indent = @indent_string * @@indent_level 197 198 @contents << val.gsub(/^/, indent) 199 else 200 @contents << val 201 end 202 203 # it already has a newline, don't add another 204 @contents << "\n" unless val =~ /\n$/ 205 end 206 end 207 end 208 end 209 end 210 211 # we need to add this so sorting hash keys works properly 212 class Symbol #:nodoc: 213 def <=> (other) 214 self.to_s <=> other.to_s 215 end 216 end 217 218 class Array #:nodoc: 219 include Plist::Emit 220 end 221 222 class Hash #:nodoc: 223 include Plist::Emit 224 end -
lib/puppet/util/plist/parser.rb
old new 1 #--########################################################### 2 # Copyright 2006, Ben Bleything <ben@bleything.net> and # 3 # Patrick May <patrick@hexane.org> # 4 # # 5 # Distributed under the MIT license. # 6 ############################################################## 7 #++ 8 # Plist parses Mac OS X xml property list files into ruby data structures. 9 # 10 # === Load a plist file 11 # This is the main point of the library: 12 # 13 # r = Plist::parse_xml( filename_or_xml ) 14 module Plist 15 # Note that I don't use these two elements much: 16 # 17 # + Date elements are returned as DateTime objects. 18 # + Data elements are implemented as Tempfiles 19 # 20 # Plist::parse_xml will blow up if it encounters a data element. 21 # If you encounter such an error, or if you have a Date element which 22 # can't be parsed into a Time object, please send your plist file to 23 # plist@hexane.org so that I can implement the proper support. 24 def Plist::parse_xml( filename_or_xml ) 25 listener = Listener.new 26 #parser = REXML::Parsers::StreamParser.new(File.new(filename), listener) 27 parser = StreamParser.new(filename_or_xml, listener) 28 parser.parse 29 listener.result 30 end 31 32 class Listener 33 #include REXML::StreamListener 34 35 attr_accessor :result, :open 36 37 def initialize 38 @result = nil 39 @open = Array.new 40 end 41 42 43 def tag_start(name, attributes) 44 @open.push PTag::mappings[name].new 45 end 46 47 def text( contents ) 48 @open.last.text = contents if @open.last 49 end 50 51 def tag_end(name) 52 last = @open.pop 53 if @open.empty? 54 @result = last.to_ruby 55 else 56 @open.last.children.push last 57 end 58 end 59 end 60 61 class StreamParser 62 def initialize( filename_or_xml, listener ) 63 @filename_or_xml = filename_or_xml 64 @listener = listener 65 end 66 67 TEXT = /([^<]+)/ 68 XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>*/um 69 DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/um 70 COMMENT_START = /\A<!--/u 71 COMMENT_END = /.*?-->/um 72 73 74 def parse 75 plist_tags = PTag::mappings.keys.join('|') 76 start_tag = /<(#{plist_tags})([^>]*)>/i 77 end_tag = /<\/(#{plist_tags})[^>]*>/i 78 79 require 'strscan' 80 81 contents = ( 82 if (File.exists? @filename_or_xml) 83 File.open(@filename_or_xml) {|f| f.read} 84 else 85 @filename_or_xml 86 end 87 ) 88 89 @scanner = StringScanner.new( contents ) 90 until @scanner.eos? 91 if @scanner.scan(COMMENT_START) 92 @scanner.scan(COMMENT_END) 93 elsif @scanner.scan(XMLDECL_PATTERN) 94 elsif @scanner.scan(DOCTYPE_PATTERN) 95 elsif @scanner.scan(start_tag) 96 @listener.tag_start(@scanner[1], nil) 97 if (@scanner[2] =~ /\/$/) 98 @listener.tag_end(@scanner[1]) 99 end 100 elsif @scanner.scan(TEXT) 101 @listener.text(@scanner[1]) 102 elsif @scanner.scan(end_tag) 103 @listener.tag_end(@scanner[1]) 104 else 105 raise "Unimplemented element" 106 end 107 end 108 end 109 end 110 111 class PTag 112 @@mappings = { } 113 def PTag::mappings 114 @@mappings 115 end 116 117 def PTag::inherited( sub_class ) 118 key = sub_class.to_s.downcase 119 key.gsub!(/^plist::/, '' ) 120 key.gsub!(/^p/, '') unless key == "plist" 121 122 @@mappings[key] = sub_class 123 end 124 125 attr_accessor :text, :children 126 def initialize 127 @children = Array.new 128 end 129 130 def to_ruby 131 raise "Unimplemented: " + self.class.to_s + "#to_ruby on #{self.inspect}" 132 end 133 end 134 135 class PList < PTag 136 def to_ruby 137 children.first.to_ruby if children.first 138 end 139 end 140 141 class PDict < PTag 142 def to_ruby 143 dict = Hash.new 144 key = nil 145 146 children.each do |c| 147 if key.nil? 148 key = c.to_ruby 149 else 150 dict[key] = c.to_ruby 151 key = nil 152 end 153 end 154 155 dict 156 end 157 end 158 159 class PKey < PTag 160 def to_ruby 161 CGI::unescapeHTML(text || '') 162 end 163 end 164 165 class PString < PTag 166 def to_ruby 167 CGI::unescapeHTML(text || '') 168 end 169 end 170 171 class PArray < PTag 172 def to_ruby 173 children.collect do |c| 174 c.to_ruby 175 end 176 end 177 end 178 179 class PInteger < PTag 180 def to_ruby 181 text.to_i 182 end 183 end 184 185 class PTrue < PTag 186 def to_ruby 187 true 188 end 189 end 190 191 class PFalse < PTag 192 def to_ruby 193 false 194 end 195 end 196 197 class PReal < PTag 198 def to_ruby 199 text.to_f 200 end 201 end 202 203 require 'date' 204 class PDate < PTag 205 def to_ruby 206 DateTime.parse(text) 207 end 208 end 209 210 require 'base64' 211 class PData < PTag 212 def to_ruby 213 data = Base64.decode64(text.gsub(/\s+/, '')) 214 215 begin 216 return Marshal.load(data) 217 rescue Exception => e 218 io = StringIO.new 219 io.write data 220 io.rewind 221 return io 222 end 223 end 224 end 225 end -
lib/puppet/util/plist.rb
old new 1 #-- 2 ############################################################## 3 # Copyright 2006, Ben Bleything <ben@bleything.net> and # 4 # Patrick May <patrick@hexane.org> # 5 # # 6 # Distributed under the MIT license. # 7 ############################################################## 8 #++ 9 # = Plist 10 # 11 # This is the main file for plist. Everything interesting happens in Plist and Plist::Emit. 12 13 require 'base64' 14 require 'cgi' 15 require 'stringio' 16 17 require 'puppet/util/plist/generator' 18 require 'puppet/util/plist/parser' 19 20 module Plist 21 VERSION = '3.0.0' 22 end