Skip to content

Commit e43c771

Browse files
committed
Parent can refer to module properties
1 parent 064fe42 commit e43c771

File tree

5 files changed

+111
-41
lines changed

5 files changed

+111
-41
lines changed

awscli/customizations/cloudformation/modules.py

Lines changed: 99 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -488,10 +488,14 @@ def process_references(self):
488488
In the parent you can !GetAtt ModuleName.OutputName
489489
This will be converted so that it's correct in the packaged template.
490490
491+
The parent can also refer to module Properties as a convenience,
492+
so !GetAtt ModuleName.PropertyName will simply copy the
493+
configured property value.
494+
491495
Recurse over all sections in the parent template looking for
492496
GetAtts and Subs that reference a module References value.
493497
"""
494-
sections = [RESOURCES, REFERENCES, MODULES]
498+
sections = [RESOURCES, REFERENCES, MODULES, OUTPUTS]
495499
for section in sections:
496500
if section not in self.template:
497501
continue
@@ -502,8 +506,7 @@ def resolve_references(self, k, v, d, n):
502506
"""
503507
Recursively resolve GetAtts and Subs that reference module references.
504508
505-
:param name The name of the output
506-
:param output The output dict
509+
:param name The name of the reference
507510
:param k The name of the node
508511
:param v The value of the node
509512
:param d The dict that holds the parent of k
@@ -512,9 +515,9 @@ def resolve_references(self, k, v, d, n):
512515
If a reference is found, this function sets the value of d[n]
513516
"""
514517
if k == SUB:
515-
self.resolve_output_sub(v, d, n)
518+
self.resolve_reference_sub(v, d, n)
516519
elif k == GETATT:
517-
self.resolve_output_getatt(v, d, n)
520+
self.resolve_reference_getatt(v, d, n)
518521
else:
519522
if isdict(v):
520523
for k2, v2 in v.copy().items():
@@ -527,8 +530,72 @@ def resolve_references(self, k, v, d, n):
527530
for k3, v3 in v2.copy().items():
528531
self.resolve_references(k3, v3, v, idx)
529532

530-
def resolve_output_sub(self, v, d, n):
531-
"Resolve a Sub that refers to a module output"
533+
def resolve_reference_sub_getatt(self, w):
534+
"""
535+
Resolve a reference to a module in a Sub GetAtt word.
536+
537+
For example, the parent has
538+
539+
Modules:
540+
A:
541+
Properties:
542+
Name: foo
543+
Outputs:
544+
B:
545+
Value: !Sub ${A.MyName}
546+
C:
547+
Value: !Sub ${A.Name}
548+
549+
The module has:
550+
551+
Inputs:
552+
Name:
553+
Type: String
554+
Resources:
555+
X:
556+
Properties:
557+
Y: !Ref Name
558+
References:
559+
MyName: !GetAtt Y.Name
560+
561+
The resulting output:
562+
B: !Sub ${AX.Name}
563+
C: foo
564+
565+
"""
566+
567+
resolved = "${" + w + "}"
568+
tokens = w.split(".", 1)
569+
if len(tokens) < 2:
570+
msg = f"GetAtt {w} has unexpected number of tokens"
571+
raise exceptions.InvalidModuleError(msg=msg)
572+
573+
# !Sub ${Content.BucketArn} -> !Sub ${ContentBucket.Arn}
574+
575+
r = None
576+
if tokens[0] == self.name:
577+
if tokens[1] in self.references:
578+
# We're referring to the module's References
579+
r = self.references[tokens[1]]
580+
elif tokens[1] in self.props:
581+
# We're referring to parent Module Properties
582+
r = self.props[tokens[1]]
583+
if r is not None:
584+
if GETATT in r:
585+
getatt = r[GETATT]
586+
resolved = "${" + self.name + ".".join(getatt) + "}"
587+
elif SUB in r:
588+
resolved = "${" + self.name + r[SUB] + "}"
589+
elif REF in r:
590+
resolved = "${" + self.name + r[REF] + "}"
591+
else:
592+
# Handle scalar properties
593+
resolved = r
594+
595+
return resolved
596+
597+
def resolve_reference_sub(self, v, d, n):
598+
"Resolve a Sub that refers to a module reference or property"
532599
words = parse_sub(v, True)
533600
sub = ""
534601
for word in words:
@@ -537,33 +604,21 @@ def resolve_output_sub(self, v, d, n):
537604
elif word.t == WordType.AWS:
538605
sub += "${AWS::" + word.w + "}"
539606
elif word.t == WordType.REF:
540-
# A reference to an output has to be a getatt
607+
# A ref to a module reference has to be a getatt
541608
resolved = "${" + word.w + "}"
542609
sub += resolved
543610
elif word.t == WordType.GETATT:
544-
resolved = "${" + word.w + "}"
545-
tokens = word.w.split(".", 1)
546-
if len(tokens) < 2:
547-
msg = f"GetAtt {word.w} has unexpected number of tokens"
548-
raise exceptions.InvalidModuleError(msg=msg)
549-
# !Sub ${Content.BucketArn} -> !Sub ${ContentBucket.Arn}
550-
if tokens[0] == self.name and tokens[1] in self.references:
551-
output = self.references[tokens[1]]
552-
if GETATT in output:
553-
getatt = output[GETATT]
554-
resolved = "${" + self.name + ".".join(getatt) + "}"
555-
elif SUB in output:
556-
resolved = "${" + self.name + output[SUB] + "}"
557-
elif REF in output:
558-
resolved = "${" + self.name + output[REF] + "}"
559-
sub += resolved
611+
sub += self.resolve_reference_sub_getatt(word.w)
560612

561-
d[n] = {SUB: sub}
613+
if is_sub_needed(sub):
614+
d[n] = {SUB: sub}
615+
else:
616+
d[n] = sub
562617

563618
# pylint:disable=too-many-branches
564-
def resolve_output_getatt(self, v, d, n):
619+
def resolve_reference_getatt(self, v, d, n):
565620
"""
566-
Resolve a GetAtt that refers to a module output.
621+
Resolve a GetAtt that refers to a module Reference.
567622
568623
:param v The value
569624
:param d The dictionary
@@ -575,20 +630,25 @@ def resolve_output_getatt(self, v, d, n):
575630
msg = f"GetAtt {v} invalid"
576631
raise exceptions.InvalidModuleError(msg=msg)
577632

