From 2365c9b801c6263e8b4bc39953924d928825eec5 Mon Sep 17 00:00:00 2001 From: Christian Groschupp Date: Thu, 20 Sep 2018 16:22:53 +0200 Subject: [PATCH] Add TF_HOSTNAME_KEY_NAME to specify which hostname to return (#94) Overwrite with the environment variable TF_HOSTNAME_KEY_NAME which attribute is to be used for the ansible inventory name. By default, the IP address is used. The Ansible variable "ansible_host" is used to connect to the host. --- README.md | 5 ++++ cli.go | 18 +++++++------ parser_test.go | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++ resource.go | 11 ++++++++ 4 files changed, 94 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 66fb08d..7d1f34f 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,11 @@ to `private_ip` before running the playbook, like: TF_KEY_NAME=private_ip ansible-playbook --inventory-file=/path/to/terraform-inventory deploy/playbook.yml +By default, the ip address is the ansible inventory name. The `TF_HOSTNAME_KEY_NAME` environment variable allows +you to overwrite the source of the ansible inventory name. + + TF_HOSTNAME_KEY_NAME=name ansible-playbook --inventory-file=/path/to/terraform-inventory deploy/playbook.yml + ## Development It's just a Go app, so the usual: diff --git a/cli.go b/cli.go index b96d032..c212193 100644 --- a/cli.go +++ b/cli.go @@ -55,20 +55,20 @@ func gatherResources(s *state) map[string]interface{} { for _, res := range s.resources() { // place in list of all resources - all.Hosts = appendUniq(all.Hosts, res.Address()) + all.Hosts = appendUniq(all.Hosts, res.Hostname()) // place in list of resource types tp := fmt.Sprintf("type_%s", res.resourceType) - types[tp] = appendUniq(types[tp], res.Address()) + types[tp] = appendUniq(types[tp], res.Hostname()) unsortedOrdered[res.baseName] = append(unsortedOrdered[res.baseName], res) // store as invdividual host (eg. .) invdName := fmt.Sprintf("%s.%d", res.baseName, res.counter) if old, exists := individual[invdName]; exists { - fmt.Fprintf(os.Stderr, "overwriting already existing individual key %s, old: %v, new: %v", invdName, old, res.Address()) + fmt.Fprintf(os.Stderr, "overwriting already existing individual key %s, old: %v, new: %v", invdName, old, res.Hostname()) } - individual[invdName] = []string{res.Address()} + individual[invdName] = []string{res.Hostname()} // inventorize tags for k, v := range res.Tags() { @@ -77,7 +77,7 @@ func gatherResources(s *state) map[string]interface{} { if v != "" { tag = fmt.Sprintf("%s_%s", k, v) } - tags[tag] = appendUniq(tags[tag], res.Address()) + tags[tag] = appendUniq(tags[tag], res.Hostname()) } } @@ -94,7 +94,7 @@ func gatherResources(s *state) map[string]interface{} { sort.Sort(cs) for i := range resources { - ordered[basename] = append(ordered[basename], resources[i].Address()) + ordered[basename] = append(ordered[basename], resources[i].Hostname()) } } @@ -187,8 +187,10 @@ func checkErr(err error, stderr io.Writer) int { func cmdHost(stdout io.Writer, stderr io.Writer, s *state, hostname string) int { for _, res := range s.resources() { - if hostname == res.Address() { - return output(stdout, stderr, res.Attributes()) + if hostname == res.Hostname() { + attributes := res.Attributes() + attributes["ansible_host"] = res.Address() + return output(stdout, stderr, attributes) } } diff --git a/parser_test.go b/parser_test.go index ef54fdb..7977f83 100644 --- a/parser_test.go +++ b/parser_test.go @@ -3,12 +3,52 @@ package main import ( "bytes" "encoding/json" + "os" "strings" "testing" "github.com/stretchr/testify/assert" ) +const exampleStateFileEnvHostname = ` +{ + "version": 1, + "serial": 1, + "modules": [ + { + "resources": { + "libvirt_domain.fourteen": { + "type": "libvirt_domain", + "primary": { + "id": "824c29be-2164-44c8-83e0-787705571d95", + "attributes": { + "name": "fourteen", + "network_interface.#": "1", + "network_interface.0.addresses.#": "1", + "network_interface.0.addresses.0": "192.168.102.14", + "network_interface.0.mac": "96:EE:4D:BD:B2:45" + } + } + } + } + } + ] +}` + +const expectedListOutputEnvHostname = ` +{ + "all": { + "hosts": [ + "fourteen" + ], + "vars": { + } + }, + "fourteen": ["fourteen"], + "fourteen.0": ["fourteen"], + "type_libvirt_domain": ["fourteen"] +}` + const exampleStateFile = ` { "version": 1, @@ -633,6 +673,7 @@ olddatacenter="\u003c0.7_format" const expectedHostOneOutput = ` { + "ansible_host": "10.0.0.1", "id":"i-aaaaaaaa", "private_ip":"10.0.0.1", "tags.#": "1", @@ -665,6 +706,33 @@ func TestListCommand(t *testing.T) { assert.Equal(t, exp, act) } +func TestListCommandEnvHostname(t *testing.T) { + var s state + r := strings.NewReader(exampleStateFileEnvHostname) + err := s.read(r) + assert.NoError(t, err) + + // Decode expectation as JSON + var exp interface{} + err = json.Unmarshal([]byte(expectedListOutputEnvHostname), &exp) + assert.NoError(t, err) + + // Run the command, capture the output + var stdout, stderr bytes.Buffer + os.Setenv("TF_HOSTNAME_KEY_NAME", "name") + exitCode := cmdList(&stdout, &stderr, &s) + os.Unsetenv("TF_HOSTNAME_KEY_NAME") + assert.Equal(t, 0, exitCode) + assert.Equal(t, "", stderr.String()) + + // Decode the output to compare + var act interface{} + err = json.Unmarshal([]byte(stdout.String()), &act) + assert.NoError(t, err) + + assert.Equal(t, exp, act) +} + func TestHostCommand(t *testing.T) { var s state r := strings.NewReader(exampleStateFile) diff --git a/resource.go b/resource.go index 5abfc14..eca628f 100644 --- a/resource.go +++ b/resource.go @@ -164,6 +164,17 @@ func (r Resource) NameWithCounter() string { return fmt.Sprintf("%s.%d", r.baseName, r.counter) } +// Hostname returns the hostname of this resource. +func (r Resource) Hostname() string { + if keyName := os.Getenv("TF_HOSTNAME_KEY_NAME"); keyName != "" { + if ip := r.State.Primary.Attributes[keyName]; ip != "" { + return ip + } + } + + return r.Address() +} + // Address returns the IP address of this resource. func (r Resource) Address() string { if keyName := os.Getenv("TF_KEY_NAME"); keyName != "" {