File Coverage

blib/lib/Cfn/Crawler.pm
Criterion Covered Total %
statement 85 89 95.5
branch 18 20 90.0
condition n/a
subroutine 11 11 100.0
pod n/a
total 114 120 95.0


line stmt bran cond sub pod time code
1             package Cfn::Crawler::Path;
2 1     1   1431 use Moose;
  1         3  
  1         12  
3              
4             has path => (is => 'ro', isa => 'Str', required => 1);
5             has element => (is => 'ro', required => 1);
6              
7             package Cfn::Crawler;
8 1     1   7296 use Moose;
  1         4  
  1         5  
9              
10             has resolve_dynamicvalues => (is => 'ro', isa => 'Bool', default => 0);
11              
12             has cfn => (is => 'ro', isa => 'Cfn', required => 1);
13              
14             has _resolved_cfn => (is => 'ro', isa => 'Cfn', lazy => 1, default => sub {
15             my $self = shift;
16             if ($self->resolve_dynamicvalues) {
17             return $self->cfn->resolve_dynamicvalues;
18             } else {
19             return $self->cfn;
20             }
21             });
22              
23             has criteria => (is => 'ro', isa => 'CodeRef', required => 1);
24              
25             has _all => (
26             is => 'ro',
27             lazy => 1,
28             builder => '_scan_all',
29             isa => 'ArrayRef',
30             traits => ['Array'],
31             handles => {
32             all => 'elements'
33             },
34             );
35             for my $property (qw/resources outputs parameters
36             metadata mappings conditions/){
37             has "_$property" => (
38             is => 'ro',
39             lazy => 1,
40             builder => "_scan_$property",
41             isa => 'ArrayRef',
42             traits => [ 'Array' ],
43             handles => {
44             $property => 'elements',
45             }
46             );
47             }
48              
49             sub _match {
50 8698     8698   14437 my ($self, $path, $element) = @_;
51 8698 100       195405 if ($self->criteria->($element)) {
52 8695         44960 return Cfn::Crawler::Path->new(
53             path => $path,
54             element => $element,
55             );
56             } else {
57 3         28 return ();
58             }
59             }
60              
61             sub _scan_all {
62 93     93   283 my $self = shift;
63              
64 93         224 my @results;
65              
66 93         3277 push @results, $self->resources;
67 93         3432 push @results, $self->outputs;
68 93         3102 push @results, $self->parameters;
69 93         3007 push @results, $self->mappings;
70 93         3182 push @results, $self->metadata;
71 93         3061 push @results, $self->conditions;
72              
73 93         2816 return \@results;
74             }
75              
76             sub _scan_conditions {
77 93     93   231 my $self = shift;
78 93         248 my @results;
79              
80 93         2083 foreach my $c_name (sort $self->_resolved_cfn->ConditionList) {
81 4         15 my $path = "Conditions.$c_name";
82 4         94 my $condition = $self->_resolved_cfn->Condition($c_name);
83              
84 4         21 push @results, $self->_match($path, $condition);
85              
86             #TODO: Can't crawl into conditions, because they are not proper
87             # objects yet
88             #foreach my $cond_key (keys %$condition) {
89             # push @results, $self->_crawl_values("$path\.$cond_key", $condition->{ $cond_key });
90             #}
91             }
92              
93 93         5305 return \@results;
94             }
95              
96             sub _scan_metadata {
97 93     93   194 my $self = shift;
98 93         222 my @results;
99              
100 93         2171 foreach my $md_name (sort $self->_resolved_cfn->MetadataList) {
101 0         0 my $path = "Metadata.$md_name";
102 0         0 my $metadata = $self->_resolved_cfn->MetadataItem($md_name);
103              
104 0         0 push @results, $self->_match($path, $metadata);
105             }
106              
107 93         2794 return \@results;
108             }
109              
110             sub _scan_mappings {
111 93     93   217 my $self = shift;
112 93         188 my @results;
113              
114 93         2131 foreach my $m_name (sort $self->_resolved_cfn->MappingList) {
115 98         35644 my $path = "Mappings.$m_name";
116 98         2300 my $mapping = $self->_resolved_cfn->Mapping($m_name);
117              
118 98         272 push @results, $self->_match($path, $mapping);
119             }
120              
121 93         30329 return \@results;
122             }
123              
124             sub _scan_parameters {
125 93     93   197 my $self = shift;
126 93         278 my @results;
127              
128 93         2111 foreach my $p_name (sort $self->_resolved_cfn->ParameterList) {
129 294         143322 my $path = "Parameters.$p_name";
130 294         6831 my $parameter = $self->_resolved_cfn->Parameter($p_name);
131              
132 294         779 push @results, $self->_match($path, $parameter);
133             }
134              
135 93         49517 return \@results;
136             }
137              
138             sub _scan_outputs {
139 93     93   228 my $self = shift;
140 93         217 my @results;
141              
142 93         2193 foreach my $o_name (sort $self->_resolved_cfn->OutputList) {
143 135         571 my $path = "Outputs.$o_name";
144 135         3253 my $output = $self->_resolved_cfn->Output($o_name);
145              
146 135         461 push @results, $self->_match($path, $output);
147              
148 135         91540 push @results, $self->_crawl_values("$path\.Value", $output->Value);
149             }
150              
151 93         3104 return \@results;
152             }
153              
154             sub _scan_resources {
155 93     93   312 my $self = shift;
156 93         330 my @results;
157              
158 93         2443 foreach my $r_name (sort $self->_resolved_cfn->ResourceList) {
159 591         1883 my $path = "Resources.$r_name";
160 591         14112 my $resource = $self->_resolved_cfn->Resource($r_name);
161              
162 591         2098 push @results, $self->_match($path, $resource);
163              
164 591 100       402074 next if (not defined $resource->Properties);
165              
166 563         16442 foreach my $prop ($resource->Properties->meta->get_all_attributes) {
167 5947         70972 my $prop_name = $prop->name;
168 5947         16150 my $prop_value = $resource->Property($prop_name);
169            
170 5947         21694 push @results, $self->_crawl_values("$path\.Properties\.$prop_name", $prop_value);
171             }
172             }
173              
174 93         3424 return \@results;
175             }
176              
177             #
178             # since Cfn::Values are recursive (can contain other Cfn::Values)
179             # this guy will recurse over them, and for the ones matching the
180             # criteria, will give you a path to them
181             #
182             sub _crawl_values {
183 12677     12677   24935 my ($self, $path, $value) = @_;
184            
185 12677 100       47395 return if (not blessed $value);
186 7576 50       25534 die "Found something that isn't a Cfn::Value" if (not $value->isa('Cfn::Value'));
187              
188 7576         10610 my @results;
189              
190 7576         17588 push @results, $self->_match($path, $value);
191              
192 7576 100       4929193 if ($value->isa('Cfn::Value::Array')) {
    100          
    100          
    100          
    100          
    50          
193 960         2001 my $index = 0;
194 960         1450 foreach my $element (@{ $value->Value }) {
  960         21555  
195 2297         10151 push @results, $self->_crawl_values("$path\.$index", $element);
196 2297         5618 $index++;
197             }
198             } elsif ($value->isa('Cfn::Value::Function')) {
199 1672         41565 push @results, $self->_crawl_values("$path\." . $value->Function, $value->Value);
200             } elsif ($value->isa('Cfn::Value::Hash')) {
201 143         266 my $index;
202 143         261 foreach my $key (sort keys %{ $value->Value }){
  143         3363  
203 305         7024 push @results, $self->_crawl_values("$path\.$key", $value->Value->{ $key });
204             }
205             } elsif ($value->isa('Cfn::Value::Primitive')) {
206             # A primitives value is not traversable
207             } elsif ($value->isa('Cfn::Value::TypedValue')) {
208 445         2424 foreach my $property ($value->meta->get_all_attributes) {
209 2321         47537 my $prop_name = $property->name;
210 2321         87081 my $prop_value = $value->$prop_name;
211 2321         9067 push @results, $self->_crawl_values("$path\.$prop_name", $prop_value);
212             }
213             } elsif ($value->isa('Cfn::DynamicValue')) {
214             # A DynamicValue is not traversable
215             } else {
216 0         0 die "Unknown $value at $path";
217             }
218              
219 7576         22290 return @results;
220             }
221              
222             1;