Skip to content
This repository has been archived by the owner on Apr 16, 2022. It is now read-only.

Issue #95 - support for user-defined resource attribute values. #142

Merged
merged 4 commits into from
May 6, 2018

Conversation

RazzM13
Copy link
Contributor

@RazzM13 RazzM13 commented Apr 13, 2018

Added support for user-defined custom resource attribute values.

@RazzM13
Copy link
Contributor Author

RazzM13 commented Apr 13, 2018

The reason for the Fn::GetAtt for an arbitrary attribute on a custom resource should return a mock result test failing is that cfn-lint does not anymore attempt to guess the custom resource attribute's value as it is expecting it to be provided on the command line.

One obvious course of action would be to copy the behaviour of the guess-parameters functionality and implement additional options, such as --guess-custom-resource-attributes, --no-guess-custom-resource-attributes and --only-guess-custom-resource-attributes.

What do you think @martysweet, @akdor1154 and anyone else?

@RazzM13 RazzM13 changed the title Fixes #95 - support for user-defined custom resource attribute values. Issue #95 - support for user-defined custom resource attribute values. Apr 13, 2018
@martysweet
Copy link
Owner

What happens if multiple custom resources are present in one template?

@RazzM13
Copy link
Contributor Author

RazzM13 commented Apr 20, 2018

In hope that this is what you're referring to, let's assume the following template:

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  AllSecurityGroups:
    Type: Custom::Split
    Properties:
      ServiceToken: "somefunctionarn"
  AllSecurityGroups2:
    Type: Custom::Split
    Properties:
      ServiceToken: "somefunctionarn2"

Outputs:
  AllSecurityGroups:
    Description: Security Groups that are associated with the EC2 instance
    Value:
      Fn::Join:
      - ", "
      - Fn::GetAtt:
        - AllSecurityGroups
        - Value
  AllSecurityGroups2:
    Description: Security Groups that are associated with the EC2 instance
    Value:
      Fn::Join:
      - ", "
      - Fn::GetAtt:
        - AllSecurityGroups2
        - Value
  1. If we attempt to validate without any custom resource attribute defined, we get:
$ cfn-lint validate 2xcustom.yaml
0 infos
0 warn
4 crit
Resource: Outputs > AllSecurityGroups > Value
Message: Value for custom resource attribute was not provided
Documentation: http://docs.aws.amazon.com/search/doc-search.html?searchPath=documentation-guide&searchQuery=Fn::GetAtt&x=0&y=0&this_doc_product=AWS+CloudF
ormation&this_doc_guide=User+Guide&doc_locale=en_us#facet_doc_product=AWS%20CloudFormation&facet_doc_guide=User%20Guide

Resource: Outputs > AllSecurityGroups > Value
Message: Invalid parameters for Fn::Join
Documentation: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-join.html

Resource: Outputs > AllSecurityGroups2 > Value
Message: Value for custom resource attribute was not provided
Documentation: http://docs.aws.amazon.com/search/doc-search.html?searchPath=documentation-guide&searchQuery=Fn::GetAtt&x=0&y=0&this_doc_product=AWS+CloudF
ormation&this_doc_guide=User+Guide&doc_locale=en_us#facet_doc_product=AWS%20CloudFormation&facet_doc_guide=User%20Guide

Resource: Outputs > AllSecurityGroups2 > Value
Message: Invalid parameters for Fn::Join
Documentation: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-join.html

Template invalid!
  1. If we attempt to validate but we specify a single custom resource attribute, we get:
$ cfn-lint validate 2xcustom.yaml -c AllSecurityGroups.Value=[1\\,2\\,3]
0 infos
0 warn
2 crit
Resource: Outputs > AllSecurityGroups2 > Value
Message: Value for custom resource attribute was not provided
Documentation: http://docs.aws.amazon.com/search/doc-search.html?searchPath=documentation-guide&searchQuery=Fn::GetAtt&x=0&y=0&this_doc_product=AWS+CloudF
ormation&this_doc_guide=User+Guide&doc_locale=en_us#facet_doc_product=AWS%20CloudFormation&facet_doc_guide=User%20Guide

