Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arrays where they shouldn't be #3

Open
jantman opened this issue Oct 21, 2016 · 15 comments
Open

Arrays where they shouldn't be #3

jantman opened this issue Oct 21, 2016 · 15 comments

Comments

@jantman
Copy link

jantman commented Oct 21, 2016

I'm using -reverse to convert an HCL file, specifically configuration for the HashiCorp Vault Secure Introduction client (a proprietary, Enterprise-only Vault add-on); HCL example here to JSON.

The input is:

environment "aws" {
}

vault {
  address = "https://vault.service.consul:8200"
  mount_path = "auth/aws"
}

serve "file" {
  path = "/ramdisk/vault-token"
}

However, using the current 0.0.6 version, the JSON output is:

  "environment": [
    {
      "aws": [
        {}
      ]
    }
  ],
  "serve": [
    {
      "file": [
        {
          "path": "/ramdisk/vault-token"
        }
      ]
    }
  ],
  "vault": [
    {
      "address": "https://vault.service.consul:8200",
      "mount_path": "auth/aws"
    }
  ]
}

This is detected as invalid by VSI, even though the HCL file works correctly. It appears that the problem is that there are extra arrays inserted. The working JSON from this HCL seems to be:

{
  "environment": {
    "aws": {}
  },
  "serve": {
    "file": {
      "path": "/ramdisk/vault-token"
    }
  },
  "vault": {
    "address": "https://vault.service.consul:8200",
    "mount_path": "auth/aws"
  }
}
@kvz
Copy link
Owner

kvz commented Oct 24, 2016

Thanks for reporting!

