2015-02-06 22:31:59 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2017-10-20 17:32:34 +02:00
|
|
|
"os"
|
2016-09-22 06:13:26 +02:00
|
|
|
"sort"
|
2015-02-06 22:31:59 +01:00
|
|
|
)
|
|
|
|
|
2017-10-20 17:32:34 +02:00
|
|
|
type counterSorter struct {
|
|
|
|
resources []*Resource
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cs counterSorter) Len() int {
|
|
|
|
return len(cs.resources)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cs counterSorter) Swap(i, j int) {
|
|
|
|
cs.resources[i], cs.resources[j] = cs.resources[j], cs.resources[i]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cs counterSorter) Less(i, j int) bool {
|
|
|
|
return cs.resources[i].counter < cs.resources[j].counter
|
|
|
|
}
|
|
|
|
|
2016-09-22 06:13:26 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2016-04-09 17:22:39 +02:00
|
|
|
func gatherResources(s *state) map[string]interface{} {
|
2017-10-20 17:32:34 +02:00
|
|
|
outputGroups := make(map[string]interface{})
|
|
|
|
|
|
|
|
all := &allGroup{Hosts: make([]string, 0), Vars: make(map[string]interface{})}
|
|
|
|
types := make(map[string][]string)
|
|
|
|
individual := make(map[string][]string)
|
|
|
|
ordered := make(map[string][]string)
|
|
|
|
tags := make(map[string][]string)
|
|
|
|
|
|
|
|
unsortedOrdered := make(map[string][]*Resource)
|
2016-09-22 06:13:26 +02:00
|
|
|
|
2016-04-09 17:27:00 +02:00
|
|
|
for _, res := range s.resources() {
|
2017-10-20 17:32:34 +02:00
|
|
|
// place in list of all resources
|
|
|
|
all.Hosts = appendUniq(all.Hosts, res.Address())
|
2016-04-09 17:22:39 +02:00
|
|
|
|
2017-10-20 17:32:34 +02:00
|
|
|
// place in list of resource types
|
|
|
|
tp := fmt.Sprintf("type_%s", res.resourceType)
|
|
|
|
types[tp] = appendUniq(types[tp], res.Address())
|
|
|
|
|
|
|
|
unsortedOrdered[res.baseName] = append(unsortedOrdered[res.baseName], res)
|
2016-04-09 17:22:39 +02:00
|
|
|
|
2017-10-20 17:32:34 +02:00
|
|
|
// store as invdividual host (eg. <name>.<count>)
|
|
|
|
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())
|
|
|
|
}
|
|
|
|
individual[invdName] = []string{res.Address()}
|
|
|
|
|
|
|
|
// inventorize tags
|
|
|
|
for k, v := range res.Tags() {
|
|
|
|
// Valueless
|
|
|
|
tag := k
|
|
|
|
if v != "" {
|
|
|
|
tag = fmt.Sprintf("%s_%s", k, v)
|
|
|
|
}
|
|
|
|
tags[tag] = appendUniq(tags[tag], res.Address())
|
2016-04-09 17:27:00 +02:00
|
|
|
}
|
2015-02-06 22:31:59 +01:00
|
|
|
}
|
|
|
|
|
2017-10-20 17:32:34 +02:00
|
|
|
// inventorize outputs as variables
|
2016-04-09 17:27:00 +02:00
|
|
|
if len(s.outputs()) > 0 {
|
2016-02-18 11:46:40 +01:00
|
|
|
for _, out := range s.outputs() {
|
2017-10-20 17:32:34 +02:00
|
|
|
all.Vars[out.keyName] = out.value
|
2016-02-18 11:46:40 +01:00
|
|
|
}
|
2016-02-16 12:12:49 +01:00
|
|
|
}
|
2017-10-20 17:32:34 +02:00
|
|
|
|
|
|
|
// sort the ordered groups
|
|
|
|
for basename, resources := range unsortedOrdered {
|
|
|
|
cs := counterSorter{resources}
|
|
|
|
sort.Sort(cs)
|
|
|
|
|
|
|
|
for i := range resources {
|
|
|
|
ordered[basename] = append(ordered[basename], resources[i].Address())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
outputGroups["all"] = all
|
|
|
|
for k, v := range individual {
|
|
|
|
if old, exists := outputGroups[k]; exists {
|
|
|
|
fmt.Fprintf(os.Stderr, "individual overwriting already existing output with key %s, old: %v, new: %v", k, old, v)
|
|
|
|
}
|
|
|
|
outputGroups[k] = v
|
|
|
|
}
|
|
|
|
for k, v := range ordered {
|
|
|
|
if old, exists := outputGroups[k]; exists {
|
|
|
|
fmt.Fprintf(os.Stderr, "ordered overwriting already existing output with key %s, old: %v, new: %v", k, old, v)
|
|
|
|
}
|
|
|
|
outputGroups[k] = v
|
|
|
|
}
|
|
|
|
for k, v := range types {
|
|
|
|
if old, exists := outputGroups[k]; exists {
|
|
|
|
fmt.Fprintf(os.Stderr, "types overwriting already existing output key %s, old: %v, new: %v", k, old, v)
|
|
|
|
}
|
|
|
|
outputGroups[k] = v
|
|
|
|
}
|
|
|
|
for k, v := range tags {
|
|
|
|
if old, exists := outputGroups[k]; exists {
|
|
|
|
fmt.Fprintf(os.Stderr, "tags overwriting already existing output key %s, old: %v, new: %v", k, old, v)
|
|
|
|
}
|
|
|
|
outputGroups[k] = v
|
|
|
|
}
|
|
|
|
|
|
|
|
return outputGroups
|
2016-02-24 13:03:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2016-04-09 17:27:00 +02:00
|
|
|
groups := gatherResources(s)
|
2017-09-19 16:54:24 +02:00
|
|
|
group_names := []string{}
|
|
|
|
for group, _ := range groups {
|
|
|
|
group_names = append(group_names, group)
|
|
|
|
}
|
|
|
|
sort.Strings(group_names)
|
|
|
|
for _, group := range group_names {
|
2016-04-09 17:27:00 +02:00
|
|
|
|
2017-09-19 16:54:24 +02:00
|
|
|
switch grp := groups[group].(type) {
|
2016-09-22 06:13:26 +02:00
|
|
|
case []string:
|
|
|
|
writeLn("["+group+"]", stdout, stderr)
|
|
|
|
for _, item := range grp {
|
|
|
|
writeLn(item, stdout, stderr)
|
|
|
|
}
|
2016-04-09 17:27:00 +02:00
|
|
|
|
2016-09-22 06:13:26 +02:00
|
|
|
case *allGroup:
|
|
|
|
writeLn("["+group+"]", stdout, stderr)
|
|
|
|
for _, item := range grp.Hosts {
|
|
|
|
writeLn(item, stdout, stderr)
|
|
|
|
}
|
|
|
|
writeLn("", stdout, stderr)
|
|
|
|
writeLn("["+group+":vars]", stdout, stderr)
|
2017-09-19 16:54:24 +02:00
|
|
|
vars := []string{}
|
|
|
|
for key, _ := range grp.Vars {
|
|
|
|
vars = append(vars, key)
|
|
|
|
}
|
|
|
|
sort.Strings(vars)
|
|
|
|
for _, key := range vars {
|
|
|
|
jsonItem, _ := json.Marshal(grp.Vars[key])
|
|
|
|
itemLn := fmt.Sprintf("%s", string(jsonItem))
|
|
|
|
writeLn(key+"="+itemLn, stdout, stderr)
|
2016-04-09 17:27:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-22 06:13:26 +02:00
|
|
|
writeLn("", stdout, stderr)
|
2016-04-09 17:27:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0
|
2015-02-06 22:31:59 +01:00
|
|
|
}
|
|
|
|
|
2016-09-22 06:13:26 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2015-02-06 22:31:59 +01:00
|
|
|
func cmdHost(stdout io.Writer, stderr io.Writer, s *state, hostname string) int {
|
2015-12-10 04:54:19 +01:00
|
|
|
for _, res := range s.resources() {
|
2015-12-10 05:34:25 +01:00
|
|
|
if hostname == res.Address() {
|
2015-06-05 04:43:56 +02:00
|
|
|
return output(stdout, stderr, res.Attributes())
|
2015-02-06 22:31:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-04 17:56:13 +02:00
|
|
|
fmt.Fprintf(stdout, "{}")
|
2015-02-06 22:31:59 +01:00
|
|
|
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
|
|
|
|
}
|