drowzy / hxl

An Elixir implementation of HCL.
Apache License 2.0
30 stars 1 forks source link

Module Support? #4

Closed samgaw closed 3 years ago

samgaw commented 3 years ago

Remembering my own naive attempt at this a few years ago using leex & yaac I very much appreciate your efforts!

I've had a hard time try to find any reference to this in the HCL spec so I'm starting to think the concept of modules are a Terraform specific behaviour?

A basic example would be:

iex> HXL.decode_file("/tmp/main.tf")
%{
  "module" => %{
    "webserver_cluster" => %{
      "cluster_name" => "webservers",
      "source" => "./_elb.tf"
    }
  }
}

The workaround is to wrap the path reference in a file() function call. Although that obviously requires a bit of cleanup afterwards :)

iex> HXL.decode_file("./priv/main.tf", functions: %{"file" => &HXL.decode_file!/1})
%{
  "module" => %{
    "webserver_cluster" => %{
      "cluster_name" => "webservers",
      "source" => %{
        "resource" => %{
          "aws_security_group" => %{
            "elb" => %{
              "egress" => %{
              "cidr_blocks" => ["0.0.0.0/0"],
              "from_port" => 0,
              "protocol" => "-1",
              "to_port" => 0
            },
            "ingress" => %{
              "cidr_blocks" => ["0.0.0.0/0"],
              "from_port" => 80,
              "protocol" => "tcp",
              "to_port" => 80
            }
          }
        }
      }
    }
  }
}

Are modules within the eventual scope of the library? Or would you have any pointers on working with includes as-is?

Thanks!

drowzy commented 3 years ago

There's nothing in HCL about modules, so I think you are correct that it's a Terraform specific behaviour.

It does however touch on something I'd like to include which is custom evaluation of the AST. Right now there isn't a way to hook into the evaluation, since it runs directly after the parse in decode/2

So define a behaviour and accept other evaluators modules as options to decode. Unfortunately the functions that actually does the evaluation are private in HXL.Eval, so you can't do anything about it right now. But I'll move it to a HXL.Eval.Base so in your case you'd implement eval/2 for Blocks matching modules something like:

Given the following ast:

  hcl = """
    module "foo" {
    source = "./mod"
  }
  """

HXL.decode_as_ast(hcl)
{:ok,
 %HXL.Ast.Body{
   statements: [
     %HXL.Ast.Block{
       body: %HXL.Ast.Body{
         statements: [
           %HXL.Ast.Attr{
             expr: %HXL.Ast.TemplateExpr{delimiter: nil, lines: ["./mod"]},
             name: "source"
           }
         ]
       },
       labels: ["foo"],
       type: "module"
     }
   ]
 }}

The evaluator have to handle the Block and Body and the Attr so it'd look something like:

defmodule Terraform.Eval do
  alias HXL.Ast.{Block, Body, Attr}
  @behaviour HXL.Eval

  @impl true
  @spec eval(HXL.Ast.t(), HXL.Eval.t()) :: {term(), HXL.Eval.t()}
  def eval(%Block{body: body, type: "module", labels: labels}, ctx) do
    # Handle module body and return a value
    # or put something into the context
    _value = handle_module_body(body, ctx)
    {:ignore, ctx}
  end

  def eval(ast, ctx) do
    HXL.Eval.Base.eval(ast, ctx)
  end

  def handle_module_body(%Body{statements: [%Attr{name: "source", expr: expr}]}, ctx) do
    # Expr is template expr containing the source path
    # Do something and return a value
  end

Handle the parts that you require and delegate to the base eval mod. I'll create an issue and track it :)

What do you think?

samgaw commented 3 years ago

That would be perfect! It solves my immediate need to handle Terraform modules. But also gives me an option of expanding it to handle Sentinel configs as well, which are another flavour of HCL.

drowzy commented 3 years ago

@samgaw I just released v0.2.0 with support for custom evaluators. I added an example with Terraform modules evaluator

Let me know how it works out for you 😄 !

samgaw commented 3 years ago

I did a little dance when I read this 😂

I’ll spend some time building with this at the end of week and let you know. Awesome work!! I’m really excited!