1
1
mirror of https://github.com/adammck/terraform-inventory synced 2024-11-22 20:01:58 +01:00
terraform-inventory/cli.go
Jan Jungnickel 9235bf4079 Return an empty hash for unknown hosts (#73)
When using a static inventory that declares new hosts alongside a
dynamic inventory provided by this utility, ansible will call the
dynamic inventory with `--host <host>` to merge in additional vars
that may have been provided by the dynamic inventory.

The default behaviour of *terraform-inventory* is to output an error
message when an unknown host is encountered, breaking this
functionality.

The correct behaviour is to return an empty hash implying there are
no additional vars for this host.
2017-09-04 11:56:13 -04:00

128 lines
2.8 KiB
Go

package main
import (
"encoding/json"
"fmt"
"io"
"sort"
)
type allGroup struct {
Hosts []string `json:"hosts"`
Vars map[string]interface{} `json:"vars"`
}
func appendUniq(strs []string, item string) []string {
if len(strs) == 0 {
strs = append(strs, item)
return strs
}
sort.Strings(strs)
i := sort.SearchStrings(strs, item)
if i == len(strs) || (i < len(strs) && strs[i] != item) {
strs = append(strs, item)
}
return strs
}
func gatherResources(s *state) map[string]interface{} {
groups := make(map[string]interface{}, 0)
all_group := allGroup{Vars: make(map[string]interface{}), Hosts: make([]string, 0)}
groups["all"] = &all_group
for _, res := range s.resources() {
for _, grp := range res.Groups() {
_, ok := groups[grp]
if !ok {
groups[grp] = []string{}
}
groups[grp] = appendUniq(groups[grp].([]string), res.Address())
all_group.Hosts = appendUniq(all_group.Hosts, res.Address())
}
}
if len(s.outputs()) > 0 {
for _, out := range s.outputs() {
all_group.Vars[out.keyName] = out.value
}
}
return groups
}
func cmdList(stdout io.Writer, stderr io.Writer, s *state) int {
return output(stdout, stderr, gatherResources(s))
}
func cmdInventory(stdout io.Writer, stderr io.Writer, s *state) int {
groups := gatherResources(s)
for group, res := range groups {
switch grp := res.(type) {
case []string:
writeLn("["+group+"]", stdout, stderr)
for _, item := range grp {
writeLn(item, stdout, stderr)
}
case *allGroup:
writeLn("["+group+"]", stdout, stderr)
for _, item := range grp.Hosts {
writeLn(item, stdout, stderr)
}
writeLn("", stdout, stderr)
writeLn("["+group+":vars]", stdout, stderr)
for key, item := range grp.Vars {
writeLn(key+"="+item.(string), stdout, stderr)
}
}
writeLn("", stdout, stderr)
}
return 0
}
func writeLn(str string, stdout io.Writer, stderr io.Writer) {
_, err := io.WriteString(stdout, str+"\n")
checkErr(err, stderr)
}
func checkErr(err error, stderr io.Writer) int {
if err != nil {
fmt.Fprintf(stderr, "Error writing inventory: %s\n", err)
return 1
}
return 0
}
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())
}
}
fmt.Fprintf(stdout, "{}")
return 1
}
// output marshals an arbitrary JSON object and writes it to stdout, or writes
// an error to stderr, then returns the appropriate exit code.
func output(stdout io.Writer, stderr io.Writer, whatever interface{}) int {
b, err := json.Marshal(whatever)
if err != nil {
fmt.Fprintf(stderr, "Error encoding JSON: %s\n", err)
return 1
}
_, err = stdout.Write(b)
if err != nil {
fmt.Fprintf(stderr, "Error writing JSON: %s\n", err)
return 1
}
return 0
}