Puppet: System Administration Automated

Support

Ticket #317: provider_pkgdmg.patch

File provider_pkgdmg.patch, 17.1 kB (added by mccune.jeff@gmail.com, 2 years ago)

Updated patch.

  • 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 
     17Puppet::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 
     99end 
  • 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. 
     9module 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 
     209end 
     210 
     211# we need to add this so sorting hash keys works properly 
     212class Symbol #:nodoc: 
     213  def <=> (other) 
     214    self.to_s <=> other.to_s 
     215  end 
     216end 
     217 
     218class Array #:nodoc: 
     219  include Plist::Emit 
     220end 
     221 
     222class Hash #:nodoc: 
     223  include Plist::Emit 
     224end 
  • 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 ) 
     14module 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 
     225end 
  • 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 
     13require 'base64' 
     14require 'cgi' 
     15require 'stringio' 
     16 
     17require 'puppet/util/plist/generator' 
     18require 'puppet/util/plist/parser' 
     19 
     20module Plist 
     21  VERSION = '3.0.0' 
     22end