We're mostly using the other way around ourselves. We switched to the official hcl repo in #2, and I think this behavior should be reported upstream there, since we're merely a simple wrapper around its printer functions (also see https://github.com/kvz/json2hcl/blob/master/main.go#L40) so I don't think this is something we can/should patch at our level. Sorry I can't be of more help!

@Acconut
Copy link
Collaborator

Acconut commented Oct 24, 2016

Personally, I do not believe that this is a bug or misbehavior of the official hcl package. I spent some time building a function to remove the unnecessary arrays but you will eventually hit some unfortunate situations when creating arrays by repeating the same block, e.g.:

    "basic-dynamodb-table" = {
      "attribute" = {
        "name" = "TopScore"
        "type" = "N"
      }

      "attribute" = {
        "name" = "UserId"
        "type" = "N"
      }
    }

Right now, this will interpreted as

          "basic-dynamodb-table": [
            {
              "attribute": [
                {
                  "name": "TopScore",
                  "type": "N"
                },
                {
                  "name": "UserId",
                  "type": "N"
                }
              ]

However, if we start to remove those arrays, it turns into this:

      "basic-dynamodb-table": {
        "attribute": {
          "name": [
            "TopScore",
            "UserId"
          ],
          "type": [
            "N",
            "N"
          ]
        },

Notice, that basic-dynamodb-table is not an array anymore but an object, which is great. However, the same took place for attribute which is probably not wanted. And the issue is, that the official hcl parser does not supply enough information to us, based on which we could decide whether we should keep it as an array or not. So right now, we are in a bit unfortunate situation where this is not easily achievable.

@jantman
Copy link
Author

jantman commented Oct 27, 2016

Hmm... thanks for the info, @Acconut. I'm going to open an issue upstream nonetheless, and see if they can shed some light (even if it just becomes a tally mark for clarifying the config format used by various tools).

@Acconut
Copy link
Collaborator

Acconut commented Oct 28, 2016

Thank you for bringing this issue up in Hashicorp's repository. I will monitor the discussion and see how we can improve the tool.

@erick-thompson
Copy link

I'm running into a similar issue, but with Terraform. Under the root node, there is a variables key with a value that the tool is turning into an array, when it should be a hash/dictionary. Is this the same core issue?

@Acconut
Copy link
Collaborator

Acconut commented May 10, 2017

@erick-thompson Most likely, yes, but it would be nice if you could supply an example for us to check it.

@erick-thompson
Copy link

Sure. I'll paste a shortened example here. If you need the full files, I can upload them. The HCL is

variable "container_name" {
	default = "insightprofileservice"
}

variable "container_port" {
	default = "8202"
}

module "insightprofileservice_service" {
  source = "<not shown here>"
  cluster_name = "${var.cluster_name}"
  environment = "${var.environment}"
  desired_count = "${var.desired_count}"
  task_policy_file = "ecs_policy.json"
}

When I run the tool with reverse, I get

{
  "module": [
    {
      "insightprofileservice_service": [
        {
          "cluster_name": "${var.cluster_name}",
          "desired_count": "${var.desired_count}",
          "environment": "${var.environment}",
          "source": "\u003cnot shown here\u003e",
          "task_policy_file": "ecs_policy.json"
        }
      ]
    }
  ],
  "variable": [
    {
      "container_name": [
        {
          "default": "insightprofileservice"
        }
      ]
    },
    {
      "container_port": [
        {
          "default": "8202"
        }
      ]
    }
  ]
}

The problem here is that "variable" is set to an array, but terraform expects it to be a hash/dictionary (this is true for all other resource types as well). So if I then convert the json (module omitted for clarity) to:


{
  "variable": {
      "container_name": {
          "default": "insightprofileservice"
      },
      "container_port": {
          "default": "8202"
      }
    }
}

Then terraform works fine. The problem is that, except for list variables, I don't believe that terraform ever wants arrays, but the output of this tool has arrays everywhere.

BTW, thanks for putting this tool together. HCL is great for humans to work with, but it isn't great to automate.

Thanks,
Erick

@Acconut
Copy link
Collaborator

Acconut commented May 14, 2017

@erick-thompson Unfortunately, this is the same problem as above and therefore we cannot do much about this. :|

@erick-thompson
Copy link

That is what I figured. Any point in filing an issue in the terraform repo?

@Acconut
Copy link
Collaborator

Acconut commented May 15, 2017

I don't think so. This is not a problem with Terraform but instead arises because HCL cannot be directly mapped to JSON (see hashicorp/hcl#162 (comment)).

@erick-thompson
Copy link

Thanks. I chimed in on the thread just in case.

BTW thanks for the awesome project. It's allowed me to figure out what variables are in play.

@Acconut
Copy link
Collaborator

Acconut commented May 20, 2017

Thank you for your kind words :)

@gellweiler
Copy link

gellweiler commented May 9, 2018

This is the workaround I currently use, because I need the extra [] to be gone:

flattenDataStructure(&objs)
data, e = json.Marshal(objs)

// Workaround for: https://github.com/hashicorp/hcl/issues/237, https://github.com/kvz/json2hcl/issues/3
func flattenDataStructure(data *map[string]interface{}) {
	for k, v := range *data {
		vListOfMap, ok := v.([]map[string]interface{})
		if ok {
			if len(vListOfMap) == 1 {
				(*data)[k] = vListOfMap[0]
			}
		}
	}

	for _, v := range *data {
		vMap, ok := v.(map[string]interface{})
		if ok {
			flattenDataStructure(&vMap)
		}
	}
}

It's not nice but at least for me it gets the job done.

@Acconut
Copy link
Collaborator

Acconut commented May 10, 2018

Thanks, @gellweiler. Unfortunately, I do not think this code works for all use cases.

@finferflu
Copy link

My solution is not clean, but it's quite effective if your use-case is specific. For example, I'm stripping spurious arrays from a template_file resource:

user_data=`json2hcl -reverse < user_data.tf`
user_data=`echo "$user_data" | jq '.data = .data[]'`
user_data=`echo "$user_data" | jq '.data.template_file = .data.template_file[]'`
user_data=`echo $user_data | jq '.data.template_file.user_data = .data.template_file.user_data[]'`
user_data=`echo $user_data | jq '.data.template_file.user_data.vars = .data.template_file.user_data.vars[]'`

There might be a way to do this in one line, but I feel that the above is more readable and manageable.

I hope it helps anyone who might get stuck here.

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

No branches or pull requests

6 participants