File Coverage

blib/lib/Cfn.pm
Criterion Covered Total %
statement 155 193 80.3
branch 39 68 57.3
condition 11 12 91.6
subroutine 38 48 79.1
pod 0 29 0.0
total 243 350 69.4


line stmt bran cond sub pod time code
1 21     21   2042292 use Moose::Util::TypeConstraints;
  21         1203526  
  21         222  
2              
3             subtype 'Cfn::Resource::DeletionPolicy',
4             as 'Str',
5             where { $_ eq 'Delete' or $_ eq 'Retain' or $_ eq 'Snapshot' },
6             message { "$_ is an invalid DeletionPolicy" };
7              
8             subtype 'Cfn::Value::ArrayOfPrimitives',
9             as 'Cfn::Value::Array',
10             where { @{ $_[0]->Value } == grep { $_->isa('Cfn::Value::Primitive') } @{ $_[0]->Value } },
11             message { 'This type only supports Primitives' };
12              
13             sub coerce_array {
14             Cfn::Value::Array->new(Value => [
15 926     926   952804 map { Moose::Util::TypeConstraints::find_type_constraint('Cfn::Value')->coerce($_) } @$_
  2191         1134315  
16             ])
17             }
18              
19             sub coerce_hash {
20 1925     1925   1732618 my $arg = $_;
21 1925         8810 my @keys = keys %$arg;
22 1925         3520 my $first_key = $keys[0];
23 1925 100 100     14548 if (@keys == 1 and (substr($first_key,0,4) eq 'Fn::' or $first_key eq 'Ref' or $first_key eq 'Condition')){
      100        
24 1317 100       4217 if ($first_key eq 'Fn::GetAtt') {
    100          
    100          
25 114         853 Cfn::Value::Function::GetAtt->new(Function => $first_key, Value => $arg->{ $first_key });
26             } elsif ($keys[0] eq 'Ref'){
27 918         4722 Cfn::Value::Function::Ref->new( Function => $first_key, Value => $arg->{ $first_key });
28             } elsif ($keys[0] eq 'Condition'){
29 2         18 Cfn::Value::Function::Condition->new( Function => $first_key, Value => $arg->{ $first_key });
30             } else {
31 283         1624 Cfn::Value::Function->new(Function => $first_key, Value => $arg->{ $first_key });
32             }
33             } else {
34             Cfn::Value::Hash->new(Value => {
35 608         1488 map { $_ => Moose::Util::TypeConstraints::find_type_constraint('Cfn::Value')->coerce($arg->{$_}) } @keys
  1623         994315  
36             });
37             }
38             }
39              
40             coerce 'Cfn::Value',
41             from 'Int|Str', via { Cfn::Value::Primitive->new( Value => $_ ) },
42             from 'HashRef', via (\&coerce_hash),
43             from 'ArrayRef', via (\&coerce_array);
44              
45             coerce 'Cfn::Value::Array',
46             from 'HashRef', via (\&coerce_hash),
47             from 'ArrayRef', via (\&coerce_array);
48              
49             coerce 'Cfn::Value::ArrayOfPrimitives',
50             from 'HashRef', via (\&coerce_hash),
51             from 'ArrayRef', via (\&coerce_array);
52              
53              
54             coerce 'Cfn::Value::Hash',
55             from 'HashRef', via (\&coerce_hash);
56              
57              
58             subtype 'Cfn::MappingHash',
59             as 'HashRef[Cfn::Mapping]';
60              
61             my $cfn_mapping_constraint = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Mapping');
62             coerce 'Cfn::MappingHash',
63             from 'HashRef', via {
64             my $original = $_;
65             return { map { ($_ => $cfn_mapping_constraint->coerce($original->{ $_ }) ) } keys %$original };
66             };
67              
68             coerce 'Cfn::Mapping',
69             from 'HashRef', via {
70             return Cfn::Mapping->new(%$_);
71             };
72              
73             subtype 'Cfn::OutputHash',
74             as 'HashRef[Cfn::Output]';
75              
76             my $cfn_output_constraint = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Output');
77             coerce 'Cfn::OutputHash',
78             from 'HashRef', via {
79             my $original = $_;
80             return { map { ($_ => $cfn_output_constraint->coerce($original->{ $_ }) ) } keys %$original };
81             };
82              
83             coerce 'Cfn::Output',
84             from 'HashRef', via {
85             return Cfn::Output->new(%$_);
86             };
87              
88             subtype 'Cfn::ConditionHash',
89             as 'HashRef[Cfn::Value]';
90              
91             my $cfn_value_constraint = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Value');
92             coerce 'Cfn::ConditionHash',
93             from 'HashRef', via {
94             my $original = $_;
95             return { map { ($_ => $cfn_value_constraint->coerce($original->{ $_ }) ) } keys %$original };
96             };
97              
98             subtype 'Cfn::ParameterHash',
99             as 'HashRef[Cfn::Parameter]';
100              
101             my $cfn_parameter_constraint = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Parameter');
102             coerce 'Cfn::ParameterHash',
103             from 'HashRef', via {
104             my $original = $_;
105             return { map { ($_ => $cfn_parameter_constraint->coerce($original->{ $_ }) ) } keys %$original };
106             };
107              
108             coerce 'Cfn::Parameter',
109             from 'HashRef', via {
110             return Cfn::Parameter->new(%$_);
111             };
112              
113             subtype 'Cfn::ResourceHash',
114             as 'HashRef[Cfn::Resource]';
115              
116             my $cfn_resource_constraint = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Resource');
117             coerce 'Cfn::ResourceHash',
118             from 'HashRef', via {
119             my $original = $_;
120             return { map { ($_ => $cfn_resource_constraint->coerce($original->{ $_ }) ) } keys %$original };
121             };
122              
123             coerce 'Cfn::Resource',
124             from 'HashRef', via {
125             my $type = delete $_->{Type};
126             die "Can't coerce HashRef into a Cfn::Resource if it doesn't have a Type key" if (not defined $type);
127             $type = "AWS::CloudFormation::CustomResource" if ($type =~ m/^Custom\:\:/);
128              
129             Cfn->load_resource_module($type);
130             # Properties is needed, although there are no properties
131             $_->{Properties} = {} if (not exists $_->{Properties});
132             return "Cfn::Resource::$type"->new(
133             %$_
134             );
135             };
136              
137             subtype 'Cfn::MetadataHash',
138             as 'HashRef[Cfn::Value]';
139              
140             coerce 'Cfn::MetadataHash',
141             from 'HashRef', via {
142             my $original = $_;
143             return { map { ($_ => $cfn_value_constraint->coerce($original->{ $_ }) ) } keys %$original };
144             };
145              
146             package Cfn::Value {
147 21     21   77245 use Moose;
  21         1414817  
  21         128  
148             has Value => (isa => 'Cfn::Value', is => 'rw', required => 1, coerce => 1);
149              
150 4     4 0 85 sub as_hashref { shift->Value->as_hashref(@_) }
151             }
152              
153             package Cfn::Value::Function {
154 21     21   147159 use Moose;
  21         51  
  21         100  
155             extends 'Cfn::Value';
156             has 'Function' => (isa => 'Str', is => 'rw', required => 1);
157             # inherits Value property as a Cfn::Value
158              
159             sub as_hashref {
160 247     247 0 442 my $self = shift;
161 247         5902 my $key = $self->Function;
162 247         5293 return { $key => $self->Value->as_hashref(@_) }
163             }
164             }
165              
166             package Cfn::Value::Function::Condition {
167 21     21   137173 use Moose;
  21         52  
  21         97  
168             extends 'Cfn::Value::Function';
169             #has '+Value' => (isa => 'Cfn::Value::Primitive', coerce => 1);
170              
171             sub Condition {
172 0     0 0 0 shift->Value->Value;
173             }
174             }
175              
176             package Cfn::Value::Function::Ref {
177 21     21   136628 use Moose;
  21         54  
  21         103  
178             extends 'Cfn::Value::Function';
179             #has '+Value' => (isa => 'Cfn::Value::Primitive', coerce => 1);
180              
181             sub LogicalId {
182 42     42 0 1265 shift->Value->Value;
183             }
184             }
185              
186             package Cfn::Value::Function::GetAtt {
187 21     21   136406 use Moose;
  21         52  
  21         96  
188             extends 'Cfn::Value::Function';
189             has '+Value' => (isa => 'Cfn::Value::ArrayOfPrimitives', coerce => 1);
190              
191             sub LogicalId {
192 8     8 0 139 my $self = shift;
193 8         229 $self->Value->Value->[0]->Value;
194             }
195              
196             sub Property {
197 2     2 0 3 my $self = shift;
198 2         48 $self->Value->Value->[1]->Value;
199             }
200             }
201              
202              
203             package Cfn::Value::Array {
204 21     21   136112 use Moose;
  21         54  
  21         99  
205             extends 'Cfn::Value';
206             has '+Value' => (
207             isa => 'ArrayRef[Cfn::Value]',
208             traits => ['Array'],
209             handles => {
210             'Count' => 'count',
211             }
212             );
213              
214             sub as_hashref {
215 214     214 0 431 my $self = shift;
216 214         416 my @args = @_;
217 214         313 return [ map { $_->as_hashref(@args) } @{ $self->Value } ]
  357         1113  
  214         4914  
218             };
219             }
220              
221             package Cfn::Value::Hash {
222 21     21   137637 use Moose;
  21         49  
  21         101  
223             extends 'Cfn::Value';
224             has '+Value' => (isa => 'HashRef[Cfn::Value]');
225             override as_hashref => sub {
226             my $self = shift;
227             my @args = @_;
228             return { map { $_ => $self->Value->{$_}->as_hashref(@args) } keys %{ $self->Value } };
229             };
230             }
231              
232             package Cfn::Value::Primitive {
233 21     21   139167 use Moose;
  21         58  
  21         113  
234             extends 'Cfn::Value';
235             has '+Value' => (isa => 'Int|Str');
236             override as_hashref => sub {
237             my $self = shift;
238             return $self->Value;
239             }
240             }
241              
242             package Cfn::Resource {
243 21     21   136511 use Moose;
  21         62  
  21         91  
244             # CCfnX::Dependencies is not production ready
245             with 'CCfnX::Dependencies';
246             has Type => (isa => 'Str', is => 'rw', required => 1, default => sub {
247             my $type = shift->meta->name;
248             $type =~ s/^Cfn\:\:Resource\:\://;
249             return $type;
250             });
251             has Properties => (isa => 'Cfn::Resource::Properties', is => 'rw', required => 1);
252             has DeletionPolicy => (isa => 'Cfn::Resource::DeletionPolicy', is => 'rw');
253             has DependsOn => (isa => 'ArrayRef[Str]|Str', is => 'rw');
254             has Condition => (isa => 'Str', is => 'rw');
255              
256             sub DependsOnList {
257 32     32 0 57 my $self = shift;
258 32 100       782 return () if (not defined $self->DependsOn);
259 10 100       241 return @{ $self->DependsOn } if (ref($self->DependsOn) eq 'ARRAY');
  9         222  
260 1         40 return $self->DependsOn;
261             }
262              
263             has Metadata => (isa => 'Cfn::Value::Hash', is => 'rw', coerce => 1);
264             #TODO: validate this http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-updatepolicy.html
265             has UpdatePolicy => (isa => 'HashRef', is => 'rw');
266              
267             sub as_hashref {
268 135     135 0 400 my $self = shift;
269 135         394 my @args = @_;
270             return {
271 136         4345 (map { $_ => $self->$_->as_hashref(@args) }
272 270         7998 grep { defined $self->$_ } qw/Properties Metadata/),
273 136         3337 (map { $_ => $self->$_ }
274 135         352 grep { defined $self->$_ } qw/Type DeletionPolicy DependsOn UpdatePolicy Condition/),
  675         16738  
275             }
276             }
277             }
278              
279             package Cfn::Resource::Properties {
280 21     21   142549 use Moose;
  21         55  
  21         114  
281             sub as_hashref {
282 135     135 0 410 my $self = shift;
283 135         382 my @args = @_;
284              
285             #return {
286 135         278 my $ret = {};
287             #map { (defined $self->$_) ? ($_ => $self->$_->as_hashref(@args)) : () } $self->meta->get_all_attributes
288 135         554 foreach my $att ($self->meta->get_all_attributes) {
289 1021         12826 my $el = $att->name;
290 1021 100       36444 if (defined $self->$el) {
291 470         15497 my @ret = $self->$el->as_hashref(@args);
292 470 50       1189 if (@ret == 1) {
293 470         1358 $ret->{ $el } = $ret[0];
294             } else {
295 0         0 die "A property returned an odd number of values";
296             }
297             }
298             }
299 135         738 return $ret;
300             #}
301             }
302              
303             sub resolve_references_to_logicalid_with {
304 60     60 0 359 my ($self, $logical_id, $object) = @_;
305 60         166 foreach my $att ($self->meta->get_attribute_list) {
306 180 100       17586 next if (not defined $self->$att);
307              
308 74 100 100     1777 if ($self->$att->isa('Cfn::Value::Function::Ref') and $self->$att->LogicalId eq $logical_id) {
    100 66        
    100          
    100          
    50          
    0          
309 8         151 my $func = $self->$att;
310             #$self->$att('TBD'); #$object->$objects_ref_prop
311             #warn "Resolved TBD $logical_id";
312 8         56 my @attrs = $object->meta->get_all_attributes;
313 8         578 my @ref = grep { $_->does('CCfnX::Meta::Attribute::Trait::RefValue') } @attrs;
  32         3069  
314 8 50       776 if (not @ref) { die $object . " has no RefValue trait. Cannot resolve Ref" }
  0         0  
315             else {
316 8         24 my $property = $ref[0]->name;
317 8         204 my $value = $object->$property;
318 8         233 $self->$att($value);
319             }
320             } elsif ($self->$att->isa('Cfn::Value::Function::GetAtt') and $self->$att->LogicalId eq $logical_id) {
321 2         41 my $func = $self->$att;
322 2         15 my $property = $func->Property;
323 2         48 $self->$att($object->$property);
324 2         1554 warn "Resolved $logical_id $property";
325             } elsif ($self->$att->isa('Cfn::Value::Array')) {
326 13         710 map { resolve_references_to_logicalid_with($_, $logical_id, $object) } @{ $self->$att->Value };
  13         31  
  13         243  
327             } elsif ($self->$att->isa('Cfn::Value::Function')) {
328 2         147 resolve_references_to_logicalid_with($self->$att, $logical_id, $object);
329             } elsif ($self->$att->isa('Cfn::Value::Primitive')) {
330             # End case. Primitives do nothing
331             # This case is important to be here, as it filters out any Primitives for
332             # the next if
333             } elsif ($self->$att->isa('Cfn::Value')) {
334 0         0 resolve_references_to_logicalid_with($self->$att, $logical_id, $object);
335             } else {
336 0         0 die "Don't know how to resolve $att on " . $self->$att;
337             }
338             }
339             }
340             }
341              
342             package Cfn::Output {
343 21     21   146871 use Moose;
  21         66  
  21         112  
344             has Value => (isa => 'Cfn::Value', is => 'rw', required => 1, coerce => 1);
345             has Condition => (isa => 'Str', is => 'rw');
346             sub as_hashref {
347 6     6 0 13 my $self = shift;
348 6 100       136 return { Value => $self->Value->as_hashref,
349             (defined $self->Condition) ? (Condition => $self->Condition) : ()
350             }
351             }
352             }
353              
354             enum 'Cfn::Parameter::Type', [
355             'String',
356             'Number',
357             'List<Number>',
358             'CommaDelimitedList',
359             'AWS::EC2::AvailabilityZone::Name',
360             'List<AWS::EC2::AvailabilityZone::Name>',
361             'AWS::EC2::Instance::Id',
362             'List<AWS::EC2::Instance::Id>',
363             'AWS::EC2::Image::Id',
364             'List<AWS::EC2::Image::Id>',
365             'AWS::EC2::KeyPair::KeyName',
366             'AWS::EC2::SecurityGroup::GroupName',
367             'List<AWS::EC2::SecurityGroup::GroupName>',
368             'AWS::EC2::SecurityGroup::Id',
369             'List<AWS::EC2::SecurityGroup::Id>',
370             'AWS::EC2::Subnet::Id',
371             'List<AWS::EC2::Subnet::Id>',
372             'AWS::EC2::Volume::Id',
373             'List<AWS::EC2::Volume::Id>',
374             'AWS::EC2::VPC::Id',
375             'List<AWS::EC2::VPC::Id>',
376             'AWS::Route53::HostedZone::Id',
377             'List<AWS::Route53::HostedZone::Id>',
378             ];
379              
380             package Cfn::Parameter {
381 21     21   138899 use Moose;
  21         54  
  21         139  
382             has Type => (isa => 'Cfn::Parameter::Type', is => 'ro', required => 1);
383             has Default => (isa => 'Str', is => 'rw');
384             has NoEcho => (isa => 'Str', is => 'rw');
385             has AllowedValues => ( isa => 'ArrayRef[Str]', is => 'rw');
386             has AllowedPattern => ( isa => 'Str', is => 'rw');
387             has MaxLength => ( isa => 'Str', is => 'rw');
388             has MinLength => ( isa => 'Str', is => 'rw');
389             has MaxValue => ( isa => 'Str', is => 'rw');
390             has MinValue => ( isa => 'Str', is => 'rw');
391             has Description => ( isa => 'Str', is => 'rw');
392             has ConstraintDescription => ( isa => 'Str', is => 'rw');
393              
394             sub as_hashref {
395 0     0 0 0 my $self = shift;
396             return {
397 0 0       0 map { (defined $self->$_) ? ($_ => $self->$_) : () }
  0         0  
398             qw/Type Default NoEcho AllowedValues AllowedPattern MaxLength
399             MinLength MaxValue MinValue Description ConstraintDescription/,
400             }
401             }
402             }
403              
404             package Cfn::Mapping {
405 21     21   141436 use Moose;
  21         60  
  21         133  
406             has Map => (isa => 'HashRef', is => 'ro');
407              
408             sub as_hashref {
409 0     0 0 0 my $self = shift;
410 0         0 return $self->Map;
411             }
412             }
413              
414             package Cfn {
415 21     21   135887 use Moose;
  21         63  
  21         123  
416 21     21   134376 use Moose::Util;
  21         57  
  21         197  
417             has AWSTemplateFormatVersion => (isa => 'Str', is => 'ro', default => '2010-09-09' );
418             has Description => (isa => 'Str', is => 'rw', required => 1, default => '' );
419             has Transform => (isa => 'Str', is => 'rw');
420             has Parameters => (
421             is => 'rw',
422             isa => 'Cfn::ParameterHash',
423             coerce => 1,
424             default => sub { {} },
425             traits => [ 'Hash' ],
426             handles => {
427             ParameterCount => 'count',
428             },
429             );
430             has Mappings => (
431             is => 'rw',
432             isa => 'Cfn::MappingHash',
433             coerce => 1,
434             traits => [ 'Hash' ],
435             handles => {
436             Mapping => 'get',
437             MappingCount => 'count',
438             },
439             default => sub { {} }
440             );
441             has Conditions => (
442             is => 'rw',
443             isa => 'Cfn::ConditionHash',
444             traits => [ 'Hash' ],
445             coerce => 1,
446             handles => {
447             Condition => 'get',
448             ConditionList => 'keys',
449             ConditionCount => 'count',
450             },
451             default => sub { {} }
452             );
453             has Resources => (
454             is => 'rw',
455             isa => 'Cfn::ResourceHash',
456             coerce => 1,
457             traits => [ 'Hash' ],
458             handles => {
459             Resource => 'get',
460             ResourceList => 'keys',
461             ResourceCount => 'count',
462             },
463             default => sub { {} }
464             );
465             has Outputs => (
466             is => 'rw',
467             isa => 'Cfn::OutputHash',
468             coerce => 1,
469             traits => [ 'Hash' ],
470             handles => {
471             Output => 'get',
472             OutputCount => 'count',
473             },
474             default => sub { {} },
475             );
476             has Metadata => (
477             is => 'rw',
478             isa => 'Cfn::MetadataHash',
479             coerce => 1,
480             traits => [ 'Hash' ],
481             handles => {
482             MetadataItem => 'get',
483             MetadataList => 'keys',
484             MetadataCount => 'count',
485             },
486             default => sub { {} },
487             );
488 21     21   9762 use Module::Runtime qw//;
  21         55  
  21         30906  
489             sub load_resource_module {
490 389     389 0 1103 my (undef, $type) = @_;
491 389         1275 my $cfn_resource_class = "Cfn::Resource::$type";
492 389         1785 my $retval = Module::Runtime::require_module($cfn_resource_class);
493 387 50       11221 die "Couldn't load $cfn_resource_class" if (not $retval);
494 387         1054 return $cfn_resource_class;
495             }
496              
497             #method addParameter (Str $name, Cfn::Parameter|Str $type, %rest) {
498             sub addParameter {
499 0     0 0 0 my ($self, $name, $type, %rest) = @_;
500 0 0       0 die "A parameter named $name already exists" if ($self->Parameters->{ $name });
501 0 0       0 if (ref $type) {
502 0         0 return $self->Parameters->{ $name } = $type;
503             } else {
504 0         0 return $self->Parameters->{ $name } = Cfn::Parameter->new(Type => $type, %rest);
505             }
506             }
507              
508             #method addMapping (Str $name, $mapping) {
509             sub addMapping {
510 0     0 0 0 my ($self, $name, $mapping) = @_;
511 0 0       0 die "A mapping named $name already exists" if ($self->Mappings->{ $name });
512 0 0       0 if (ref $mapping eq 'HASH') {
513 0         0 return $self->Mappings->{ $name } = Cfn::Mapping->new(Map => $mapping);
514             } else {
515 0         0 return $self->Mappings->{ $name } = $mapping;
516             }
517             }
518              
519             #method addOutput (Str $name, $output, @rest) {
520             sub addOutput {
521 8     8 0 25 my ($self, $name, $output, @rest) = @_;
522 8 50       187 die "An output named $name already exists" if ($self->Outputs->{ $name });
523 8         74 return $self->Outputs->{ $name } = Cfn::Output->new( Value => $output, @rest );
524             }
525              
526             sub addCondition {
527 1     1 0 4 my ($self, $name, $value) = @_;
528 1 50       24 die "A condition named $name already exists" if ($self->Conditions->{ $name });
529 1         16 return $self->Conditions->{ $name } = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Value')->coerce($value);
530             }
531              
532             sub addResource {
533 199     199 0 299403 my ($self, $name, $type, @rest) = @_;
534 199 50       5150 die "A resource named $name already exists" if ($self->Resources->{ $name });
535 199 100       757 if (not ref $type){
536 66         380 return $self->Resources->{ $name } = Moose::Util::TypeConstraints::find_type_constraint('Cfn::Resource')->coerce({
537             Type => $type,
538             Properties => { @rest }
539             });
540             } else {
541 133         2920 return $self->Resources->{ $name } = $type;
542             }
543             }
544              
545             #method addMetadata (Str $name, %args) {
546             sub addMetadata {
547 1     1 0 28 my ($self, $name, %args) = @_;
548 1 50       40 die "A resource named $name must already exist" if (not defined $self->Resources->{ $name });
549 1         35 $self->Resources->{ $name }->Metadata({ %args });
550             }
551             #method addResourceMetadata (Str $name, %args) {
552             sub addResourceMetadata {
553 0     0 0 0 my ($self, $name, %args) = @_;
554 0 0       0 die "A resource named $name must already exist" if (not defined $self->Resources->{ $name });
555 0         0 $self->Resources->{ $name }->Metadata({ %args });
556             }
557             #method addDependsOn (Str $name, @args) {
558             sub addDependsOn {
559 0     0 0 0 my ($self, $name, @args) = @_;
560 0 0       0 die "A resource named $name must already exist" if (not defined $self->Resources->{ $name });
561 0         0 $self->Resources->{ $name }->DependsOn( [ @args ] );
562             }
563             #method addDeletionPolicy (Str $name, Str $policy) {
564             sub addDeletionPolicy {
565 0     0 0 0 my ($self, $name, $policy) = @_;
566 0 0       0 die "A resource named $name must already exist" if (not defined $self->Resources->{ $name });
567 0         0 $self->Resources->{ $name }->DeletionPolicy( $policy );
568             }
569             #method addUpdatePolicy (Str $name, Str $policy) {
570             sub addUpdatePolicy {
571 0     0 0 0 my ($self, $name, $policy) = @_;
572 0 0       0 die "A resource named $name must already exist" if (not defined $self->Resources->{ $name });
573 0         0 $self->Resources->{ $name }->UpdatePolicy( $policy );
574             }
575              
576             sub from_hashref {
577 46     46 0 12555 my ($class, $hashref) = @_;
578 46         404 return $class->new(%$hashref);
579             }
580              
581             sub as_hashref {
582 10     10 0 67 my $self = shift;
583             return {
584             AWSTemplateFormatVersion => $self->AWSTemplateFormatVersion,
585             Description => $self->Description,
586             (defined $self->Transform) ? (Transform => $self->Transform) : (),
587 15         476 Resources => { map { ($_ => $self->Resource($_)->as_hashref($self)) } $self->ResourceList },
588 10         306 (keys %{$self->Mappings} > 0) ? ( Mappings => { map { ($_ => $self->Mappings->{ $_ }->as_hashref) } keys %{ $self->Mappings } } ) : (),
  0         0  
  0         0  
589 0         0 Parameters => { map { ($_ => $self->Parameters->{ $_ }->as_hashref) } keys %{ $self->Parameters } },
  10         294  
590 6         133 Outputs => { map { ($_ => $self->Outputs->{ $_ }->as_hashref($self)) } keys %{ $self->Outputs } },
  10         269  
591 1         29 Conditions => { map { ($_ => $self->Condition($_)->as_hashref($self)) } $self->ConditionList },
592 10 100       333 Metadata => { map { ($_ => $self->Metadata->{ $_ }->as_hashref($self)) } $self->MetadataList },
  4 50       96  
593             }
594             }
595              
596             has json => (is => 'ro', lazy => 1, default => sub {
597             require JSON;
598             return JSON->new->pretty->canonical
599             });
600              
601             sub as_json {
602 0     0 0 0 my $self = shift;
603 0         0 my $href = $self->as_hashref;
604 0         0 return $self->json->encode($href);
605             }
606              
607             sub from_json {
608 41     41 0 684980 my ($class, $json) = @_;
609              
610 41         418 require JSON;
611 41         202 return $class->from_hashref(JSON::from_json($json));
612             }
613              
614             }
615              
616             1;