Skip to content

Commit 86f2714

Browse files
authored
fix(cfn-diff): correctly handle Date strings in diff (#16591)
Turns out, `parseFloat()` in JavaScript is even crazier than we thought, and returns nonsense like `2021` for a string containing a Date like `'2021-10-25'`. For that reason, add an explicit check that the string parsed looks like a number before calling `parseFloat()`. Fixes #16444 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent ef7e20d commit 86f2714

File tree

3 files changed

+27
-50
lines changed

3 files changed

+27
-50
lines changed

packages/@aws-cdk/cloudformation-diff/lib/diff-template.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,17 @@ function calculateTemplateDiff(currentTemplate: { [key: string]: any }, newTempl
9999
for (const key of unionOf(Object.keys(currentTemplate), Object.keys(newTemplate)).sort()) {
100100
const oldValue = currentTemplate[key];
101101
const newValue = newTemplate[key];
102-
if (deepEqual(oldValue, newValue)) { continue; }
102+
if (deepEqual(oldValue, newValue)) {
103+
continue;
104+
}
103105
const handler: DiffHandler = DIFF_HANDLERS[key]
104106
|| ((_diff, oldV, newV) => unknown[key] = impl.diffUnknown(oldV, newV));
105107
handler(differences, oldValue, newValue);
106108

107109
}
108-
if (Object.keys(unknown).length > 0) { differences.unknown = new types.DifferenceCollection(unknown); }
110+
if (Object.keys(unknown).length > 0) {
111+
differences.unknown = new types.DifferenceCollection(unknown);
112+
}
109113

110114
return new types.TemplateDiff(differences);
111115
}

packages/@aws-cdk/cloudformation-diff/lib/diff/util.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -138,20 +138,10 @@ export function unionOf(lv: string[] | Set<string>, rv: string[] | Set<string>):
138138
* A parseFloat implementation that does the right thing for
139139
* strings like '0.0.0'
140140
* (for which JavaScript's parseFloat() returns 0).
141+
* We return NaN for all of these strings that do not represent numbers,
142+
* and so comparing them fails,
143+
* and doesn't short-circuit the diff logic.
141144
*/
142145
function safeParseFloat(str: string): number {
143-
const ret = parseFloat(str);
144-
const nonNumericRegex = /\d*\.\d+\./;
145-
if (ret === 0) {
146-
// if the str is exactly '0', that's OK;
147-
// but parseFloat() also returns 0 for things like '0.0';
148-
// in this case, return NaN, so we'll fall back to string comparison
149-
return str === '0' ? ret : NaN;
150-
} else if (nonNumericRegex.test(str)) {
151-
// if the str contains non-numeric characters,
152-
// return NaN, so we'll fall back to string comparison
153-
return NaN;
154-
} else {
155-
return ret;
156-
}
146+
return Number(str);
157147
}

packages/@aws-cdk/cloudformation-diff/test/diff-template.test.ts

Lines changed: 17 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -582,84 +582,67 @@ test('when a property changes including equivalent DependsOn', () => {
582582
expect(differences.resources.differenceCount).toBe(1);
583583
});
584584

585-
test('when a property with a number-like format changes', () => {
586-
const bucketName = 'ShineyBucketName';
587-
const tagChanges = {
588-
'0.31.1-prod': '0.31.2-prod',
589-
'8.0.5.5.4-identifier': '8.0.5.5.5-identifier',
590-
'1.1.1.1': '1.1.2.2',
591-
'1.2.3': '1.2.4',
592-
'2.2.2.2': '2.2.3.2',
593-
'3.3.3.3': '3.4.3.3',
594-
};
595-
const oldTags = Object.keys(tagChanges);
596-
const newTags = Object.values(tagChanges);
585+
test.each([
586+
['0.31.1-prod', '0.31.2-prod'],
587+
['8.0.5.5.4-identifier', '8.0.5.5.5-identifier'],
588+
['1.1.1.1', '1.1.1.2'],
589+
['1.2.3', '1.2.4'],
590+
['2.2.2.2', '2.2.3.2'],
591+
['3.3.3.3', '3.4.3.3'],
592+
['2021-10-23T06:07:08.000Z', '2021-10-23T09:10:11.123Z'],
593+
])("reports a change when a string property with a number-like format changes from '%s' to '%s'", (oldValue, newValue) => {
594+
// GIVEN
597595
const currentTemplate = {
598596
Resources: {
599-
QueueResource: {
600-
Type: 'AWS::SQS::Queue',
601-
},
602597
BucketResource: {
603598
Type: 'AWS::S3::Bucket',
604599
Properties: {
605-
BucketName: bucketName,
606-
Tags: oldTags,
600+
Tags: [oldValue],
607601
},
608602
},
609603
},
610604
};
611605
const newTemplate = {
612606
Resources: {
613-
QueueResource: {
614-
Type: 'AWS::SQS::Queue',
615-
},
616607
BucketResource: {
617608
Type: 'AWS::S3::Bucket',
618609
Properties: {
619-
BucketName: bucketName,
620-
Tags: newTags,
610+
Tags: [newValue],
621611
},
622612
},
623613
},
624614
};
625-
615+
// WHEN
626616
const differences = diffTemplate(currentTemplate, newTemplate);
617+
618+
// THEN
627619
expect(differences.differenceCount).toBe(1);
628620
expect(differences.resources.differenceCount).toBe(1);
629621
const difference = differences.resources.changes.BucketResource;
630622
expect(difference).not.toBeUndefined();
631623
expect(difference?.oldResourceType).toEqual('AWS::S3::Bucket');
632624
expect(difference?.propertyUpdates).toEqual({
633-
Tags: { oldValue: oldTags, newValue: newTags, changeImpact: ResourceImpact.WILL_UPDATE, isDifferent: true },
625+
Tags: { oldValue: [oldValue], newValue: [newValue], changeImpact: ResourceImpact.WILL_UPDATE, isDifferent: true },
634626
});
635627
});
636628

637629
test('when a property with a number-like format doesn\'t change', () => {
638-
const bucketName = 'ShineyBucketName';
639630
const tags = ['0.31.1-prod', '8.0.5.5.4-identifier', '1.1.1.1', '1.2.3'];
640631
const currentTemplate = {
641632
Resources: {
642-
QueueResource: {
643-
Type: 'AWS::SQS::Queue',
644-
},
645633
BucketResource: {
646634
Type: 'AWS::S3::Bucket',
647635
Properties: {
648-
BucketName: bucketName,
649636
Tags: tags,
650637
},
651638
},
652639
},
653640
};
654641
const newTemplate = {
655642
Resources: {
656-
QueueResource: {
657-
Type: 'AWS::SQS::Queue',
658-
},
659643
BucketResource: {
660644
Type: 'AWS::S3::Bucket',
661645
Properties: {
662-
BucketName: bucketName,
663646
Tags: tags,
664647
},
665648
},
@@ -671,4 +654,4 @@ test('when a property with a number-like format doesn\'t change', () => {
671654
expect(differences.resources.differenceCount).toBe(0);
672655
const difference = differences.resources.changes.BucketResource;
673656
expect(difference).toBeUndefined();
674-
});
657+
});

0 commit comments

Comments
 (0)