blib/lib/Mirror/YAML.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 23 | 25 | 92.0 |
branch | 2 | 4 | 50.0 |
condition | n/a | ||
subroutine | 8 | 8 | 100.0 |
pod | 0 | 2 | 0.0 |
total | 33 | 39 | 84.6 |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | package Mirror::YAML; | ||||||
2 | |||||||
3 | 4 | 4 | 750468 | use 5.006; | |||
4 | 18 | ||||||
4 | 554 | ||||||
4 | 4 | 4 | 25 | use strict; | |||
4 | 8 | ||||||
4 | 124 | ||||||
5 | 4 | 4 | 2758 | use Mirror::URI (); | |||
4 | 12 | ||||||
4 | 87 | ||||||
6 | 4 | 4 | 16010 | use Parse::CPAN::Meta (); | |||
4 | 11611 | ||||||
4 | 115 | ||||||
7 | |||||||
8 | 4 | 4 | 33 | use vars qw{$VERSION @ISA}; | |||
4 | 8 | ||||||
4 | 324 | ||||||
9 | BEGIN { | ||||||
10 | 4 | 4 | 10 | $VERSION = '0.90'; | |||
11 | 4 | 1271 | @ISA = 'Mirror::URI'; | ||||
12 | } | ||||||
13 | |||||||
14 | |||||||
15 | |||||||
16 | |||||||
17 | |||||||
18 | ##################################################################### | ||||||
19 | # Implementation-Specific Methods | ||||||
20 | |||||||
21 | sub filename { | ||||||
22 | 6 | 6 | 0 | 34 | return 'mirror.yml'; | ||
23 | } | ||||||
24 | |||||||
25 | sub parse { | ||||||
26 | 4 | 4 | 0 | 12 | my $class = shift; | ||
27 | |||||||
28 | # Make sure we actually have a YAML document | ||||||
29 | 4 | 50 | 27 | unless ( $_[0] =~ /^---/ ) { | |||
30 | 0 | 0 | return undef; | ||||
31 | } | ||||||
32 | |||||||
33 | # Parse the file | ||||||
34 | 4 | 28 | my @docs = Parse::CPAN::Meta::Load( $_[0] ); | ||||
35 | |||||||
36 | # We only care about the first document | ||||||
37 | 4 | 50 | 19938 | if ( defined $docs[0] ) { | |||
38 | 4 | 21 | return $docs[0]; | ||||
39 | } else { | ||||||
40 | 0 | Carp::croak("Illegal YAML document"); | |||||
41 | } | ||||||
42 | } | ||||||
43 | |||||||
44 | 1; | ||||||
45 | |||||||
46 | =pod | ||||||
47 | |||||||
48 | =head1 NAME | ||||||
49 | |||||||
50 | Mirror::YAML - Mirror Configuration and Auto-Discovery | ||||||
51 | |||||||
52 | =head1 DESCRIPTION | ||||||
53 | |||||||
54 | A C |
||||||
55 | robustly locate, identify, validate and age a repository. | ||||||
56 | |||||||
57 | It contains a timestamp for when the repository was last updated, the URI | ||||||
58 | for the master repository, and a list of all the current mirrors at the | ||||||
59 | time the repository was last updated. | ||||||
60 | |||||||
61 | B |
||||||
62 | and read the F |
||||||
63 | mirrors entirely automatically. | ||||||
64 | |||||||
65 | It currently scales cleanly for a dozen or so mirrors, but may be slow | ||||||
66 | when used with very large repositories with a hundred or more mirrors. | ||||||
67 | |||||||
68 | =head2 Methodology | ||||||
69 | |||||||
70 | A variety of simple individual mechanisms are combined to provide a | ||||||
71 | completely robust discovery and validation system. | ||||||
72 | |||||||
73 | B |
||||||
74 | |||||||
75 | The F |
||||||
76 | the root of the repository. The file is very small (no more than a few | ||||||
77 | kilobytes at most) so the overhead of fetching one (or several) of them | ||||||
78 | is negligable. | ||||||
79 | |||||||
80 | The file is pulled via FTP or HTTP. Once pulled, the first three | ||||||
81 | characters are examined to validate it is a YAML file and not a | ||||||
82 | login page for a "captured hotspot" such as at hotels and airports. | ||||||
83 | |||||||
84 | The shorter ".yml" is used in the file name to allow for Mirror::YAML | ||||||
85 | to be used even in the rare situation of mirrors that must work | ||||||
86 | on operating systems with (now relatively rare) 8.3 filesystems. | ||||||
87 | |||||||
88 | B |
||||||
89 | |||||||
90 | Because the F |
||||||
91 | packets) the download time can be used to measure the responsiveness of | ||||||
92 | that mirror. | ||||||
93 | |||||||
94 | By pulling the files from several mirrors, the comparative download | ||||||
95 | times can be used as part of the process of selecting the fastest mirror. | ||||||
96 | |||||||
97 | B |
||||||
98 | |||||||
99 | The mirror.yml file contains a timestamp that records the last update time | ||||||
100 | for the repository. This timestamp should be updated every repository | ||||||
101 | update cycle, even if there are no actual changes to the repository. | ||||||
102 | |||||||
103 | Once a F |
||||||
104 | then be used to verify the age of the mirror. Whereas a perfectly up to | ||||||
105 | date mirror will show an age of less than an hour (assuming that the | ||||||
106 | repository master updates every hour) a repository that has stopped | ||||||
107 | updating will show an age that is greater than the longest mirror rate | ||||||
108 | plus the update cycle time. | ||||||
109 | |||||||
110 | Thus, any mirror that as "gone stale" can be filter out of the potential | ||||||
111 | mirrors to use. | ||||||
112 | |||||||
113 | For portability, the timestamp is recording in ISO format Zulu time. | ||||||
114 | |||||||
115 | B |
||||||
116 | |||||||
117 | The F |
||||||
118 | |||||||
119 | If the L |
||||||
120 | point, it will use the master repository URI in the current state to | ||||||
121 | pull a fresh F |
||||||
122 | |||||||
123 | This solves the most-simple case, but other cases require a little | ||||||
124 | more complexity (which we'll address later). | ||||||
125 | |||||||
126 | B |
||||||
127 | |||||||
128 | The F |
||||||
129 | |||||||
130 | Apart from filtering the list to try and find the best mirror to use, | ||||||
131 | the mirror list allows the B |
||||||
132 | options for locating the master repository if it moves, or the | ||||||
133 | bootstrap F |
||||||
134 | |||||||
135 | If the client can't find the master repository (because it has moved) | ||||||
136 | the client will scan the list of mirrors to try to find the location | ||||||
137 | of the updated repository. | ||||||
138 | |||||||
139 | B |
||||||
140 | |||||||
141 | To bootstrap the client, it should come with a default bootstrap | ||||||
142 | F |
||||||
143 | first time, it will attempt to fetch an updated mirror.yml from the | ||||||
144 | master repository, and if that doesn't exist will pull from the | ||||||
145 | default list of mirrors until it can find more than one up to date | ||||||
146 | mirror that agrees on the real location of the master server. | ||||||
147 | |||||||
148 | B |
||||||
149 | |||||||
150 | On top of the straight forward mirror discovery functionality, the | ||||||
151 | client algorithm contains additional logic to deal with either a | ||||||
152 | mirror or the master server goes bad. While likely not 100% secure | ||||||
153 | it heads off several attack scenarios to prevent anyone trying them, | ||||||
154 | and provides as much as can be expected without resorting to cryto | ||||||
155 | and certificates. | ||||||
156 | |||||||
157 | =head1 SUPPORT | ||||||
158 | |||||||
159 | Bugs should be reported via the CPAN bug tracker at | ||||||
160 | |||||||
161 | L |
||||||
162 | |||||||
163 | For other issues, or commercial enhancement or support, contact the author. | ||||||
164 | |||||||
165 | =head1 AUTHOR | ||||||
166 | |||||||
167 | Adam Kennedy E |
||||||
168 | |||||||
169 | =head1 SEE ALSO | ||||||
170 | |||||||
171 | L |
||||||
172 | |||||||
173 | =head1 COPYRIGHT | ||||||
174 | |||||||
175 | Copyright 2007 - 2009 Adam Kennedy. | ||||||
176 | |||||||
177 | This program is free software; you can redistribute | ||||||
178 | it and/or modify it under the same terms as Perl itself. | ||||||
179 | |||||||
180 | The full text of the license can be found in the | ||||||
181 | LICENSE file included with this module. | ||||||
182 | |||||||
183 | =cut |