-
Notifications
You must be signed in to change notification settings - Fork 38
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi David,
Thanks for your contribution to cfn-lint! I have made a few comments below, let me know if you need additional information or you disagree on anything.
I have used https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-select.html for the quotations.
Thanks,
Marty
src/validator.ts
Outdated
} | ||
|
||
let list = toGet[1] | ||
if (!Array.isArray(list)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like CommaDelimitedList should be supported.
Type: CommaDelimitedList
Default: "10.0.48.0/24, 10.0.112.0/24, 10.0.176.0/24"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll be sure to get that in there too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be automatically converted using this lookup https://github.com/martysweet/cfn-lint/blob/master/data/aws_parameter_types.json. So should just be a case of using the array.
There may be some validation issues here https://github.com/martysweet/cfn-lint/blob/master/src/validator.ts#L247-L263 which don't catch invalid CommaDelimitedList provided lists. If this turns into a lot of effort we can split improving that into another PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, how would I go about getting that array? I'm not seeing it in workingInput
, but maybe I'm looking in the wrong place. workingInput['Parameters'][parameterName]['Attributes']['Ref']
is just returning the original string. I could just do a .split(',')
and .trim()
each value in the array from that. But I feel like there is a better way to do that. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's what I added to assignParametersOutput
just after it checks allowedValues
if(parameter['Type'] === "CommaDelimitedList" && typeof parameterValue === 'string') {
parameterValue = parameterValue.split(',').map(x => x.trim());
parameterValue.forEach(val => {
if (val === ""){
addError('crit', `Parameter ${parameterName} contains a CommaDelimitedList where the number of commas appears to be equal or greater than the list of items.`, ['Parameters', parameterName], "Parameters");
}
})
}
on my local build, but I'm sure there is a better way somewhere
@@ -0,0 +1,245 @@ | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Due to the size of these templates, it not particularly clear where the error specified in the test should be occurring. These would be nicer as smaller templates where the test case to fail is easy to spot, and relatively isolated. I.e. no parameters and only features which are required for the test to fail in the specified way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think I'll try to move my tests into something more a like a unit test. That will make it much more clear whats happening.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These look much better!
src/validator.ts
Outdated
if (index >= 0 && index < list.length) { | ||
return list[index]; | ||
} else { | ||
addError('warn', "First element of Fn::Select exceeds the length of the list, if list is a function, make sure it returns a longer list", placeInTemplate, "Fn::Select"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably result in a error instead as it will fail the deployment.
Fn::Select does not check for null values or if the index is out of bounds of the array. Both conditions will result in a stack error, so you should be certain that the index you choose is valid, and that the list contains non-null values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was unsure on that one. Since it was possible that a mocked intrinsic could have supplied a shorter list than would be was expected. Fn::Select I think is recommended for choosing AZs, but it looked like the mocked getAZ was only returning three entries. (I'm not sure about that, I only had a minute to glance over it. - and I'm definitely not an expert that front :) )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I was wondering about that too. It would involve lots of effort to improve the getAZ mock (i.e. integration with the AWS SDK), as you can't guarantee that all account have the same number of AZs in each region.
Therefore, your account might have a different number of available Availability Zones in a region than another account.
We could, however, have a flag to define the amount of AZs you wish to validate against, as we can always expect this to start from a, AFAIK.
If you can think of any other edge cases where making this an "error" would cause annoyance then we might need to think of another solution.
src/validator.ts
Outdated
addError('crit', "Fn::Select requires the second element to resolve to a list, it appears to be a string", placeInTemplate, "Fn::Select"); | ||
return 'INVALID_SELECT'; | ||
} | ||
if (!Array.isArray(list)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we are expecting an Array, can we check for the following property of null entries?
This list must not be null, nor can it have null entries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep.
src/test/validatorTest.ts
Outdated
expect(result).to.have.deep.property('templateValid', false); | ||
expect(result['errors']['crit']).to.have.lengthOf(1); | ||
expect(result['errors']['warn']).to.have.lengthOf(0); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would also be good to ensure the YAML shorthand is working as expected.
AvailabilityZone: !Select
- 0
- !GetAZs
Ref: 'AWS::Region'
AvailabilityZone: !Select
- 0
- Fn::GetAZs: !Ref 'AWS::Region'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep
src/validator.ts
Outdated
if (!Array.isArray(list)) { | ||
//we may need to resolve it | ||
if (typeof list !== 'string') { | ||
list = resolveIntrinsicFunction(list, Object.keys(list)[0]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be some validation around resolving an intrinsic function, this is to support nested functions, see https://github.com/martysweet/cfn-lint/blob/master/src/validator.ts#L894-L897 for an example. This file will need updating with the list of the supported functions from AWS. https://github.com/martysweet/cfn-lint/blob/master/data/aws_intrinsic_functions.json#L13
Supported Functions
For the Fn::Select index value, you can use the Ref and Fn::FindInMap functions.
For the Fn::Select list of objects, you can use the following functions:
Fn::FindInMap
Fn::GetAtt
Fn::GetAZs
Fn::If
Fn::Split
Ref
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for explaining that bit.
Thanks for the feedback! It may be a few days before I can address everything. Thank you for making such a nice product! |
…s, with simplified templates... more to come
Update LA Fork
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good, is that all the functionality that is required? Just a couple of typos and formatting issues below.
src/validator.ts
Outdated
@@ -526,7 +526,9 @@ function resolveIntrinsicFunction(ref: any, key: string) : string | boolean | st | |||
case 'Fn::Not': | |||
return doIntrinsicNot(ref, key); | |||
case 'Fn::ImportValue': | |||
return doIntrinsicImportValue(ref, key); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, can you clean up the spacing issue on here?
src/validator.ts
Outdated
} else if (typeof toGet[0] === 'number'){ | ||
index = toGet[0]; | ||
} else { | ||
addError('crit', `Fn:Select's first arguement must be a number or resolve to a number, it appears to be a ${typeof(toGet[0])}`, placeInTemplate, "Fn::Select"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
arguement
typo
src/validator.ts
Outdated
return 'INVALID_SELECT'; | ||
} | ||
} else if (list.indexOf(null) > -1) { | ||
addError('crit', "FnSelect requires that the list be free of null values", placeInTemplate, "Fn::Select"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FnSelect
typo
src/validator.ts
Outdated
if (index >= 0 && index < list.length) { | ||
return list[index]; | ||
} else { | ||
addError('crit', "First element of Fn::Select exceeds the length of the list, if list is a function, make sure it returns a longer list", placeInTemplate, "Fn::Select"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not too keen on the wording of this.
@@ -0,0 +1,245 @@ | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These look much better!
…e to invalid json, to make sure it is invalid yaml too, as well as checking for null values in the yamlcleanup function
@@ -63,7 +63,7 @@ function cleanupYaml(ref: any){ | |||
let key = Object.keys(ref)[i]; | |||
|
|||
// Resolve the function | |||
if(ref[key].hasOwnProperty('class') && ref[key].hasOwnProperty('data')){ | |||
if( ref[key] !== null && ref[key].hasOwnProperty('class') && ref[key].hasOwnProperty('data')){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found that I needed to make a couple of changes to the parser to block the script from trying to parse out null entries in yaml arrays.
@@ -96,12 +96,12 @@ function cleanupYaml(ref: any){ | |||
ref[key] = {}; | |||
ref[key][outputKeyName] = outputData; | |||
|
|||
}else if(key != 'Attributes' && typeof ref[key] == "object"){ | |||
}else if(key != 'Attributes' && typeof ref[key] == "object" && ref[key] !== null){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And here too.
The result of this was that I had to modify the invalid json doc, so that it would also be invalid yaml
{'some': 'Invalid', 'json' => 0} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the arrow assigment operator and 0 to make it both invalid json and invalid yaml
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be most of it, but I want to do one more thorough combing through of the tests, to make sure I have them all generating the right error for the right reason.
Ok, did another once over on the tests, and made them check for specific responses. |
Great stuff @kcculhwch, thanks! Are you happy for this to be merged? |
Yep, I think this is ready. I didn't update the README.md, yet, to indicate support for Fn::Select, as I figured that might need to wait until the release itself gets created. |
Hi, I'm just taking a stab at implementing Fn::Select here. I'm not sure if this is all in exactly the format you would like. Please let me know if this works, and what I can do to get the code up to par, if not.
Tests included should cover all major failure points, as well as a passing scenario.
Thanks,
David P. Smith
LinuxAcademy.com