def parameter_override_click_option(): return click.option( "--parameter-overrides", type=CfnParameterOverridesType(), help= "Optional. A string that contains CloudFormation parameter overrides encoded as key=value " "pairs. Use the same format as the AWS CLI, e.g. 'ParameterKey=KeyPairName," "ParameterValue=MyKey ParameterKey=InstanceType,ParameterValue=t1.micro'", )
def parameter_override_click_option(): return click.option( "--parameter-overrides", cls=OptionNargs, type=CfnParameterOverridesType(), default={}, help="Optional. A string that contains AWS CloudFormation parameter overrides encoded as key=value pairs." "For example, 'ParameterKey=KeyPairName,ParameterValue=MyKey ParameterKey=InstanceType," "ParameterValue=t1.micro' or KeyPairName=MyKey InstanceType=t1.micro", )
def setUp(self): self.param_type = CfnParameterOverridesType()
class TestCfnParameterOverridesType(TestCase): def setUp(self): self.param_type = CfnParameterOverridesType() @parameterized.expand([ ("some string"), # Key must not contain spaces ('ParameterKey="Ke y",ParameterValue=Value'), # No value ("ParameterKey=Key,ParameterValue="), # No key ("ParameterKey=,ParameterValue=Value"), # Case sensitive ("parameterkey=Key,ParameterValue=Value"), # No space after comma ("ParameterKey=Key, ParameterValue=Value"), # Bad separator ("ParameterKey:Key,ParameterValue:Value"), ]) def test_must_fail_on_invalid_format(self, input): self.param_type.fail = Mock() self.param_type.convert(input, "param", "ctx") self.param_type.fail.assert_called_with(ANY, "param", "ctx") @parameterized.expand([ ( "ParameterKey=KeyPairName,ParameterValue=MyKey ParameterKey=InstanceType,ParameterValue=t1.micro", { "KeyPairName": "MyKey", "InstanceType": "t1.micro" }, ), ('ParameterKey="Key",ParameterValue=Val\\ ue', { "Key": "Val ue" }), ('ParameterKey="Key",ParameterValue="Val\\"ue"', { "Key": 'Val"ue' }), ("ParameterKey=Key,ParameterValue=Value", { "Key": "Value" }), ('ParameterKey=Key,ParameterValue=""', { "Key": "" }), ( # Trailing and leading whitespaces " ParameterKey=Key,ParameterValue=Value ParameterKey=Key2,ParameterValue=Value2 ", { "Key": "Value", "Key2": "Value2" }, ), ( # Quotes at the end 'ParameterKey=Key,ParameterValue=Value\\"', { "Key": 'Value"' }, ), ( # Quotes at the start 'ParameterKey=Key,ParameterValue=\\"Value', { "Key": '"Value' }, ), ( # Value is spacial characters "ParameterKey=Key,ParameterValue==-_)(*&^%$#@!`~:;,. ParameterKey=Key2,ParameterValue=Value2", { "Key": "=-_)(*&^%$#@!`~:;,.", "Key2": "Value2" }, ), ('ParameterKey=Key1230,ParameterValue="{\\"a\\":\\"b\\"}"', { "Key1230": '{"a":"b"}' }), ( # Must ignore empty inputs "", {}, ), ]) def test_successful_parsing(self, input, expected): result = self.param_type.convert(input, None, None) self.assertEqual(result, expected, msg="Failed with Input = " + input)
class TestCfnParameterOverridesType(TestCase): def setUp(self): self.param_type = CfnParameterOverridesType() @parameterized.expand( [ # Random string ("some string",), # Only commas (",,",), # Bad separator ("ParameterKey:Key,ParameterValue:Value",), ] ) def test_must_fail_on_invalid_format(self, input): self.param_type.fail = Mock() self.param_type.convert(input, "param", "ctx") self.param_type.fail.assert_called_with(ANY, "param", "ctx") @parameterized.expand( [ # No enclosing quotes and non escaped quotes in values. ( ( "DeployStackName=new-stack " 'DeployParameterOverrides="{"bucketName":"production","bucketRegion":"eu-west-1"}" ' 'DeployParameterBucketOverrides="{"bucketName":"myownbucket"}"' ), { "DeployStackName": "new-stack", "DeployParameterOverrides": "{", "DeployParameterBucketOverrides": "{", }, ) ] ) def test_unsupported_formats(self, input, expected): result = self.param_type.convert(input, None, None) self.assertEqual(result, expected, msg="Failed with Input = " + str(input)) @parameterized.expand( [ ( ("ParameterKey=KeyPairName,ParameterValue=MyKey ParameterKey=InstanceType,ParameterValue=t1.micro",), {"KeyPairName": "MyKey", "InstanceType": "t1.micro"}, ), (("KeyPairName=MyKey InstanceType=t1.micro",), {"KeyPairName": "MyKey", "InstanceType": "t1.micro"}), (("KeyPairName=MyKey, InstanceType=t1.micro,",), {"KeyPairName": "MyKey,", "InstanceType": "t1.micro,"}), (('ParameterKey="Ke y",ParameterValue=Value',), {"ParameterKey": "Ke y"}), ((("ParameterKey=Key,ParameterValue="),), {"ParameterKey": "Key,ParameterValue="}), (('ParameterKey="Key",ParameterValue=Val\\ ue',), {"Key": "Val ue"}), (('ParameterKey="Key",ParameterValue="Val\\"ue"',), {"Key": 'Val"ue'}), (("ParameterKey=Key,ParameterValue=Value",), {"Key": "Value"}), (('ParameterKey=Key,ParameterValue=""',), {"Key": ""}), ( # Trailing and leading whitespaces (" ParameterKey=Key,ParameterValue=Value ParameterKey=Key2,ParameterValue=Value2 ",), {"Key": "Value", "Key2": "Value2"}, ), ( # Quotes at the end ('ParameterKey=Key,ParameterValue=Value\\"',), {"Key": 'Value"'}, ), ( # Quotes at the start ('ParameterKey=Key,ParameterValue=\\"Value',), {"Key": '"Value'}, ), ( # Value is spacial characters ("ParameterKey=Key,ParameterValue==-_)(*&^%$#@!`~:;,. ParameterKey=Key2,ParameterValue=Value2",), {"Key": "=-_)(*&^%$#@!`~:;,.", "Key2": "Value2"}, ), (('ParameterKey=Key1230,ParameterValue="{\\"a\\":\\"b\\"}"',), {"Key1230": '{"a":"b"}'}), (('Key=Key1230 Value="{\\"a\\":\\"b\\"}"',), {"Key": "Key1230", "Value": '{"a":"b"}'}), ( ( 'Key=Key1230 Value="{\\"a\\":\\"b\\"}" ' 'Key1=Key1230 Value1="{\\"a\\":\\"b\\"}" ' 'Key2=Key1230 Value2="{\\"a\\":\\"b\\"}"', ), { "Key": "Key1230", "Value": '{"a":"b"}', "Key1": "Key1230", "Value1": '{"a":"b"}', "Key2": "Key1230", "Value2": '{"a":"b"}', }, ), ( ( "DeployStackName=new-stack " 'DeployParameterOverrides="{\\"bucketName\\":\\"production\\",\\"bucketRegion\\":\\"eu-west-1\\"}" ' 'DeployParameterBucketOverrides="{\\"bucketName\\":\\"myownbucket\\"}"' ), { "DeployStackName": "new-stack", "DeployParameterOverrides": '{"bucketName":"production","bucketRegion":"eu-west-1"}', "DeployParameterBucketOverrides": '{"bucketName":"myownbucket"}', }, ), ( # Must ignore empty inputs ("",), {}, ), ] ) def test_successful_parsing(self, input, expected): result = self.param_type.convert(input, None, None) self.assertEqual(result, expected, msg="Failed with Input = " + str(input))
def invoke_common_options(f): """ Common CLI options shared by "local invoke" and "local start-api" commands :param f: Callback passed by Click """ invoke_options = [ template_click_option(), click.option( '--env-vars', '-n', type=click.Path(exists=True), help= "JSON file containing values for Lambda function's environment variables." ), click.option( "--parameter-overrides", type=CfnParameterOverridesType(), help= "Optional. A string that contains CloudFormation parameter overrides encoded as key=value " "pairs. Use the same format as the AWS CLI, e.g. 'ParameterKey=KeyPairName," "ParameterValue=MyKey ParameterKey=InstanceType,ParameterValue=t1.micro'" ), click.option( '--debug-port', '-d', help= "When specified, Lambda function container will start in debug mode and will expose this " "port on localhost.", envvar="SAM_DEBUG_PORT"), click.option( '--debugger-path', help= "Host path to a debugger that will be mounted into the Lambda container." ), click.option('--debug-args', help="Additional arguments to be passed to the debugger.", envvar="DEBUGGER_ARGS"), click.option( '--docker-volume-basedir', '-v', envvar="SAM_DOCKER_VOLUME_BASEDIR", help= "Specifies the location basedir where the SAM file exists. If the Docker is running on " "a remote machine, you must mount the path where the SAM file exists on the docker machine " "and modify this value to match the remote machine."), click.option( '--docker-network', envvar="SAM_DOCKER_NETWORK", help= "Specifies the name or id of an existing docker network to lambda docker " "containers should connect to, along with the default bridge network. If not specified, " "the Lambda containers will only connect to the default bridge docker network." ), click.option('--log-file', '-l', help="logfile to send runtime logs to."), click.option( '--skip-pull-image', is_flag=True, help= "Specify whether CLI should skip pulling down the latest Docker image for Lambda runtime.", envvar="SAM_SKIP_PULL_IMAGE"), click.option('--profile', help="Specify which AWS credentials profile to use."), click.option('--region', help="Specify which AWS region to use."), ] # Reverse the list to maintain ordering of options in help text printed with --help for option in reversed(invoke_options): option(f) return f