1
1
Fork 0
mirror of https://github.com/adammck/terraform-inventory synced 2024-05-11 09:36:12 +02:00

Fix parsing `index` field in case of non-numeric string (as allowed in `for_each` construct), fix sorting (#153)

* Fix parsing `index` field in case of non-numeric string (as allowed in `for_each` construct), fix sorting
* Use newer Go versions in CI so functions like `errors.Unwrap` are available
This commit is contained in:
Andreas Sommer 2021-07-30 23:06:38 +02:00 committed by GitHub
parent bb01111c63
commit 8b4b0ccdc0
Signed by: GitHub
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 105 additions and 28 deletions

View File

@ -4,8 +4,7 @@ env:
- GO111MODULE=on
go:
- "1.8"
- "1.11.x"
- "1.13"
- "1.x" # latest
script:

25
cli.go
View File

@ -6,6 +6,7 @@ import (
"io"
"os"
"sort"
"strings"
)
type counterSorter struct {
@ -21,7 +22,7 @@ func (cs counterSorter) Swap(i, j int) {
}
func (cs counterSorter) Less(i, j int) bool {
return cs.resources[i].counter < cs.resources[j].counter
return cs.resources[i].counterNumeric < cs.resources[j].counterNumeric || (cs.resources[i].counterNumeric == cs.resources[j].counterNumeric && cs.resources[i].counterStr < cs.resources[j].counterStr)
}
type allGroup struct {
@ -74,8 +75,13 @@ func gatherResourcesPre0dot12(s *state) map[string]interface{} {
unsortedOrdered[res.baseName] = append(unsortedOrdered[res.baseName], res)
// store as invdividual host (eg. <name>_<count>)
invdName := fmt.Sprintf("%s_%d", res.baseName, res.counter)
// store as individual host (e.g. <name>_<count>)
var invdName string
if res.counterStr != "" {
invdName = fmt.Sprintf("%s_%s", res.baseName, strings.Replace(res.counterStr, ".", "_", -1))
} else {
invdName = fmt.Sprintf("%s_%d", res.baseName, res.counterNumeric)
}
if old, exists := individual[invdName]; exists {
fmt.Fprintf(os.Stderr, "overwriting already existing individual key %s, old: %v, new: %v\n", invdName, old, res.Hostname())
}
@ -161,11 +167,17 @@ func gatherResources0dot12(s *stateTerraform0dot12) map[string]interface{} {
// place in list of resource types
tp := fmt.Sprintf("type_%s", res.resourceType)
types[tp] = appendUniq(types[tp], res.Hostname())
sort.Strings(types[tp])
unsortedOrdered[res.baseName] = append(unsortedOrdered[res.baseName], res)
// store as invdividual host (eg. <name>_<count>)
invdName := fmt.Sprintf("%s_%d", res.baseName, res.counter)
// store as individual host (e.g. <name>_<count>)
var invdName string
if res.counterStr != "" {
invdName = fmt.Sprintf("%s_%s", res.baseName, strings.Replace(res.counterStr, ".", "_", -1))
} else {
invdName = fmt.Sprintf("%s_%d", res.baseName, res.counterNumeric)
}
if old, exists := individual[invdName]; exists {
fmt.Fprintf(os.Stderr, "overwriting already existing individual key %s, old: %v, new: %v", invdName, old, res.Hostname())
}
@ -183,9 +195,12 @@ func gatherResources0dot12(s *stateTerraform0dot12) map[string]interface{} {
tag = resourceIDNames[v]
}
tags[tag] = appendUniq(tags[tag], res.Hostname())
sort.Strings(tags[tag])
}
}
sort.Strings(all.Hosts)
// inventorize outputs as variables
if len(s.outputs()) > 0 {
for _, out := range s.outputs() {

View File

@ -340,7 +340,7 @@ func (s *stateTerraform0dot12) resources() []*Resource {
case float64:
resourceKeyName += "." + strconv.Itoa(int(v))
case string:
resourceKeyName += "." + v
resourceKeyName += "." + strings.Replace(v, ".", "_", -1)
default:
fmt.Fprintf(os.Stderr, "Warning: unknown index type %v\n", v)
}

View File

@ -1190,6 +1190,41 @@ const exampleStateFileTerraform0dot12 = `
}
],
"address": "module.my-module-three"
},
{
"resources": [
{
"address": "aws_instance.host",
"type": "aws_instance",
"name": "host",
"index": "for_each_example.first",
"values": {
"ami": "ami-00000000000000001",
"id": "i-44444444444444444",
"private_ip": "10.0.0.4",
"public_ip": "",
"tags": {
"Name": "four-aws-instance"
}
}
},
{
"address": "aws_instance.host",
"type": "aws_instance",
"name": "host",
"index": "for_each_example.second",
"values": {
"ami": "ami-00000000000000001",
"id": "i-11144444444444444",
"private_ip": "10.0.1.4",
"public_ip": "",
"tags": {
"Name": "four-aws-instance"
}
}
}
],
"address": "module.my-module-four"
}
]
}
@ -1203,9 +1238,11 @@ const expectedListOutputTerraform0dot12 = `
"hosts": [
"10.0.0.2",
"10.0.0.3",
"10.0.0.4",
"10.0.1.3",
"35.159.25.34",
"12.34.56.78"
"10.0.1.4",
"12.34.56.78",
"35.159.25.34"
],
"vars": {
"my_endpoint": "a.b.c.d.example.com",
@ -1215,14 +1252,18 @@ const expectedListOutputTerraform0dot12 = `
},
"one_0": ["35.159.25.34"],
"one": ["35.159.25.34"],
"module_my-module-four_host_for_each_example_first": ["10.0.0.4"],
"module_my-module-four_host_for_each_example_second": ["10.0.1.4"],
"module_my-module-four_host": ["10.0.0.4", "10.0.1.4"],
"module_my-module-two_host_0": ["10.0.0.2"],
"module_my-module-two_host": ["10.0.0.2"],
"module_my-module-three_host_0": ["10.0.0.3"],
"module_my-module-three_host_1": ["10.0.1.3"],
"module_my-module-three_host": ["10.0.0.3", "10.0.1.3"],
"type_aws_instance": ["10.0.0.2", "10.0.0.3", "10.0.1.3", "35.159.25.34"],
"type_aws_instance": ["10.0.0.2", "10.0.0.3", "10.0.0.4", "10.0.1.3", "10.0.1.4", "35.159.25.34"],
"name_four-aws-instance": ["10.0.0.4", "10.0.1.4"],
"name_one-aws-instance": ["35.159.25.34"],
"name_two-aws-instance": ["10.0.0.2"],
"name_three-aws-instance": ["10.0.0.3", "10.0.1.3"],
@ -1237,9 +1278,11 @@ const expectedListOutputTerraform0dot12 = `
const expectedInventoryOutputTerraform0dot12 = `[all]
10.0.0.2
10.0.0.3
10.0.0.4
10.0.1.3
35.159.25.34
10.0.1.4
12.34.56.78
35.159.25.34
[all:vars]
map={"first":"a","second":"b"}
@ -1249,6 +1292,16 @@ my_password="1234"
[foo_bar]
12.34.56.78
[module_my-module-four_host]
10.0.0.4
10.0.1.4
[module_my-module-four_host_for_each_example_first]
10.0.0.4
[module_my-module-four_host_for_each_example_second]
10.0.1.4
[module_my-module-three_host]
10.0.0.3
10.0.1.3
@ -1265,6 +1318,10 @@ my_password="1234"
[module_my-module-two_host_0]
10.0.0.2
[name_four-aws-instance]
10.0.0.4
10.0.1.4
[name_one-aws-instance]
35.159.25.34
@ -1284,7 +1341,9 @@ my_password="1234"
[type_aws_instance]
10.0.0.2
10.0.0.3
10.0.0.4
10.0.1.3
10.0.1.4
35.159.25.34
[type_vsphere_virtual_machine]

View File

@ -61,7 +61,13 @@ type Resource struct {
// Extracted from keyName
resourceType string
baseName string
counter int
// counterNumeric is 0 for resources created without `count=` attribute or
// having a non-numeric string index
counterNumeric int
// counterStr is set if the resource index (e.g. in `for_each`-constructed
// resources) is not a number.
counterStr string
}
func NewResource(keyName string, state resourceState) (*Resource, error) {
@ -72,24 +78,28 @@ func NewResource(keyName string, state resourceState) (*Resource, error) {
return nil, fmt.Errorf("couldn't parse resource keyName: %s", keyName)
}
var c int
counterNumeric := 0
counterStr := ""
var err error
if m[3] != "" {
// The third section should be the index, if it's present. Not sure what
// else we can do other than panic (which seems highly undesirable) if that
// isn't the case. With Terraform 0.12 for_each syntax, index is a string.
c, err = strconv.Atoi(m[3])
// isn't the case. With Terraform 0.12 for_each syntax, index can also be
// a non-numeric string (loop over any string value).
counterNumeric, err = strconv.Atoi(m[3])
if err != nil {
m[2] = fmt.Sprintf("%s.%s", m[2], m[3])
counterNumeric = 0
counterStr = m[3]
}
}
return &Resource{
State: state,
keyName: keyName,
resourceType: m[1],
baseName: m[2],
counter: c,
State: state,
keyName: keyName,
resourceType: m[1],
baseName: m[2],
counterNumeric: counterNumeric,
counterStr: counterStr,
}, nil
}
@ -210,12 +220,6 @@ func (r Resource) Attributes() map[string]string {
return r.State.Primary.Attributes
}
// NameWithCounter returns the resource name with its counter. For resources
// created without a 'count=' attribute, this will always be zero.
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 != "" {