578-
if v[0] == self.name and v[1] in self.references:
579-
output = self.references[v[1]]
580-
if REF in output:
581-
ref = output[REF]
633+
r = None
634+
if v[0] == self.name:
635+
if v[1] in self.references:
636+
r = self.references[v[1]]
637+
elif v[1] in self.props:
638+
r = self.props[v[1]]
639+
if r is not None:
640+
if REF in r:
641+
ref = r[REF]
582642
d[n] = {REF: self.name + ref}
583-
elif GETATT in output:
584-
getatt = output[GETATT]
643+
elif GETATT in r:
644+
getatt = r[GETATT]
585645
if len(getatt) < 2:
586646
msg = f"GetAtt {getatt} in Output {v[1]} is invalid"
587647
raise exceptions.InvalidModuleError(msg=msg)
588648
d[n] = {GETATT: [self.name + getatt[0], getatt[1]]}
589-
elif SUB in output:
590-
# Parse the Sub in the module output
591-
words = parse_sub(output[SUB], True)
649+
elif SUB in r:
650+
# Parse the Sub in the module reference
651+
words = parse_sub(r[SUB], True)
592652
sub = ""
593653
for word in words:
594654
if word.t == WordType.STR:
@@ -612,8 +672,10 @@ def resolve_output_getatt(self, v, d, n):
612672
if tokens[0] in self.resources:
613673
resolved = "${" + self.name + word.w + "}"
614674
sub += resolved
615-
616675
d[n] = {SUB: sub}
676+
else:
677+
# Handle scalars in Properties
678+
d[n] = r
617679

618680
def validate_overrides(self):
619681
"Make sure resources referenced by overrides actually exist"

tests/unit/customizations/cloudformation/modules/getatt-expect.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
References:
1+
Outputs:
22
BucketName:
33
Value:
44
Fn::GetAtt:
55
- ContentBucket
66
- BucketName
7+
PropGetAtt:
8+
Value: foo
9+
PropSub:
10+
Value: foo
711
Resources:
812
ContentBucket:
913
Type: AWS::S3::Bucket

tests/unit/customizations/cloudformation/modules/getatt-template.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ Modules:
44
Source: ./getatt-module.yaml
55
Properties:
66
Name: foo
7-
References:
7+
Outputs:
88
BucketName:
99
Value: !GetAtt Content.BucketName
10+
PropGetAtt:
11+
Value: !GetAtt Content.Name
12+
PropSub:
13+
Value: !Sub ${Content.Name}

tests/unit/customizations/cloudformation/modules/output-expect.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Resources:
1818
Properties:
1919
AName:
2020
Fn::Sub: another-${ContentBucket.Arn}
21-
References:
21+
Outputs:
2222
ExampleOutput:
2323
Value:
2424
Fn::GetAtt:

tests/unit/customizations/cloudformation/modules/output-template.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Resources:
1515
Object:
1616
Arn: !Sub ${Content.BucketArn}
1717
ArnGetAtt: !GetAtt Content.BucketArn
18-
References:
18+
Outputs:
1919
ExampleOutput:
2020
Value: !GetAtt Content.BucketArn
2121
ExampleSub:

0 commit comments

Comments
 (0)