Resource: Outputs > AllSecurityGroups2 > Value
Message: Invalid parameters for Fn::Join
Documentation: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-join.html

Template invalid!
  1. If we attempt to validate and we do specify both custom resouce attribute values correctly:
$ cfn-lint validate 2xcustom.yaml -c AllSecurityGroups.Value=[1\\,2\\,3],AllSecurityGroups2.Value=[4\\,5\\,6]
0 infos
0 warn
0 crit
Template valid!
  1. If we attempt to validate and we don't correctly specify one of the custom resource attribute values:
$ cfn-lint validate 2xcustom.yaml -c AllSecurityGroups.Value=[1\\,2\\,3],AllSecurityGroups2.Value=[4\\,5\\,6],AllSecurityGroups.Value
=gigi
0 infos
0 warn
1 crit
Resource: Outputs > AllSecurityGroups > Value
Message: Invalid parameters for Fn::Join
Documentation: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-join.html

Template invalid!

As you can see from the previous examples, the value of the custom-resource-attributes argument is used to build object representations of the specified custom resources and the properties of the aforementioned are updated based on the order of their occurence during processing therefore, AFAICT, there could be any number of individual custom resources and a multitude of attributes for each them and as long as they depict the contextually correct type of value and are not overriden with an incorrect one, by mistake, then everything should just-work(TM) 😈

PS: the above examples are with all the relevant PR's merged.

@RazzM13
Copy link
Contributor Author

RazzM13 commented Apr 25, 2018

I've just managed to merge the current master into this branch but we still have the above mentioned test failling, @martysweet please advise 😁?

@akdor1154
Copy link
Contributor

