Skip to content
This repository has been archived by the owner on Oct 17, 2024. It is now read-only.

Intrisic helper Select redering base64 encoded value #362

Open
apgmckay opened this issue Mar 27, 2021 · 5 comments
Open

Intrisic helper Select redering base64 encoded value #362

apgmckay opened this issue Mar 27, 2021 · 5 comments
Assignees
Labels

Comments

@apgmckay
Copy link

There seems to be an issue with rendering the to JSON or YAML from the Select intrinsic helper function.

See the below render:

    "VPCSubnet2": {
      "Properties": {
        "AvailabilityZone": "eyAiRm46OlNlbGVjdCI6IFsgJ1x4MDAnLCAgImV1LXdlc3QtMWEiIF0gfQ==",
        "CidrBlock": {
          "Ref": "Subnet2CIDR"
        },
        "VpcId": {
          "Ref": "VPC"
        }
      },

which is produced by:

template.Resources["VPCSubnet2"] = &ec2.Subnet{
CidrBlock:        cloudformation.Ref("Subnet2CIDR"),
VpcId:            cloudformation.Ref("VPC"),
AvailabilityZone: cloudformation.Select(0, []string{"eu-west-1a"}),
}

I initially thought this was when you embedded calls to intrisic helpers within one another for example see render:

    "VPCSubnet3": {
      "Properties": {
        "AvailabilityZone": "eyAiRm46OlNlbGVjdCI6IFsgJ1x4MDInLCAgImV5QWlSbTQ2T2tkbGRFRmFjeUk2SUNKbGRTMTNaWE4wTFRFaUlIMD0iIF0gfQ==",
        "CidrBlock": {
          "Ref": "Subnet3CIDR"
        },
        "VpcId": {
          "Ref": "VPC"
        }
      },

Produced from:

	template.Resources["VPCSubnet3"] = &ec2.Subnet{
		CidrBlock:        cloudformation.Ref("Subnet3CIDR"),
		VpcId:            cloudformation.Ref("VPC"),
		AvailabilityZone: cloudformation.Select(2, []string{cloudformation.GetAZs("eu-west-1")}),
	}

However the issue only seems to be when using Select, as calls from GetAZs work as expected, see below:

    "VPCSubnet1": {
      "Properties": {
        "AvailabilityZone": {
          "Fn::GetAZs": "eu-west-1"
        },
        "CidrBlock": {
          "Ref": "Subnet1CIDR"
        },
        "VpcId": {
          "Ref": "VPC"
        }
      },

Produced from:

	template.Resources["VPCSubnet1"] = &ec2.Subnet{
		CidrBlock:        cloudformation.Ref("Subnet1CIDR"),
		VpcId:            cloudformation.Ref("VPC"),
		AvailabilityZone: cloudformation.GetAZs("eu-west-1"),
	}

I'm making use of the goformation JSON() and YAML() render functions and both produce the same resulting hash. Which makes me think the issue is probably somewhere in the Select() codebase.

Please let me know if you need any more detail.

Thanks for the great work so far with goformation!

@tnsardesai
Copy link

Using cloudformation.Select("2", []string{cloudformation.GetAZs("eu-west-1")}) works instead of cloudformation.Select(2, []string{cloudformation.GetAZs("eu-west-1")}), (note 2 int vs string)

This is happening because of %q at https://github.com/awslabs/goformation/blob/master/cloudformation/intrinsics.go#L205-L210 for index.

Reading https://golang.org/pkg/fmt/ see that for integer it says %q a single-quoted character literal safely escaped with Go syntax.

https://gist.github.com/tnsardesai/b697eefd35c09dfeed4cde5eaf535851

{ "Fn::Select": [ '\x00' ] }
not json: 
invalid character '\'' looking for beginning of value

@apgmckay
Copy link
Author

apgmckay commented Apr 2, 2021

That's worked thanks very much for taking the time to look into this @tnsardesai.

@xrn
Copy link
Contributor

xrn commented Jul 18, 2022

@rubenfonseca I had also problems in past with cloudformation.Select(2, and is required cloudformation.Select("2", what do you think about change of

// Select returns a single object from a list of objects by index.
func Select(index interface{}, list []string) string {
	if len(list) == 1 {
		return encode(fmt.Sprintf(`{ "Fn::Select": [ %q,  %q ] }`, index, list[0]))
	}
	return encode(fmt.Sprintf(`{ "Fn::Select": [ %q, [ %v ] ] }`, index, printList(list)))
}

to

// Select returns a single object from a list of objects by index.
func Select(index string, list []string) string {
	if len(list) == 1 {
		return encode(fmt.Sprintf(`{ "Fn::Select": [ %q,  %q ] }`, index, list[0]))
	}
	return encode(fmt.Sprintf(`{ "Fn::Select": [ %q, [ %v ] ] }`, index, printList(list)))
}

or

// Select returns a single object from a list of objects by index.
func Select(index int, list []string) string {
	if len(list) == 1 {
		return encode(fmt.Sprintf(`{ "Fn::Select": [ %v,  %q ] }`, index, list[0]))
	}
	return encode(fmt.Sprintf(`{ "Fn::Select": [ %v, [ %v ] ] }`, index, printList(list)))
}

@rubenfonseca
Copy link
Contributor

Hi everyone, thank you for the input! I was thinking changing the Sprintf template for the index from %q to %v as it would cover a lot of scenarios automatically:

The default format for %v is:
bool: %t
int, int8 etc.: %d
uint, uint8 etc.: %d, %#x if printed with %#v
float32, complex64, etc: %g
string: %s
chan: %p
pointer: %p
For compound objects, the elements are printed using these rules, recursively, laid out like this:
struct: {field0 field1 ...}
array, slice: [elem0 elem1 ...]
maps: map[key1:value1 key2:

Does this make sense to solve this and other problems?

@rubenfonseca rubenfonseca self-assigned this Sep 2, 2022
@xrn
Copy link
Contributor

xrn commented Sep 3, 2022

I believe this is good idea

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

4 participants