solaris-patch 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright 2011 Martin Carpenter. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without modification, are
4
+ permitted provided that the following conditions are met:
5
+
6
+ 1. Redistributions of source code must retain the above copyright notice, this list of
7
+ conditions and the following disclaimer.
8
+
9
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
10
+ of conditions and the following disclaimer in the documentation and/or other materials
11
+ provided with the distribution.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY Martin Carpenter ``AS IS'' AND ANY EXPRESS OR IMPLIED
14
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
15
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Martin Carpenter OR
16
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
17
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
20
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
21
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
+
23
+ The views and conclusions contained in the software and documentation are those of the
24
+ authors and should not be interpreted as representing official policies, either expressed
25
+ or implied, of Martin Carpenter.
26
+
data/README.rdoc ADDED
@@ -0,0 +1,71 @@
1
+
2
+ = solaris-patch
3
+
4
+ Author:: Martin Carpenter
5
+ Email:: mcarpenter@free.fr
6
+ Copyright:: Copyright (c) Martin Carpenter 2011
7
+
8
+ == About
9
+ The solaris-patch gem helps with the manipulation of SunOS and Solaris
10
+ patches.
11
+
12
+ * Read (or write!) patchdiag.xref files.
13
+ * Find latest version of a patch.
14
+ * Find latest non-superseded version of a patch.
15
+ * Download patchdiag.xref, patches, readmes from Oracle.
16
+ * ...
17
+
18
+ == Examples
19
+
20
+ === Download a patch
21
+
22
+ require 'solaris/patch'
23
+ Solaris::Patch.download_patch!('123456-78',
24
+ :to_file => '/tmp/123456-78.zip')
25
+
26
+ Alternatively:
27
+
28
+ patch = Solaris::Patch.new( '123456-78' )
29
+ patch.download_patch!( :to_dir => '/tmp' )
30
+
31
+ === Get the latest version of a patch
32
+
33
+ require 'solaris/patch'
34
+ Solaris::Patchdiag.open( '/tmp/patchdiag.xref' ).latest( '123456-01' )
35
+ => "123456|12|..."
36
+
37
+ === Get the latest non-obsolete version of an possibly obsoleted patch
38
+
39
+ require 'solaris/patch'
40
+ Solaris::Patchdiag.open( '/tmp/patchdiag.xref' ).successor( '123456-01' )
41
+ => "234567|12|..."
42
+
43
+ === Interrogate patchdiag.xref
44
+
45
+ require 'solaris/patch'
46
+
47
+ # slurp in patchdiag.xref
48
+ patchdiag = Solaris::Patchdiag.new( '/tmp/patchdiag.xref' )
49
+
50
+ # all sparc patches
51
+ patchdiag.all.select { |p| p.archs.include? 'sparc' }.inspect
52
+ => [ "123456-78", ... ]
53
+
54
+ # latest line added to patchdiag.xref
55
+ most_recent = patchdiag.all( :sort_by => :date, :order => ascending ).max
56
+ most_recent
57
+ => "123456|78|..."
58
+
59
+ # most recent patch number
60
+ most_recent.patch
61
+ => "123456-78"
62
+
63
+ # most recent patch's README if it was recommended
64
+ most_recent.download_readme! if most_recent.recommended?
65
+ => "Patch-ID# 123456-78..."
66
+
67
+ == Known issues
68
+
69
+ Dates in patchdiag.xref are Mon/dd/yy format. This gem will break in
70
+ 2050 since it assumes year 50 and above is in the 20th Century.
71
+
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/gempackagetask'
4
+ require 'rake/rdoctask'
5
+ require 'rake/testtask'
6
+
7
+ desc 'Default task (package)'
8
+ task :default => [:package]
9
+
10
+ Rake::TestTask.new( 'test' )
11
+
12
+ SPECFILE = 'solaris-patch.gemspec'
13
+ if File.exist?( SPECFILE )
14
+ spec = eval( File.read( SPECFILE ) )
15
+ Rake::GemPackageTask.new( spec ).define
16
+ end
17
+
18
+ Rake::RDocTask.new do |rdoc|
19
+ rdoc.rdoc_dir = 'rdoc'
20
+ rdoc.title = 'solaris-patch'
21
+ rdoc.options << '--charset' << 'utf-8'
22
+ rdoc.options << '--all'
23
+ rdoc.rdoc_files.include( 'README.rdoc' )
24
+ rdoc.rdoc_files.include( FileList[ 'lib/**/*' ] )
25
+ rdoc.rdoc_files.include( FileList[ 'test/**/*' ] )
26
+ end
27
+
@@ -0,0 +1,31 @@
1
+
2
+ module Solaris
3
+
4
+ class Patch
5
+
6
+ # Superclass for all throwable exceptions.
7
+ class Exception < ::Exception ; end
8
+
9
+ # Exception is raised by #find_successor when the patch whose
10
+ # successor is to be sought or the supposed successor to a patch
11
+ # does not exist in patchdiag.xref.
12
+ class NotFound < Exception ; end
13
+
14
+ # Exception is raised when the terminal (non-obsolete) succesor
15
+ # to a patch has been withdrawn (is bad).
16
+ class BadSuccessor < Exception ; end
17
+
18
+ # Raised if the "obsoleted by" value for this patchdiag entry is not
19
+ # comprehensible.
20
+ class InvalidSuccessor < Exception ; end
21
+
22
+ # Raised if a patchdiag entry appears to have multiple successors.
23
+ class MultipleSuccessors < Exception ; end
24
+
25
+ # Raised if one tries to determine the successor of a non-obsolete patch.
26
+ class NotObsolete < Exception ; end
27
+
28
+ end
29
+
30
+ end # Solaris
31
+
data/lib/solaris/foo ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'pp'
4
+
5
+ def find(what=:all, opts={})
6
+ pp what
7
+ pp opts
8
+ puts
9
+ end
10
+
11
+ find
12
+ find(:something, :foo=>:bar)
13
+ find(:foo=>:bar)
@@ -0,0 +1,116 @@
1
+
2
+ module Solaris
3
+
4
+ # Class to represent a patch number.
5
+ class Patch
6
+
7
+ include Comparable
8
+
9
+ require 'solaris/util'
10
+ require 'solaris/exception'
11
+ require 'solaris/patchdiag'
12
+ require 'solaris/patchdiag_entry'
13
+
14
+ # Hash of URL patterns for downloads of type :patch or :readme.
15
+ # The string format parameter (%s) is the full patch number.
16
+ URL = {
17
+ :patch => 'https://getupdates.oracle.com/all_unsigned/%s.zip',
18
+ :readme => 'https://getupdates.oracle.com/readme/README.%s'
19
+ }
20
+
21
+ # The major number of the patch (integer), the part before the dash.
22
+ attr_accessor :major
23
+
24
+ # The minor number of the patch (integer), the part after the dash.
25
+ # Since this is an integer, values less than 10 will not be left
26
+ # padded with zero (see Patch::pad_minor). May be nil if not specified
27
+ # in the constructor.
28
+ attr_accessor :minor
29
+
30
+ # Create a patch. May take zero, one or two parameters.
31
+ #
32
+ # In the one parameter form, this is a string of the patch number
33
+ # eg '123456-78'. (If you specify integer 123456-78 then the
34
+ # resulting object will have major number 123378 and no minor
35
+ # number; this is probably not what you want).
36
+ #
37
+ # In the two parameter form, the first parameter is the major
38
+ # patch number (123456) and the second is the minor number (78).
39
+ # Although it is possible to provide strings or integers here strings
40
+ # should be prefered since a leading zero on an integer in Ruby
41
+ # indicates an octal representation (in the tradition of C). This can
42
+ # cause problems with a revision number of 09, since this is not a
43
+ # valid octal representation.
44
+ #
45
+ # TLDR: Patch.new('123456-78')
46
+ def initialize(major=nil, minor=nil)
47
+ if major
48
+ patch_no = major.to_s + ( minor ? "-#{minor}" : '' )
49
+ if patch_no =~ /^(\d+)(-(\d+))?$/
50
+ @major = $1.to_i
51
+ @minor = $3.to_i if $3
52
+ else
53
+ raise ArgumentError, "Invalid patch number string #{patch_no.inspect}"
54
+ end
55
+ end
56
+ end
57
+
58
+ # Download this patch. For the options hash see private method Patch#download!
59
+ def download_patch!(opts={})
60
+ download!( :patch, opts )
61
+ end
62
+
63
+ # Download this README. For the options hash see private method Patch#download!
64
+ def download_readme!(opts={})
65
+ download!( :readme, opts )
66
+ end
67
+
68
+ # Return a string representation of this patch. If the minor
69
+ # number has not been set then just return the major number.
70
+ def to_s
71
+ minor ? "#{@major}-#{Patch.pad_minor( @minor )}" : "#{@major}"
72
+ end
73
+
74
+ # Compare patch versions. (Performs a string compare on the full patch numbers).
75
+ def <=>(other)
76
+ self.to_s <=> other.to_s
77
+ end
78
+
79
+ # Download the given patch (this may be a Patch object or a string
80
+ # like '123456-78'). For the options hash see Patch#download!.
81
+ def Patch.download_patch!(patch, opts={})
82
+ patch_to_dl = Patch.new( patch.to_s )
83
+ patch_to_dl.download_patch!( opts )
84
+ end
85
+
86
+ # Download the given readme (this may be a Patch object or a string
87
+ # like '123456-78'). For the options hash see Patch#download!.
88
+ def Patch.download_readme!(patch, opts={})
89
+ patch_to_dl = Patch.new( patch.to_s )
90
+ patch_to_dl.download_readme!( opts )
91
+ end
92
+
93
+ # Left pad a minor version number with zeros as required.
94
+ def Patch.pad_minor(minor)
95
+ "#{minor.to_s.rjust( 2, '0' )}"
96
+ end
97
+
98
+ private
99
+
100
+ # Download this patch or readme from Oracle.
101
+ # For the options hash argument see Solaris::Util.download!
102
+ def download!(type, opts={})
103
+ raise ArgumentError, "Patch #{self.inspect} requires a major number to download" unless @major
104
+ raise ArgumentError, "Patch #{self.inspect} requires a minor number to download" unless @minor
105
+ raise ArgumentError, "Unknown type #{type.inspect}" unless URL[ type ]
106
+ url = URL[ type ] % self.to_s
107
+ opts = {
108
+ :agent => 'Wget/1.10.2', # default user agent, required by Oracle
109
+ }.merge( opts )
110
+ Solaris::Util.download!( url, opts )
111
+ end
112
+
113
+ end # Patch
114
+
115
+ end # Solaris
116
+
@@ -0,0 +1,135 @@
1
+
2
+ module Solaris
3
+
4
+ # Class to represent the Oracle patchdiag "database" (file).
5
+ # See the following Oracle support publication for format:
6
+ # https://support.oracle.com/CSP/main/article?cmd=show&type=NOT&doctype=REFERENCE&id=1019527.1
7
+ class Patchdiag
8
+
9
+ require 'date'
10
+
11
+ require 'solaris/exception'
12
+ require 'solaris/patchdiag_entry'
13
+ require 'solaris/util'
14
+
15
+ # Default patchdiag.xref file, as for Patch Check Advanced cache
16
+ DEFAULT_XREF_FILE = '/var/tmp/patchdiag.xref'
17
+
18
+ # URL of latest patchdiag.xref from Oracle.
19
+ DEFAULT_XREF_URL = 'https://getupdates.oracle.com/reports/patchdiag.xref'
20
+
21
+ # An array containing all entries (of class PatchdiagEntry) read
22
+ # from the patchdiag.xref file.
23
+ attr_accessor :entries
24
+
25
+ # Create a new patchdiag database object by reading the given
26
+ # xref file (this may be a filename (string) or a fileish object
27
+ # (File, StringIO)). If no xref file is given then the default
28
+ # is read (/var/tmp/patchdiag.xref); this is the cache file used
29
+ # by Patch Check Advanced (pca).
30
+ def initialize(xref_file=DEFAULT_XREF_FILE)
31
+ xref_file = File.new( xref_file ) if xref_file.is_a?(String)
32
+ @entries = xref_file.
33
+ readlines.
34
+ reject { |line| line =~ /^#|^\s*$/ }. # discard comments, blanks
35
+ map { |line| PatchdiagEntry.new( line ) }
36
+ end
37
+
38
+ # Download the patchdiag database and return it as a string.
39
+ # Note the contents will be returned as a string but not saved
40
+ # to disk unless :to_file or :to_dir are given.
41
+ # For the options hash argument see Solaris::Util.download!
42
+ def Patchdiag.download!(opts={})
43
+ url = opts.delete( :url ) || DEFAULT_XREF_URL
44
+ Util.download!( url, opts )
45
+ end
46
+
47
+ # Open the given optional patchdiag xref file and yield to the optional
48
+ # block.
49
+ def Patchdiag.open(xref_file=DEFAULT_XREF_FILE, &blk)
50
+ patchdiag = Patchdiag.new( xref_file )
51
+ if block_given?
52
+ yield patchdiag
53
+ else
54
+ patchdiag
55
+ end
56
+ end
57
+
58
+ # Returns an array of Solaris::PatchdiagEntry from the patchdiag with the given
59
+ # patch number (a String like xxxxxx-yy or xxxxxx or a Solaris::Patch). Returns an
60
+ # empty array if no such patches can be found.
61
+ def find(patch)
62
+ patch = Patch.new( patch.to_s )
63
+ property = patch.minor ? :to_s : :major
64
+ comparator = patch.send( property )
65
+ all.select { |pde| pde.patch.send( property ) == comparator }
66
+ end
67
+
68
+ # Return the Solaris::PatchdiagEntry of the latest version of the given patch
69
+ # (a String like xxxxxx-yy or xxxxxx or a Solaris::Patch).
70
+ # Throws Solaris::Patch::NotFound if the patch cannot be found in patchdiag.xref.
71
+ def latest(patch)
72
+ major = Patch.new( patch.to_s ).major
73
+ find( major ).max ||
74
+ raise( Solaris::Patch::NotFound, "Cannot find patch #{patch} in patchdiag.xref" )
75
+ end
76
+
77
+ # Return the Solaris::PatchdiagEntry of the latest non-obsolete successor
78
+ # of this patch. Throws Solaris::Patch::NotFound if the patch or any of its
79
+ # named successors cannot be found in patchdiag.xref.
80
+ def successor(patch)
81
+ patch = Patch.new( patch.to_s )
82
+ if ! patch.minor || ! entry = find( patch ).first
83
+ entry = latest( patch )
84
+ end
85
+ raise Solaris::Patch::NotFound,
86
+ "Cannot find patch #{patch} in patchdiag.xref" unless entry
87
+ if entry.obsolete?
88
+ succ = entry.successor # may raise
89
+ successor( succ )
90
+ elsif entry.bad?
91
+ raise BadSuccessor, "Terminal successor #{patch} is bad/withdrawn"
92
+ else
93
+ entry
94
+ end
95
+ end
96
+
97
+ # Return all patchdiag entries (PatchdiagEntry) defined in the patchdiag.xref.
98
+ # This method scans the entire patchdiag database so may be a little slow:
99
+ # callers are encouraged to cache or memoize their results.
100
+ def all(opts={})
101
+
102
+ # Defaults
103
+ order = :ascending
104
+ sort_by = :patch
105
+
106
+ # Parse options
107
+ opts.each do |key, value|
108
+ case key
109
+ when :sort_by
110
+ raise ArgumentError, "Invalid sort_by #{value.inspect}" unless [ :patch, :date ].include?( value )
111
+ sort_by = value
112
+ when :order
113
+ raise ArgumentError, "Invalid order #{value.inspect}" unless [ :ascending, :descending ].include?( value )
114
+ order = value
115
+ else
116
+ raise ArgumentError, "Unknown option key #{key.inspect}"
117
+ end
118
+ end
119
+
120
+ # Compute the lambda for sorting.
121
+ if order == :ascending
122
+ boat_op = lambda { |x,y| x.send( sort_by ) <=> y.send( sort_by ) }
123
+ else
124
+ boat_op = lambda { |x,y| y.send( sort_by ) <=> x.send( sort_by ) }
125
+ end
126
+
127
+ # Sort and return.
128
+ @entries.sort { |x,y| boat_op.call( x, y ) }
129
+
130
+ end
131
+
132
+ end # Patchdiag
133
+
134
+ end # Solaris
135
+
@@ -0,0 +1,182 @@
1
+
2
+ module Solaris
3
+
4
+ # Class to represent a line from Sun's patchdiag.xref patch "database".
5
+ # See the following Oracle support publication for format:
6
+ # https://support.oracle.com/CSP/main/article?cmd=show&type=NOT&doctype=REFERENCE&id=1019527.1
7
+ class PatchdiagEntry
8
+
9
+ include Comparable
10
+
11
+ require 'date'
12
+
13
+ require 'solaris/exception'
14
+ require 'solaris/patch'
15
+
16
+ # An array of architectures for this patch. Values are strings like
17
+ # "sparc", "i386".
18
+ attr_accessor :archs
19
+
20
+ # The bad field from the patchdiag xref database. Should be either 'B'
21
+ # or the empty string. See also PatchdiagEntry#bad?
22
+ attr_accessor :bad
23
+
24
+ # The date of this patch (a Date object).
25
+ attr_accessor :date
26
+
27
+ # The operating system for this patch, a string like "2.4, "10",
28
+ # "10_x86" or "Unbundled".
29
+ attr_accessor :os
30
+
31
+ # The patch object (class Patch) that this entry represents. See
32
+ # also convenience methods PatchdiagEntry#major and PatchdiagEntry#minor.
33
+ attr_accessor :patch
34
+
35
+ # An array of packages that pertain to this patch. Values are strings
36
+ # like "SUNWcsr".
37
+ attr_accessor :pkgs
38
+
39
+ # The recommended field from the patchdiag xref database. Should be either 'R'
40
+ # or the empty string. See also PatchdiagEntry#recommended?
41
+ attr_accessor :recommended
42
+
43
+ # The security field from the patchdiag xref database. Should be either 'S'
44
+ # or the empty string. See also PatchdiagEntry#security?
45
+ attr_accessor :security
46
+
47
+ # This synopsis of this patch from the patchdiag xref database. This is a free
48
+ # text field (string).
49
+ attr_accessor :synopsis
50
+
51
+ def initialize(patchdiag_line)
52
+ fields = patchdiag_line.split('|')[0..10]
53
+ major, minor, date, @recommended, @security, @obsolete, @bad, @os, archs, pkgs, @synopsis = *fields
54
+ @archs = archs.split( ';' )
55
+ if date == ''
56
+ year, month, day = 1970, 1, 1
57
+ else
58
+ month_s, day_s, year_s = *date.split( '/' )
59
+ year = ( year_s.to_i > 50 ? "19#{year_s}" : "20#{year_s}" ).to_i
60
+ month = Date::ABBR_MONTHNAMES.index( month_s )
61
+ day = day_s.to_i
62
+ end
63
+ @date = Date.new( year, month, day )
64
+ @patch = Patch.new( major, minor )
65
+ @pkgs = pkgs.split( ';' )
66
+ end
67
+
68
+ # Boolean, returns true if this patch is marked as "bad" in the patchdiag
69
+ # xref database.
70
+ def bad? ; @bad == 'B' end
71
+
72
+ # Download this patch. For options hash see Patch#download!.
73
+ def download_patch!(opts={}) ;
74
+ @patch.download_patch!( opts )
75
+ end
76
+
77
+ # Download the README for this patch. For options hash see Patch#download!.
78
+ def download_readme!(opts={})
79
+ @patch.download_readme!( opts )
80
+ end
81
+
82
+ # Returns this entries major patch number as an integer.
83
+ def major ; @patch.major end
84
+
85
+ # Returns this entries minor patch number as an integer.
86
+ def minor ; @patch.minor end
87
+
88
+ # Boolean, returns true if this patch is marked as "obsolete" in the patchdiag
89
+ # xref database.
90
+ def obsolete? ; @obsolete == 'O' end
91
+
92
+ # Boolean, returns true if this patch is marked as "recommended" in the patchdiag
93
+ # xref database.
94
+ def recommended? ; @recommended == 'R' end
95
+
96
+ # Boolean, returns true if this patch is marked as "security" in the patchdiag
97
+ # xref database.
98
+ def security? ; @security == 'S' end
99
+
100
+ # Return the Solaris::Patch by which this entry is obsoleted.
101
+ # Throws Solaris::Patch::NotObsolete if this entry is not obsolete.
102
+ # Throws Solaris::Patch::MultipleSuccessors if this entry has more than
103
+ # one successor.
104
+ # Throws Solaris::Patch::InvalidSuccessor if the "obsoleted by" entry cannot
105
+ # be understood.
106
+ def successor
107
+ # I <3 consistency:
108
+ # Obsoleted by : XXXXXX-XX
109
+ # Obsoleted by: XXXXXX-XX
110
+ # OBSOLETED by: XXXXXX
111
+ # Obsoleted by: XXXXXX-XX OBSOLETED by WITHDRAWN
112
+ # OBSOLETED by WITHDRAWN
113
+ # OBSOLETED by XXXXXX
114
+ # OBSOLETED by XXXXXX and XXXXXX # we ignore this pattern, see below
115
+ # Obsoleted by XXXXXX-XX:
116
+ # OBSOLETED by XXXXXX-XX:
117
+ # WITHDRAWN Obsoleted by: XXXXXX-XX
118
+ # WITHDRAWN PATCH Obsolete by:
119
+ # WITHDRAWN PATCH Obsoleted by:
120
+ # WITHDRAWN PATCH Obsoleted by XXXXXX-XX:
121
+
122
+ # Fail if this entry is not actually obsolete
123
+ raise Solaris::Patch::NotObsolete,
124
+ "Entry #{patch.inspect} not obsolete" unless obsolete?
125
+
126
+ # Fail on these two entries from 1999 since they are the only ones ever
127
+ # that are succeeded by two patches each
128
+ raise Solaris::Patch::MultipleSuccessors,
129
+ "More than one successor for entry #{patch.inspect}" if [ 105716, 105717 ].include?( self.major )
130
+
131
+ # See if we can find a successor
132
+ if synopsis =~ /obsolete(d?) by\s*(:?)\s*(\d+(-\d+)?)/i
133
+ Patch.new( $3 )
134
+ else
135
+ raise Solaris::Patch::InvalidSuccessor,
136
+ "Failed to parse successor to obsolete patchdiag entry for patch #{patch.inspect}"
137
+ end
138
+ end
139
+
140
+ # Output this patchdiag xref entry as a string, in format of Oracle's
141
+ # database.
142
+ def to_s
143
+ [ patch.major,
144
+ Patch.pad_minor( patch.minor ),
145
+ date_s,
146
+ @recommended,
147
+ @security,
148
+ @obsolete,
149
+ @bad,
150
+ @os,
151
+ join_semis( @archs ),
152
+ join_semis( @pkgs ),
153
+ @synopsis
154
+ ].join('|')
155
+ end
156
+
157
+ # Compare (by delegated comparison of patch versions, see Solaris::Patch#<=>).
158
+ def <=>(other)
159
+ self.patch <=> other.patch
160
+ end
161
+
162
+ private
163
+
164
+ # Convert the Date object to a date string as found in patchdiag.xref.
165
+ def date_s
166
+ [ Date::ABBR_MONTHNAMES[ @date.mon ], # month eg Jan
167
+ @date.mday.to_s.rjust(2, '0'), # day of month
168
+ @date.year % 100 # 2 digit year
169
+ ].join('/')
170
+ end
171
+
172
+ # Join an array of items with semicolons and append a trailing
173
+ # semicolon if the array is non-empty, otherwise return the
174
+ # empty string. Used to format @archs and @pkgs for #to_s.
175
+ def join_semis(a)
176
+ a.empty? ? '' : a.join(';') + ';'
177
+ end
178
+
179
+ end # PatchdiagEntry
180
+
181
+ end # Solaris
182
+
@@ -0,0 +1,68 @@
1
+
2
+ module Solaris
3
+
4
+ # Utility module.
5
+ module Util
6
+
7
+ require 'rubygems'
8
+ gem 'mechanize', '>= 1.0.0'
9
+ require 'mechanize'
10
+
11
+ # Download the given URL, return the document body. Options:
12
+ # :agent -- set HTTP user agent
13
+ # :password -- Oracle support password
14
+ # :to_file -- a file path to which the patch/readme should be saved
15
+ # :to_dir -- a directory path to which the patch/readme should be saved
16
+ # :user -- Oracle support username
17
+ # (:to_dir and :to_file are mutually exclusive)
18
+ def Util.download!(url, opts={})
19
+ agent = Mechanize.new
20
+ dirname, filename = nil, nil
21
+ opts.each do |key, value|
22
+ case key
23
+ when :agent
24
+ agent.user_agent = value
25
+ when :to_dir
26
+ dirname = value
27
+ when :to_file
28
+ filename = value
29
+ when :password, :user
30
+ # noop
31
+ else
32
+ raise ArgumentError, "Unknown option key #{key.inspect}"
33
+ end
34
+ end
35
+ # If we got a filename then open now before attempting download
36
+ raise ArgumentError, 'Cannot specify both :to_dir and :to_file' if filename && dirname
37
+ filename = File.join( dirname, File.basename( url ) ) if dirname
38
+ begin
39
+ file = File.open( filename, 'w' ) if filename
40
+ # Set agent authentication parameters
41
+ if opts[:user] && opts[:password]
42
+ agent.basic_auth( opts[:user], opts[:password] )
43
+ elsif opts[:user]
44
+ raise ArgumentError, 'Cannot authenticate without a password'
45
+ elsif opts[:password]
46
+ raise ArgumentError, 'Cannot authenticate without a username'
47
+ end
48
+ # Download file and save as required
49
+ page = agent.get( url )
50
+ if file
51
+ file.write( page.body )
52
+ file.close
53
+ end
54
+ rescue => exception
55
+ # Try to remove incomplete file on error
56
+ if file
57
+ begin file.close ; rescue ; end
58
+ begin File.unlink( file ) ; rescue ; end
59
+ end
60
+ raise exception # rethrow original exception
61
+ end
62
+ page.body # return file as string (even if written)
63
+ end
64
+
65
+ end # Util
66
+
67
+ end # Solaris
68
+
data/lib/solaris.rb ADDED
@@ -0,0 +1,10 @@
1
+
2
+ # Namespace for child classes.
3
+ module Solaris
4
+
5
+ require 'solaris/patch'
6
+ require 'solaris/patchdiag'
7
+ require 'solaris/patchdiag_entry'
8
+
9
+ end # Solaris
10
+
@@ -0,0 +1,100 @@
1
+
2
+ require 'test/unit'
3
+ require 'solaris/patch'
4
+
5
+ # Unit tests for class Patch.
6
+ class TestPatch < Test::Unit::TestCase #:nodoc:
7
+
8
+ def test_new_string_major_only
9
+ patch = Solaris::Patch.new( '123456' )
10
+ assert_equal( 123456, patch.major )
11
+ assert_nil( patch.minor )
12
+ end
13
+
14
+ def test_new_int_major_only
15
+ patch = Solaris::Patch.new( 123456 )
16
+ assert_equal( 123456, patch.major )
17
+ assert_nil( patch.minor )
18
+ end
19
+
20
+ def test_new_string_minor_only
21
+ assert_raise( ArgumentError ) do
22
+ Solaris::Patch.new( '-78' )
23
+ end
24
+ end
25
+
26
+ def test_new_string_major_and_minor
27
+ patch = Solaris::Patch.new( '123456-78' )
28
+ assert_equal( 123456, patch.major )
29
+ assert_equal( 78, patch.minor )
30
+ end
31
+
32
+ def test_new_int_major_and_minor
33
+ patch = Solaris::Patch.new( 123456, 78 )
34
+ assert_equal( 123456, patch.major )
35
+ assert_equal( 78, patch.minor )
36
+ end
37
+
38
+ def test_new_no_args
39
+ patch = Solaris::Patch.new()
40
+ assert_nil( patch.major )
41
+ assert_nil( patch.minor )
42
+ end
43
+
44
+ def test_new_valid_opts
45
+ patch = Solaris::Patch.new( '123456-78' )
46
+ assert_equal( 123456, patch.major )
47
+ assert_equal( 78, patch.minor )
48
+ end
49
+
50
+ def test_to_s
51
+ patch = Solaris::Patch.new( '123456-78' )
52
+ assert_equal( '123456-78', patch.to_s )
53
+ end
54
+
55
+ def test_to_s_padding
56
+ patch = Solaris::Patch.new( '123456-7' )
57
+ assert_equal( '123456-07', patch.to_s )
58
+ end
59
+
60
+ def test_to_s_major_only
61
+ patch = Solaris::Patch.new( 123456 )
62
+ assert_equal( '123456', patch.to_s )
63
+ end
64
+
65
+ def test_comparison
66
+ assert( Solaris::Patch.new( '123456-78' ) == Solaris::Patch.new( '123456-78' ) )
67
+ assert( Solaris::Patch.new( '123456-1' ) == Solaris::Patch.new( '123456-01' ) )
68
+ assert( Solaris::Patch.new( '123456' ) == Solaris::Patch.new( '123456' ) )
69
+ assert( Solaris::Patch.new( '123456-78' ) < Solaris::Patch.new( '123456-79' ) )
70
+ assert( Solaris::Patch.new( '123456-78' ) < Solaris::Patch.new( '123457-78' ) )
71
+ assert( Solaris::Patch.new( '123456-1' ) < Solaris::Patch.new( '123456-10' ) )
72
+ end
73
+
74
+ def test_download_requires_major
75
+ patch = Solaris::Patch.new
76
+ patch.minor = 1
77
+ assert_raise( ArgumentError ) { patch.download_patch! }
78
+ assert_raise( ArgumentError ) { patch.download_readme! }
79
+ end
80
+
81
+ def test_download_requires_minor
82
+ patch = Solaris::Patch.new
83
+ patch.major = 123456
84
+ assert_raise( ArgumentError ) { patch.download_patch! }
85
+ assert_raise( ArgumentError ) { patch.download_readme! }
86
+ end
87
+
88
+ def test_download!
89
+ skip 'Mock required'
90
+ end
91
+
92
+ def test_pad_minor
93
+ assert_equal( '00', Solaris::Patch.pad_minor( 0 ) )
94
+ assert_equal( '01', Solaris::Patch.pad_minor( 1 ) )
95
+ assert_equal( '12', Solaris::Patch.pad_minor( 12 ) )
96
+ assert_equal( '123', Solaris::Patch.pad_minor( 123 ) )
97
+ end
98
+
99
+ end
100
+
@@ -0,0 +1,198 @@
1
+
2
+ require 'test/unit'
3
+ require 'stringio'
4
+ require 'solaris/patchdiag'
5
+
6
+ # Unit tests for class Patchdiag.
7
+ class TestPatchdiag < Test::Unit::TestCase #:nodoc:
8
+
9
+ def setup
10
+ @empty_file = '/dev/null'
11
+ @patchdiag_fileish = StringIO.new(<<-EOF
12
+ ## PATCHDIAG TOOL CROSS-REFERENCE FILE AS OF Feb/10/11 ##
13
+ ##
14
+ ## The content of this file has changed as of Jun/04/10. Due to the merging of
15
+ ## the Sun Alert and Recommended patch clusters, some patches tagged with the
16
+ ## "R" flag may now be obsoleted patches (and as a result additionally have the
17
+ ## "O" flag set).
18
+ ## Details of this change are explained in the following blog posting:
19
+ ## http://blogs.sun.com/patch/entry/merging_the_solaris_recommended_and
20
+ ## The following Technical Info Document explains the various fields in the
21
+ ## patchdiag.xref file:
22
+ ## https://support.oracle.com/CSP/main/article?cmd=show&type=NOT&doctype=REFERENCE&id=1019527.1
23
+ 100287|05|Oct/31/91| | | | |Unbundled|||PC-NFS 3.5c: Jumbo patch (updated PRT.COM to v3.5c)
24
+ 100323|05|Feb/11/92| | | | |Unbundled|||PC-NFS Advanced Telnet: bug fixes, National Character Set support
25
+ 100386|01|Sep/20/91| | | | |Unbundled|||PC-NFS Programmer's Toolkit/2.0: Runtime modules
26
+ 100393|01|Sep/02/94| | |O| |Unbundled|||OBSOLETED by 100394
27
+ 100438|01|Dec/02/91| | | | |Unbundled|||PCNFS/v3.5c: PC-NFS loses mounted printer names when PCNFSLPD load
28
+ 100455|01|Dec/17/91| | | | |Unbundled|||LifeLine/2.0: Does not parse the FROM: and/or Reply-TO: fields cor
29
+ 100648|01|Jun/11/92| | | | |Unbundled|||PC-NFS/4.0a: Jumbo Patch
30
+ 100701|03|Feb/17/93| | | | |Unbundled|sparc;|SUNWowbcp:1.8.1;SUNWowbcp:2.10;|OpenWindows 3.0.1;3.1: programs compiled under Solaris 1.x /usr/5b
31
+ 100791|04|Apr/12/93| | |O| |Unbundled||SPROsw:2.1.0.1;|OBSOLETED by 100974
32
+ 100791|05|Apr/12/93| | |O| |Unbundled||SPROsw:2.1.0.1;|OBSOLETED by 100974
33
+ 100807|03|May/02/94| | |O| B|Unbundled|||OBSOLETED by WITHDRAWN
34
+ 100807|04|Jul/07/94| | | | |Unbundled|sparc;|SPROswmgr:2.1.2.1;|Sparcworks 2.0.1: statically linking X11 library doesn't run on As
35
+ 100811|01|Mar/17/93| | |O| |Unbundled||SPROcpl:2.1.0.3;SPROlang:2.1.0.3;|OBSOLETED by 100967
36
+ 100812|02|Jan/27/93| | | | |Unbundled|sparc;|SUNWowrqd:1.16.2;|OpenWindows 3.0.1: Binder as root with "-system" or "-network" doe
37
+ 100824|03|Jun/29/93| | | | |Unbundled|sparc;|SUNWowrqd:2.10;|OpenWindows 3.1: fixes various bugs in xnews server
38
+ 100830|01|Mar/12/93| | |O| |Unbundled||SPROlang:2.1.0.1;|OBSOLETED by 100861
39
+ 100837|01|Jan/20/93| | | | |Unbundled|sparc;|SUNWowrqd:2.10.0.1;|OpenWindows 3.1: core dump in ras3,ras4,ras5 and install_check on
40
+ 100843|01|Feb/05/93| | | | |Unbundled|||PC-NFS/4.0a: various, system or print server hangs under heavy loa
41
+ 100849|01|Jan/20/93| | |O| |Unbundled||SPROpas:2.1.0.1;|OBSOLETED by 100964
42
+ 100852|02|Jul/01/93| | |O| |2.1||SUNWcsu:11.4.2,PATCH=28;|OBSOLETED by WITHDRAWN
43
+ 146124|01|Dec/08/10| | | | |10|sparc;|SUNWthunderbirdl10n-de-DE:3.0.3,REV=101.0.3;SUNWthunderbirdl10n-es-ES:3.0.3,REV=101.0.3;SUNWthunderbirdl10n-extra:3.0.3,REV=101.0.3;SUNWthunderbirdl10n-fr-FR:3.0.3,REV=101.0.3;SUNWthunderbirdl10n-it-IT:3.0.3,REV=101.0.3;SUNWthunderbirdl10n-ja-JP:3.0.3,REV=101.0.3;SUNWthunderbirdl10n-ko-KR:3.0.3,REV=101.0.3;SUNWthunderbirdl10n-pl-PL:3.0.3,REV=101.0.3;SUNWthunderbirdl10n-pt-BR:3.0.3,REV=101.0.3;SUNWthunderbirdl10n-ru-RU:3.0.3,REV=101.0.3;SUNWthunderbirdl10n-sv-SE:3.0.3,REV=101.0.3;SUNWthunderbirdl10n-zh-CN:3.0.3,REV=101.0.3;SUNWthunderbirdl10n-zh-TW:3.0.3,REV=101.0.3;|SunOS 5.10: Thunderbird l10n packages update Patch
44
+ 146132|01|Jan/05/11| | | | |10|sparc;137137-09;141444-09;|SUNWcsu:11.10.0,REV=2005.01.21.15.53;|SunOS 5.10: newfs fails on newly-formatted floppy disk
45
+ 146133|01|Jan/05/11| | | | |10_x86|i386;137138-09;141445-09;|SUNWcsu:11.10.0,REV=2005.01.21.16.34;|SunOS 5.10_x86: newfs fails on newly-formatted floppy disk
46
+ 146236|01|Dec/10/10| | | | |10|sparc;137137-09;|SUNWckr:11.10.0,REV=2005.01.21.15.53;|SunOS 5.10: dcfs patch
47
+ 146237|01|Dec/10/10| | | | |10_x86|i386;141445-09;|SUNWckr:11.10.0,REV=2005.01.21.16.34;|SunOS 5.10_x86: dcfs patch
48
+ 146241|02|Dec/16/10| | | | |Unbundled|sparc;|SUNWscsaa:3.3.0,REV=2010.07.26.12.56;|Oracle Solaris Cluster 3.3: SWIFTAllianceAccess patch
49
+ 146277|01|Dec/10/10| | | | |10|sparc;118833-36;120011-14;127127-11;139555-08;142909-17;|SUNWcslr:11.10.0,REV=2005.01.21.15.53;|SunOS 5.10: libnsl patch
50
+ 146278|01|Dec/10/10| | | | |10_x86|i386;118855-36;120012-14;127128-11;139556-08;142910-17;|SUNWcslr:11.10.0,REV=2005.01.21.16.34;|SunOS 5.10_x86: libnsl patch
51
+ 146279|01|Dec/22/10|R|S| | |10|sparc;|SUNWbnuu:11.10.0,REV=2005.01.21.15.53;|SunOS 5.10: uucp patch
52
+ 146280|01|Dec/22/10|R|S| | |10_x86|i386;|SUNWbnuu:11.10.0,REV=2005.01.21.16.34;|SunOS 5.10_x86: uucp patch
53
+ 146281|01|Dec/15/10| | | | |10|sparc;|SUNWckr:11.10.0,REV=2005.01.21.15.53;SUNWhea:11.10.0,REV=2005.01.21.15.53;|SunOS 5.10: ldterm patch
54
+ 146282|01|Dec/15/10| | | | |10_x86|i386;|SUNWckr:11.10.0,REV=2005.01.21.16.34;SUNWhea:11.10.0,REV=2005.01.21.16.34;|SunOS 5.10_x86: ldterm patch
55
+ 146283|01|Dec/10/10| | | | |10|sparc;118833-36;137137-09;142909-17;|SUNWckr:11.10.0,REV=2005.01.21.15.53;|SunOS 5.10: lofi patch
56
+ 146284|01|Dec/10/10| | | | |10_x86|i386;118855-36;137138-09;142910-17;|SUNWckr:11.10.0,REV=2005.01.21.16.34;|SunOS 5.10_x86: lofi patch
57
+ 146287|01|Jan/24/11| | | | |10|sparc.sun4u;sparc.sun4us;sparc.sun4v;118833-36;120011-14;|SUNWkvm:11.10.0,REV=2005.01.20.17.25;SUNWkvm:11.10.0,REV=2005.01.21.15.53;SUNWkvm:11.10.0,REV=2005.08.04.12.25;|SunOS 5.10: trapstat Patch
58
+ 146332|02|Feb/03/11| | | | |10|sparc;142909-17;|SUNWaccu:11.10.0,REV=2005.01.21.15.53;|SunOS 5.10: sadc patch
59
+ 146333|02|Feb/03/11| | | | |10_x86|i386;142910-17;|SUNWaccu:11.10.0,REV=2005.01.21.16.34;|SunOS 5.10_x86: sadc patch
60
+ 146334|01|Jan/19/11| | | | |10|sparc;120011-14;121133-02;127127-11;139555-08;142909-17;|SUNWzoneu:11.10.0,REV=2005.01.21.15.53;|SunOS 5.10: zlogin patch
61
+ 146335|01|Jan/19/11| | | | |10_x86|i386;120012-14;121334-04;127128-11;139556-08;142910-17;|SUNWzoneu:11.10.0,REV=2005.01.21.16.34;|SunOS 5.10_x86: zlogin patch
62
+ 146336|01|Jan/13/11| | | | |10|sparc;118833-36;120011-14;127127-11;137137-09;142909-17;|SUNWarc:11.10.0,REV=2005.01.21.15.53;SUNWcsl:11.10.0,REV=2005.01.21.15.53;|SunOS 5.10: libsldap.so.1 patch
63
+ 146337|01|Jan/13/11| | | | |10_x86|i386;120012-14;127128-11;137138-09;142910-17;|SUNWarc:11.10.0,REV=2005.01.21.16.34;SUNWcsl:11.10.0,REV=2005.01.21.16.34;|SunOS 5.10_x86: libsldap.so.1 patch
64
+ 146363|01|Jan/04/11|R|S| | |10|sparc;119757-19;|SUNWsfman:11.10.0,REV=2005.01.08.05.16;SUNWsmbaS:11.10.0,REV=2005.01.08.05.16;SUNWsmbar:11.10.0,REV=2005.01.08.05.16;SUNWsmbau:11.10.0,REV=2005.01.08.05.16;|SunOS 5.10: Samba patch
65
+ 146364|01|Jan/04/11|R|S| | |10_x86|i386;119758-19;|SUNWsfman:11.10.0,REV=2005.01.08.01.09;SUNWsmbaS:11.10.0,REV=2005.01.08.01.09;SUNWsmbar:11.10.0,REV=2005.01.08.01.09;SUNWsmbau:11.10.0,REV=2005.01.08.01.09;|SunOS 5.10_x86: Samba patch
66
+ 146435|01|Feb/01/11| | | | |Unbundled|||Hardware FC Disk Drive Patch : Download program and FC Disk Drive
67
+ 146440|01|Jan/13/11| | | | |10|sparc;118833-36;120011-14;137137-09;139555-08;141444-09;142909-17;|SUNWckr:11.10.0,REV=2005.01.21.15.53;|SunOS 5.10: dld patch
68
+ 146441|01|Jan/13/11| | | | |10_x86|i386;118855-36;120012-14;139556-08;141445-09;142910-17;|SUNWckr:11.10.0,REV=2005.01.21.16.34;|SunOS 5.10_x86: dld patch
69
+ 146442|01|Jan/24/11| | | | |10|sparc;126897-02;127127-11;127755-01;137137-09;139555-08;141444-09;142909-17;|SUNWfmd:11.10.0,REV=2005.01.21.15.53;|SunOS 5.10: libldom.so.1 patch
70
+ 146443|01|Feb/09/11| | | | |10|sparc;142909-17;|SUNWfmd:11.10.0,REV=2005.01.21.15.53;|SunOS 5.10: gmem.eft and generic-mem.so patch
71
+ 146444|01|Jan/27/11| | | | |10_x86|i386;120012-14;142910-17;|SUNWnge:11.10.0,REV=2005.06.22.03.40;|SunOS 5.10_x86: nge patch
72
+ 800054|01|Mar/16/01| | |O| |Unbundled|||Obsoleted by: 111346-01 Hardware/PROM: Sun Fire 3800/4800/4810/680
73
+ 100974|02|Mar/14/95| | | | |Unbundled|sparc;|SPROsw:2.1.0;|SparcWorks 2.0.1: dbx jumbo patch
74
+ EOF
75
+ )
76
+ @patchdiag = Solaris::Patchdiag.open( @patchdiag_fileish )
77
+ end
78
+
79
+ def teardown
80
+ @empty_file = nil
81
+ @patchdiag = nil
82
+ @patchdiag_fileish = nil
83
+ end
84
+
85
+ def test_new_by_fileish
86
+ assert_equal( 51, @patchdiag.entries.size )
87
+ end
88
+
89
+ def test_new_by_filename
90
+ temp = Tempfile.new( 'test_patchdiag' )
91
+ path = temp.path
92
+ temp.write( @patchdiag_fileish )
93
+ temp.close
94
+ patchdiag = Solaris::Patchdiag.new( path )
95
+ File.unlink( path )
96
+ assert_equal( 51, @patchdiag.entries.size )
97
+ end
98
+
99
+ def test_new_empty_file
100
+ if File.exists?( @empty_file )
101
+ patchdiag = Solaris::Patchdiag.new( @empty_file )
102
+ assert_equal( 0, patchdiag.entries.size )
103
+ end
104
+ end
105
+
106
+ def test_all
107
+ assert_equal( 51, @patchdiag.all.size )
108
+ end
109
+
110
+ def test_all_sort_by_date_order_ascending
111
+ all = @patchdiag.all( :sort_by => :date, :order => :ascending )
112
+ first = all.first.patch # oldest
113
+ last = all.last.patch # newest
114
+ assert_equal( Solaris::Patch.new( '100386-01' ), first )
115
+ assert_equal( Solaris::Patch.new( '146443-01' ), last )
116
+ end
117
+
118
+ def test_all_sort_by_patch_order_descending
119
+ all = @patchdiag.all( :sort_by => :patch, :order => :descending )
120
+ first = all.first.patch # largest
121
+ last = all.last.patch # smallest
122
+ assert_equal( Solaris::Patch.new( '800054-01' ), first )
123
+ assert_equal( Solaris::Patch.new( '100287-05' ), last )
124
+ end
125
+
126
+ def test_open_block
127
+ ret = Solaris::Patchdiag.open( @patchdiag_fileish ) do |patchdiag|
128
+ assert_equal( Solaris::Patchdiag, patchdiag.class )
129
+ :return_code
130
+ end
131
+ assert_equal( :return_code, ret )
132
+ end
133
+
134
+ def test_open_return
135
+ assert_equal( Solaris::Patchdiag, @patchdiag.class )
136
+ end
137
+
138
+ def test_find
139
+ assert_equal( [], @patchdiag.find( 123456 ) )
140
+ assert_equal( [], @patchdiag.find( '123456' ) )
141
+ assert_equal( [], @patchdiag.find( '123456-78' ) )
142
+ assert_equal( [], @patchdiag.find( Solaris::Patch.new( 123456 ) ) )
143
+ assert_equal( [], @patchdiag.find( Solaris::Patch.new( '123456' ) ) )
144
+ assert_equal( [], @patchdiag.find( Solaris::Patch.new( '123456-78' ) ) )
145
+ assert_equal( [], @patchdiag.find( '100791-01' ) )
146
+ assert_equal( [], @patchdiag.find( Solaris::Patch.new( '100791-01' ) ) )
147
+ assert_equal( '100791-04', @patchdiag.find( 100791 ).first.patch.to_s )
148
+ assert_equal( '100791-04', @patchdiag.find( '100791' ).first.patch.to_s )
149
+ assert_equal( '100791-04', @patchdiag.find( '100791-04' ).first.patch.to_s )
150
+ assert_equal( '100791-04', @patchdiag.find( Solaris::Patch.new( 100791) ).first.patch.to_s )
151
+ assert_equal( '100791-04', @patchdiag.find( Solaris::Patch.new( '100791') ).first.patch.to_s )
152
+ assert_equal( '100791-04', @patchdiag.find( Solaris::Patch.new( '100791-04') ).first.patch.to_s )
153
+ end
154
+
155
+ def test_latest
156
+ assert_raise( Solaris::Patch::NotFound ) do
157
+ @patchdiag.latest( 123456 )
158
+ end
159
+ assert_equal( '100791-05', @patchdiag.latest( 100791 ).patch.to_s )
160
+ assert_equal( '100791-05', @patchdiag.latest( '100791' ).patch.to_s )
161
+ assert_equal( '100791-05', @patchdiag.latest( '100791-01' ).patch.to_s )
162
+ assert_equal( '100791-05', @patchdiag.latest( '100791-05' ).patch.to_s )
163
+ assert_equal( '100791-05', @patchdiag.latest( Solaris::Patch.new( 100791 ) ).patch.to_s )
164
+ assert_equal( '100791-05', @patchdiag.latest( Solaris::Patch.new( '100791' ) ).patch.to_s )
165
+ assert_equal( '100791-05', @patchdiag.latest( Solaris::Patch.new( '100791-01' ) ).patch.to_s )
166
+ assert_equal( '100791-05', @patchdiag.latest( Solaris::Patch.new( '100791-05' ) ).patch.to_s )
167
+ end
168
+
169
+ def test_successor
170
+ assert_raise( Solaris::Patch::NotFound ) do
171
+ @patchdiag.latest( 123456 )
172
+ end
173
+ assert_equal( '100287-05', @patchdiag.successor( 100287 ).patch.to_s )
174
+ assert_equal( '100287-05', @patchdiag.successor( '100287' ).patch.to_s )
175
+ assert_equal( '100287-05', @patchdiag.successor( '100287-05' ).patch.to_s )
176
+ assert_equal( '100287-05', @patchdiag.successor( Solaris::Patch.new( '100287-05' ) ).patch.to_s )
177
+ assert_equal( '100974-02', @patchdiag.successor( 100791 ).patch.to_s )
178
+ assert_raise( Solaris::Patch::NotFound ) do
179
+ @patchdiag.successor( 100393 ) # successor 100394 not in patchdiag.xref
180
+ end
181
+ assert_equal( '100807-04', @patchdiag.successor( '100807-01' ).patch.to_s )
182
+ assert_raise( Solaris::Patch::InvalidSuccessor ) do
183
+ @patchdiag.successor( '100807-03' ) # successor 100807-03 WITHDRAWN
184
+ end
185
+ end
186
+
187
+ def test_download!
188
+ skip 'Mock required'
189
+ end
190
+
191
+ private
192
+
193
+ def successor(patch)
194
+ @patchdiag.successor( patch ).patch.to_s
195
+ end
196
+
197
+ end
198
+
@@ -0,0 +1,115 @@
1
+
2
+ require 'test/unit'
3
+ require 'solaris/patchdiag_entry'
4
+
5
+ # Unit tests for class PatchdiagEntry.
6
+ class TestPatchdiagEntry < Test::Unit::TestCase #:nodoc:
7
+
8
+ def test_146636_01
9
+ line = '146336|01|Jan/13/11| | | | |10|sparc;118833-36;120011-14;127127-11;137137-09;142909-17;|SUNWarc:11.10.0,REV=2005.01.21.15.53;SUNWcsl:11.10.0,REV=2005.01.21.15.53;|SunOS 5.10: libsldap.so.1 patch'
10
+ pde = Solaris::PatchdiagEntry.new( line )
11
+ assert_equal( 146336, pde.major )
12
+ assert_equal( 1, pde.minor )
13
+ assert_equal( false, pde.recommended? )
14
+ assert_equal( false, pde.obsolete? )
15
+ assert_equal( false, pde.security? )
16
+ assert_equal( Date.new(2011, 1, 13), pde.date )
17
+ assert_equal( '10', pde.os )
18
+ assert_equal( '146336-01', pde.patch.to_s )
19
+ assert_equal( ['SUNWarc:11.10.0,REV=2005.01.21.15.53',
20
+ 'SUNWcsl:11.10.0,REV=2005.01.21.15.53'], pde.pkgs )
21
+ assert_equal( [ 'sparc', '118833-36', '120011-14', '127127-11', '137137-09', '142909-17' ], pde.archs )
22
+ assert_equal( 'SunOS 5.10: libsldap.so.1 patch', pde.synopsis )
23
+ assert_equal( line, pde.to_s )
24
+ end
25
+
26
+ def test_100393_01
27
+ line = '100393|01|Sep/02/94| | |O| |Unbundled|||OBSOLETED by 100394'
28
+ pde = Solaris::PatchdiagEntry.new( line )
29
+ assert_equal( 100393, pde.major )
30
+ assert_equal( 1, pde.minor )
31
+ assert_equal( false, pde.recommended? )
32
+ assert_equal( true, pde.obsolete? )
33
+ assert_equal( false, pde.security? )
34
+ assert_equal( Date.new(1994, 9, 2), pde.date )
35
+ assert_equal( 'Unbundled', pde.os )
36
+ assert_equal( '100393-01', pde.patch.to_s )
37
+ assert_equal( [], pde.pkgs )
38
+ assert_equal( [], pde.archs )
39
+ assert_equal( 'OBSOLETED by 100394', pde.synopsis )
40
+ assert_equal( line, pde.to_s )
41
+ end
42
+
43
+ def test_146364_01
44
+ line = '146364|01|Jan/04/11|R|S| | |10_x86|i386;119758-19;|SUNWsfman:11.10.0,REV=2005.01.08.01.09;SUNWsmbaS:11.10.0,REV=2005.01.08.01.09;SUNWsmbar:11.10.0,REV=2005.01.08.01.09;SUNWsmbau:11.10.0,REV=2005.01.08.01.09;|SunOS 5.10_x86: Samba patch'
45
+ pde = Solaris::PatchdiagEntry.new( line )
46
+ assert_equal( 146364, pde.major )
47
+ assert_equal( 1, pde.minor )
48
+ assert_equal( true, pde.recommended? )
49
+ assert_equal( false, pde.obsolete? )
50
+ assert_equal( true, pde.security? )
51
+ assert_equal( Date.new(2011, 1, 4), pde.date )
52
+ assert_equal( '10_x86', pde.os )
53
+ assert_equal( '146364-01', pde.patch.to_s )
54
+ assert_equal( ['SUNWsfman:11.10.0,REV=2005.01.08.01.09',
55
+ 'SUNWsmbaS:11.10.0,REV=2005.01.08.01.09',
56
+ 'SUNWsmbar:11.10.0,REV=2005.01.08.01.09',
57
+ 'SUNWsmbau:11.10.0,REV=2005.01.08.01.09'],
58
+ pde.pkgs )
59
+ assert_equal( ['i386', '119758-19'], pde.archs )
60
+ assert_equal( 'SunOS 5.10_x86: Samba patch', pde.synopsis )
61
+ assert_equal( line, pde.to_s )
62
+ end
63
+
64
+ def test_compare_equal
65
+ line = '100393|01|Sep/02/94| | |O| |Unbundled|||OBSOLETED by 100394'
66
+ assert_equal(Solaris::PatchdiagEntry.new( line ),
67
+ Solaris::PatchdiagEntry.new( line ))
68
+ end
69
+
70
+ def test_compare_less_than
71
+ line1 = '100393|01|Sep/02/94| | |O| |Unbundled|||OBSOLETED by 100394'
72
+ line2 = '146364|01|Jan/04/11|R|S| | |10_x86|i386;119758-19;|SUNWsfman:11.10.0,REV=2005.01.08.01.09;SUNWsmbaS:11.10.0,REV=2005.01.08.01.09;SUNWsmbar:11.10.0,REV=2005.01.08.01.09;SUNWsmbau:11.10.0,REV=2005.01.08.01.09;|SunOS 5.10_x86: Samba patch'
73
+ assert( Solaris::PatchdiagEntry.new( line1 ) < Solaris::PatchdiagEntry.new( line2 ) )
74
+ end
75
+
76
+ def test_successor
77
+ assert_raise( Solaris::Patch::NotObsolete ) do
78
+ successor( '100287|05|Oct/31/91| | | | |Unbundled|||PC-NFS 3.5c: Jumbo patch (updated PRT.COM to v3.5c)' )
79
+ end
80
+ assert_equal( '110258-01', successor( '109687|01|Aug/11/00| | |O| |Unbundled|sparc;|VRTSvxvm:3.0.4,REV=04.18.2000.10.00;|Obsoleted by : 110258-01 VxVM 3.0.4: vxio and vxdmp driver patch' ) )
81
+ assert_equal( '101318-94', successor( '101859|01|Feb/06/01| | |O| |2.3|sparc;|SUNWcsr:11.5.0,REV=2.0.19,PATCH=118;|Obsoleted by: 101318-94 SunOS 5.3: socket lib in 2.3/2.2 have prob' ) )
82
+ assert_equal( '106542', successor( '107440|01|Mar/26/99| | |O| |7_x86||SUNWcar:11.7.0,REV=1998.09.01.04.53;|OBSOLETED by: 106542 SunOS 5.7_x86: /platform/i86pc/kernel/mmu/mm' ) )
83
+ assert_equal( '109212-02', successor( '108191|01|Sep/07/99| | |O| B|Unbundled|||Obsoleted by: 109212-02 OBSOLETED by WITHDRAWN' ) )
84
+ assert_raise( Solaris::Patch::InvalidSuccessor ) do
85
+ successor( '100807|03|May/02/94| | |O| B|Unbundled|||OBSOLETED by WITHDRAWN' )
86
+ end
87
+ assert_equal( '100394', successor( '100393|01|Sep/02/94| | |O| |Unbundled|||OBSOLETED by 100394' ) )
88
+ assert_raise( Solaris::Patch::MultipleSuccessors ) do
89
+ successor( '105716|07|Jun/30/99| |S|O| |Unbundled||SUNWdtbas:1.0.2,REV=10.96.04.12;|OBSOLETED by 108363 and 108289' )
90
+ end
91
+ assert_raise( Solaris::Patch::MultipleSuccessors ) do
92
+ successor( '105717|06|Jun/30/99| |S|O| |Unbundled||SUNWdtbas:1.0.2,REV=10.96.04.12;|OBSOLETED by 108290 and 108364' )
93
+ end
94
+ assert_equal( '110256-01', successor( '109685|02|Feb/09/01| | |O| |Unbundled|sparc;|VRTSvxvm:3.0.4,REV=04.18.2000.10.00;|Obsoleted by 110256-01: VxVM 3.0.4: vxio and vxdmp driver patch' ) )
95
+ assert_equal( '110257-01', successor( '109686|02|Feb/09/01| | |O| |Unbundled|sparc;|VRTSvxvm:3.0.4,REV=04.18.2000.10.00;|OBSOLETED by 110257-01: VxVM 3.0.4: vxio and vxdmp driver patch' ) )
96
+ assert_equal( '106513-07', successor( '106513|06|Oct/03/00| | |O| B|Unbundled|sparc;|SUNWosafw:6.01,REV=01.11;SUNWosau:6.01,REV=01.11;108555-02;|WITHDRAWN Obsoleted by: 106513-07 RM 6.1.1: generic Raid Manager 6' ) )
97
+ assert_raise( Solaris::Patch::InvalidSuccessor ) do
98
+ # NB fudged the obsolete flag here
99
+ successor( '111442|01|May/04/01| | |O| B|2.5.1|sparc;sparc.sun4c;sparc.sun4d;sparc.sun4m;sparc.sun4u;sparc.sun4u1;103640-35;|SUNWcar:11.5.1,REV=96.05.02.21.09;SUNWcar:11.5.1,REV=97.02.21.21.14;SUNWcsr:11.5.1,REV=96.05.02.21.09;103640-36;|WITHDRAWN PATCH Obsolete by: <INTEGRATION> SunOS 5.5.1: Supplement' )
100
+ end
101
+ assert_equal( '103901-08', successor( '103901|07|Aug/05/97| |S|O| B|2.5.1|||WITHDRAWN PATCH Obsoleted by: 103901-08 OpenWindows 3.5.1: Xview P' ) )
102
+ assert_raise( Solaris::Patch::InvalidSuccessor ) do
103
+ # NB fudged the obsolete flag here
104
+ successor( '111433|02|Jun/04/01| | |O| B|8|sparc;sparc.sun4d;sparc.sun4m;sparc.sun4u;sparc.sun4us;108528-08;|FJSVhea:1.0,REV=1999.12.23.19.10;SUNWcar:11.8.0,REV=2000.01.08.18.12;SUNWcar:11.8.0,REV=2000.01.13.13.40;SUNWcarx:11.8.0,REV=2000.01.08.18.12;SUNWcarx:11.8.0,REV=2000.01.13.13.40;SUNWcsr:11.8.0,REV=2000.01.08.18.12;SUNWhea:11.8.0,REV=2000.01.08.18.12;108528-09;|WITHDRAWN PATCH Obsoleted by: <Integration> SunOS 5.8: Supplementa' )
105
+ end
106
+ end
107
+
108
+ private
109
+
110
+ def successor(line)
111
+ Solaris::PatchdiagEntry.new( line ).successor.to_s
112
+ end
113
+
114
+ end
115
+
@@ -0,0 +1,29 @@
1
+
2
+ require 'test/unit'
3
+ require 'solaris'
4
+
5
+ # Unit tests for top level require.
6
+ class TestSolaris < Test::Unit::TestCase #:nodoc:
7
+
8
+ def test_solaris
9
+ assert_nothing_raised { Solaris }
10
+ end
11
+
12
+ def test_solaris_patch
13
+ assert_nothing_raised { Solaris::Patch }
14
+ end
15
+
16
+ def test_solaris_patchdiag
17
+ assert_nothing_raised { Solaris::Patchdiag }
18
+ end
19
+
20
+ def test_solaris_patchdiag_entry
21
+ assert_nothing_raised { Solaris::PatchdiagEntry }
22
+ end
23
+
24
+ def test_test
25
+ assert_raise( NameError ) { Solaris::DoesNotExist }
26
+ end
27
+
28
+ end
29
+
data/test/test_util.rb ADDED
@@ -0,0 +1,31 @@
1
+
2
+ require 'test/unit'
3
+ require 'solaris/util'
4
+
5
+ # Unit tests for utility module.
6
+ class TestUtil < Test::Unit::TestCase #:nodoc:
7
+
8
+ def test_to_dir_to_file_mutually_exclusive
9
+ assert_raise( ArgumentError ) do
10
+ Solaris::Util.download!('http://example.com',
11
+ :to_dir => 'dir',
12
+ :to_file => 'file')
13
+ end
14
+ end
15
+
16
+ def test_user_without_password
17
+ assert_raise( ArgumentError ) do
18
+ Solaris::Util.download!('http://example.com',
19
+ :user => 'user')
20
+ end
21
+ end
22
+
23
+ def test_password_without_user
24
+ assert_raise( ArgumentError ) do
25
+ Solaris::Util.download!('http://example.com',
26
+ :password => 'password')
27
+ end
28
+ end
29
+
30
+ end
31
+
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: solaris-patch
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 0
9
+ version: 1.0.0
10
+ platform: ruby
11
+ authors:
12
+ - Martin Carpenter
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-05-03 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: mechanize
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 1
30
+ - 0
31
+ - 0
32
+ version: 1.0.0
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ description: Provides methods to deal with Solaris patchdiag.xref and patches, including parsing patchdiag.xref, downloads from Oracle (patch and readme), patch version comparison, and eg seeking the latest non-obsolete version of a patch
36
+ email: mcarpenter@free.fr
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - LICENSE
43
+ - Rakefile
44
+ - README.rdoc
45
+ files:
46
+ - lib/solaris/exception.rb
47
+ - lib/solaris/patch.rb
48
+ - lib/solaris/foo
49
+ - lib/solaris/patchdiag_entry.rb
50
+ - lib/solaris/util.rb
51
+ - lib/solaris/patchdiag.rb
52
+ - lib/solaris.rb
53
+ - test/test_patchdiag_entry.rb
54
+ - test/test_patch.rb
55
+ - test/test_util.rb
56
+ - test/test_solaris.rb
57
+ - test/test_patchdiag.rb
58
+ - LICENSE
59
+ - Rakefile
60
+ - README.rdoc
61
+ has_rdoc: true
62
+ homepage: http://mcarpenter.org/projects/solaris-patch
63
+ licenses:
64
+ - BSD
65
+ post_install_message:
66
+ rdoc_options: []
67
+
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ segments:
76
+ - 0
77
+ version: "0"
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ segments:
84
+ - 0
85
+ version: "0"
86
+ requirements: []
87
+
88
+ rubyforge_project:
89
+ rubygems_version: 1.3.7
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: Facilitate the manipulation of Solaris patches
93
+ test_files:
94
+ - test/test_patchdiag_entry.rb
95
+ - test/test_patch.rb
96
+ - test/test_util.rb
97
+ - test/test_solaris.rb
98
+ - test/test_patchdiag.rb