starhawking / python-terrascript

Create Terraform files using Python scripts.
BSD 2-Clause "Simplified" License
515 stars 76 forks source link

provisioner return one line with `provisioner.file` instead a dictionary #32

Closed nielsonsantana closed 4 years ago

nielsonsantana commented 6 years ago

The expected result of provisioner is a dictonary, but return just a provisioner.file.

p = provisioner(
    'file',
    source='pgbouncer-config',
    destination='/tmp/pgbouncer'
)
print(p)
# ------------------
$ provisioner.file
mjuenema commented 6 years ago

Hi @nielsonsantana. Can you please test against the features/issue32 branch of the repository. I haven't been able to fully test it myself yet so your feedback is welcome. I am particularly interested whether terrascript apply has a problem with the JSON input. terraform validate works fine. Please be aware that currently there can be only one provisioner per resource. This is a known bug #29.

mjuenema commented 6 years ago

This should be fixed in release 0.5.1 whcih is now on PyPi. I tested with a local-exec provisioner (see tests/test_issue32.py. @nielsonsantana Can you please verify whether this fixes your problem. Thanks.

nielsonsantana commented 6 years ago

Hi @mjuenema, I did some tests. I have some doubts. What should be the behavior of print for one object(data, resource, provider...)? Should print the fullname or a dict that correspond what will be write to terrascript? Because, print a provider, show the dict, print another objects still show the fullname. I think that print the object would be interesting to debug, instead of open the ts generated. Maybe, is possible debug by print the entire ts.

About the issue #29, I think that I by pessed it by using native dictionary. Here my example:

provisioners = [
    dict(file=dict(
        source='pgbouncer-config',
        destination='/tmp/pgbouncer')
    ),
    dict(file=dict(
        source='docker-compose.yml',
        destination='/tmp/pgbouncer/docker-compose.yml')
    ),
    dict(file=dict(
        source='bash_scripts',
        destination='/tmp/bash_scripts')
    )
]

instance = ts.add(aws_instance(
    'instance-name',
    availability_zone=zone.availability_zone,
    subnet_id=zone.id,
    connection=dict(
        user='ubuntu',
        type='ssh',
        private_key='${file("${var.key_pair_name_path}")}',
        timeout='1m',
        agent=False,
    ),
    provisioner=provisioners
))

This is the result of ts.dump()

"aws_instance": {
      "pgbouncer-primary": {
        "ami": "${var.image}",
        "associate_public_ip_address": true,
        "availability_zone": "${data.aws_subnet.primary.availability_zone}",
        "connection": {
          "agent": false,
          "private_key": "${file(\"${var.key_pair_name_path}\")}",
          "timeout": "1m",
          "type": "ssh",
          "user": "ubuntu"
        },
        "instance_type": "${var.instance_type}",
        "key_name": "${var.key_pair_name}",
        "provisioner": [
          {
            "file": {
              "destination": "/tmp/pgbouncer",
              "source": "pgbouncer-config"
            }
          },
          {
            "file": {
              "destination": "/tmp/pgbouncer/docker-compose.yml",
              "source": "docker-compose.yml"
            }
          },
          {
            "remote-exec": {
              "inline": [
                "chmod +x /tmp/bash_scripts/*.sh",
                "sudo /tmp/bash_scripts/setup_aws_ssm_agent.sh ",
              ]
            }
          }
        ],
        "subnet_id": "${data.aws_subnet.primary.id}",
        "vpc_security_group_ids": [
          "${aws_security_group.pgbouncer-sg.id}"
        ]
      }
    }

I think that the problem of #29 would be solved if the provisioner return a dict:

file_provisioner = provisioner(
    'file',
    source='docker-compose.yml',
    destination='/tmp/pgbouncer/docker-compose.yml'
)

The result for file_provisioner would be:

{
   "file": {
              "source": "docker-compose.yml",
              "destination": "/tmp/pgbouncer/docker-compose.yml"
    }
}
mjuenema commented 6 years ago

There are some grey areas in how Terraform's HCL language should translate into JSON. It's not actually documented. I tested the following Python/JSON code and it works, but does not solve issue #29.

...
ts = Terrascript()
ts += provider('aws', region='ap-southeast-2')
p = provisioner('local-exec', command='date > $(mktemp tmpXXXXXXX.terrascript)')
ts += aws_instance('I1', ami='ami-60a26a02', instance_type='t2.nano', provisioner=p)
...

Creates

{
  "provider": {
    "aws": {
      "__DEFAULT__": {
        "region": "ap-southeast-2"
      }
    }
  },
  "resource": {
    "aws_instance": {
      "I1": {
        "ami": "ami-60a26a02",
        "instance_type": "t2.nano",
        "provisioner": {
          "local-exec": {
            "command": "date > $(mktemp tmpXXXXXXX.terrascript)"
          }
        }
      }
    }
  }
}

and running terraform apply does execute the provisioner.

mjuenema commented 6 years ago

I haven't tested adding a list of provisioners as in your example.

nielsonsantana commented 6 years ago

I will do more tests.

mjuenema commented 4 years ago

TODO: Create a test case for this issue.

mjuenema commented 4 years ago

Created test case