If I understand correctly (PR has no added tests and no docs, wink wink) this lets the user specify the attributes to mock by the logical resource name?
I would find it more useful to mock by the custom resource type. Not sure if this matches your expected usage.
Also maybe (not quite sure of Marty's normal git workflow) it would be better to rebase on master instead of merging master?

git checkout -b fn_getatt_custom_resource_saved # just in case

# update your master
git checkout master
git fetch upstream master
git reset --hard upstream/master

# rebase your branch
git checkout fn_getatt_custom_resource
git reset --hard c02edb5 # reset to before your merge commits
git rebase master # rebase on master
# you might need to fix conflicts here, git will walk you through it. Generally just
#  edit conflicting files
#  git add file
#  git rebase --continue

If something goes wrong your branch as now is saved as fn_getatt_custom_resource_saved.

@RazzM13 RazzM13 force-pushed the fn_getatt_custom_resource branch from 10ad14b to 13487b5 Compare April 26, 2018 06:44
@RazzM13
Copy link
Contributor Author

RazzM13 commented Apr 26, 2018

Hi @akdor1154, thank you very much for the rebasing advice, I've adjusted the PR so that the changes are finally legible 😁

In regards, to the lack of documentation and tests, I've intentionally omitted them due to the fact that I wasn't really sure if my implementation was adequate and I was envisioning this PR to be more of a preliminary version / collaborative effort than a penultimatum.

Last but not least, you are correct, my vision for the -c command-line option is for it to accept lists of values and each value to be of the format: LogicalResourceName.PropertyName=SomeValue; I believe that this coincides with the current behaviour of the --parameters and --pseudo options and helps provide overall consistency.

Now, AFAICT and please correct me if I'm wrong, you are suggesting that the aforementioned value format should be something like: CustomResourceType.PropertyName=SomeValue. From my limited POV, this approach does seem more appealing because it allows a user to specifiy a mock value that could be applicable to a bulk of resources however, it does raises the dilemma of how to distinguish the CustomResourceType at runtime due to the fact that it can't be determined simply by checking the resource Type attribute's value, as it can have multiple forms such as AWS::CloudFormation::CustomResource and Custom::String (String can be any custom value), and probably there could also be a distinction between custom resources of the same type based on the ServiceToken attribute's value, we might be able to use something similar to CustomResourceType<ServiceToken>.PropertyName=SomeValue or ServiceToken.PropertyName=SomeValue, what are your thoughts on this?

@martysweet
Copy link
Owner

So we also have #149 "AWS::CloudFormation::Stack", which raises the need for other resources which might have variable attribute output - yay CFN Specification! This should no longer be focused on just Custom resource types. I'm sure there are other resources which probably have variable attribute output.

I feel overriding a whole type can be useful in some scenarios, but heavy-handed in others, so perhaps this calls for a dynamic way to give power users the flexibility they desire.

-c MyLambdaCustom.Attribute1="gg",MyCFNStack.VPCID="vpc-abcde"

-c AWS::CustomResource.Attribute1="abc"

In fact, we could allow both on a single cli parameter, as a logical name can never be the same as a resource name - a simple regex of allowed logical name characters can confirm this.

Then it would be

make map of -c parameters
   get all resource type overrides
   add to cfn specification object for this run, derive type from given values
   store a default value somewhere? maybe for reference at validation time?

At validation time for !GetAtt

  check if any custom attributes have been given to this resource directly use that value
  otherwise check the cfn spec - apply any default value if stored
  do magic

@martysweet martysweet added this to the v1.7.0 milestone Apr 26, 2018
@RazzM13
Copy link
Contributor Author

RazzM13 commented Apr 26, 2018

Could this be an augmented version of --pseudo parameters and perhaps replace it?

@akdor1154
Copy link
Contributor

re types - yeah. I was actually thinking about the arbitrary part of the Type: Custom::TypeName (String in your example), but this only aligns with my usage, there is nothing forcing other cfn users to write their templates this way. I expect a fair amount of them do though. The service token of course would also be a way to do this.

Overriding per name would be also be a nice general purpose way for users to be able to do Stack Attributes though, maybe it's worth implementing both ways.

Not sure how useful it would be though, as it means all of your templates would need special attention for cfn-lint to work with them. Targeting by type means your could preconfigure cfn-lint in a script or something (I have it wrapped in something similar) to already know about all your custom resources.

@martysweet
Copy link
Owner

all of your templates would need special attention for cfn-lint to work with them

This is certainly something I want to avoid, I don't want any learning curve to using the tool, so assumptions (like we currently have) would probably be the best way to go.

Most users, unless they are aware of the advanced techniques they are using, will be returning strings from custom attributes, so this should be assumed if there is no additional CLI information to suggest otherwise.

@RazzM13
Copy link
Contributor Author

RazzM13 commented Apr 27, 2018

AFAICT, everyone agrees that the way to move forward with this is by allowing user-defined attributes to be set at the resource-Type level (something similar to what @martysweet suggested above) and perhaps if it's not too difficult and doesn't impose any special attention on behalf of the user at the resource-Name level too. Hope I got that right if not then please advise.

PS: I am currently experimenting with the resource-Type version ... so stay tuned, more to follow 😁

@RazzM13 RazzM13 force-pushed the fn_getatt_custom_resource branch from 93d74b2 to f90fba7 Compare April 27, 2018 16:34
@RazzM13 RazzM13 changed the title Issue #95 - support for user-defined custom resource attribute values. Issue #95 - support for user-defined resource attribute values. Apr 27, 2018
@RazzM13
Copy link
Contributor Author

RazzM13 commented Apr 27, 2018

I've just managed to write up the initial version for user-defined resource attribute values that are matched based on the resource type. How does this look?

@RazzM13
Copy link
Contributor Author

RazzM13 commented Apr 27, 2018

In hopes that what I've just done is ok, a short summary of what remains:

  • investigate and implement user-defined attributes for logical resource names, perhaps they may take precedence over resource-type based ones;
  • infer a runtime value (potentially string) for custom resources that the user has not defined attributes for, which will fix the currently failling test;
  • write tests and docs.

@akdor1154, @martysweet if you could both verify that I'm not missing something from this list and please advise if you would prefer a certain behaviour.

@martysweet
Copy link
Owner

The approach seems sane. It would be good if we can emit an info for usability when assuming values for backwards compatibility.

A logical resource attr should take precedence over a base resource type attr.

@RazzM13
Copy link
Contributor Author

RazzM13 commented May 4, 2018

Hi @martysweet and @akdor1154, sorry for taking so long with this but I was working on something else that sparked my personal curiosity, which I'll be sharing with you shortly.
In regards to the previous commits, I've made a few changes such as dropping deep-assign as some weird issues were occuring and I've just noticed that it has been deprecated.
Please let me know if any changes need to be made.

@RazzM13 RazzM13 force-pushed the fn_getatt_custom_resource branch 3 times, most recently from d79a0d6 to edc5c76 Compare May 4, 2018 16:04
Copy link
Owner

@martysweet martysweet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple of things. @akdor1154 Any thoughts?

README.md Outdated
@@ -73,6 +73,11 @@ Template invalid!
`--only-guess-parameters <param names>`: Only guess the provided parameters, and disable the guessing of all others without Defaults. A critical error will be raised for missing parameters, as above. e.g.
- `--only-guess-parameters InstanceType,Memory`

`--custom-resource-attributes <attribute values>`: Provide a list of comma-separated key=value pairs of resource attributes using either resource-type notation (e.g. `AWS::CloudFormation::
CustomResource.SomeAttribute`) or logical-name notation (e.g. `SomethingPretty.SomeAttribute`) and their expected values, to use when validating your template. If a custom resource attribute is not specified here, `cfn-lint` will guess a mock value. e.g.
- `--custom-resource-attributes AWS::CloudFormation::
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't format properly, is the bullet point is in the wrong place?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a few others which don't format correctly. Leave this and I will clean it up afterwards

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.

@@ -82,6 +84,18 @@ program
}
}

if(cmd.customResourceAttributes){
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need tests for this block

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests added in b0c9a22. Please let me know if this new style is ok otherwise I'll change it to reflect the others.

if (!specification.ResourceTypes[type]) {
type = 'AWS::CloudFormation::CustomResource';
}
return specification.ResourceTypes[type]!;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does the cheeky ! do on the end of this?

Copy link
Contributor Author

@RazzM13 RazzM13 May 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch, the cheeky ! disables validation as Typescript believes that type can be undefined or at least non-existent in ResourceTypes however, this should not occur in this particular scenario. I actually borrowed it from the previous code.

src/validator.ts Outdated
};

// extend defintion for existing resource type
if (RegExp(/::/).test(resource)) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use indexof() for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why not ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now I remember, by using indexOf we either have to use!!~ or == -1, which both seem kind of cheeky to me ;) but sure I'll change it now.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://xkcd.com/1171/ ;)

str.indexOf("::") > -1 ?

I dunno, @akdor1154 any suggestions on the neatest/cleanest way of simple substring checking in TS?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:)

@martysweet
Copy link
Owner

@RazzM13 I will be looking at merging this and publishing v1.7.0, probably on Sunday.

package.json Outdated
"opn": "^5.2.0",
"winston": "^2.4.0",
"source-map-support": "^0.5.0",
"safe-buffer": "^5.1.1"
"safe-buffer": "^5.1.1",
"proxyquire-2": "^1.0.7"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a devdependancy?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Affirmative, thank you.

@RazzM13 RazzM13 force-pushed the fn_getatt_custom_resource branch 2 times, most recently from 9c0c773 to c7b2f03 Compare May 6, 2018 18:39
@RazzM13 RazzM13 force-pushed the fn_getatt_custom_resource branch from c7b2f03 to 601674d Compare May 6, 2018 18:46
@RazzM13
Copy link
Contributor Author

RazzM13 commented May 6, 2018

I believe I've finally managed to integrate master without losing history :)

@martysweet martysweet merged commit 00aaaed into martysweet:master May 6, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants