Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Top level role support #118

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
language: go

go:
- 1.5
- 1.6
- "1.8"
- "1.11.x"
- "1.x" # latest

script:
- go test -v ./...
112 changes: 108 additions & 4 deletions cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,107 @@ func appendUniq(strs []string, item string) []string {
return strs
}

func gatherResources(s *state) map[string]interface{} {
func gatherResources(s *stateAnyTerraformVersion) map[string]interface{} {
if s.TerraformVersion == TerraformVersionPre0dot12 {
return gatherResourcesPre0dot12(&s.StatePre0dot12)
} else if s.TerraformVersion == TerraformVersion0dot12 {
return gatherResources0dot12(&s.State0dot12)
} else {
panic("Unimplemented Terraform version enum")
}
}

func gatherResourcesPre0dot12(s *state) map[string]interface{} {
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)

resourceIDNames := s.mapResourceIDNames()
for _, res := range s.resources() {
// place in list of all resources
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.Hostname())

unsortedOrdered[res.baseName] = append(unsortedOrdered[res.baseName], res)

// 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\n", invdName, old, res.Hostname())
}
individual[invdName] = []string{res.Hostname()}

// inventorize tags
for k, v := range res.Tags() {
// Valueless
tag := k
if v != "" {
tag = fmt.Sprintf("%s_%s", k, v)
}
// if v is a resource ID, then tag should be resource name
if _, exists := resourceIDNames[v]; exists {
tag = resourceIDNames[v]
}
tags[tag] = appendUniq(tags[tag], res.Hostname())
}
}

// inventorize outputs as variables
if len(s.outputs()) > 0 {
for _, out := range s.outputs() {
all.Vars[out.keyName] = out.value
}
}

// 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].Hostname())
}
}

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
}

func gatherResources0dot12(s *stateTerraform0dot12) map[string]interface{} {
outputGroups := make(map[string]interface{})

all := &allGroup{Hosts: make([]string, 0), Vars: make(map[string]interface{})}
Expand Down Expand Up @@ -78,6 +178,10 @@ func gatherResources(s *state) map[string]interface{} {
if v != "" {
tag = fmt.Sprintf("%s_%s", k, v)
}
// if k is special "role", then tag should be the role value
if k == "role" {
tag = v
}
// if v is a resource ID, then tag should be resource name
if _, exists := resourceIDNames[v]; exists {
tag = resourceIDNames[v]
Expand Down Expand Up @@ -132,11 +236,11 @@ func gatherResources(s *state) map[string]interface{} {
return outputGroups
}

func cmdList(stdout io.Writer, stderr io.Writer, s *state) int {
func cmdList(stdout io.Writer, stderr io.Writer, s *stateAnyTerraformVersion) int {
return output(stdout, stderr, gatherResources(s))
}

func cmdInventory(stdout io.Writer, stderr io.Writer, s *state) int {
func cmdInventory(stdout io.Writer, stderr io.Writer, s *stateAnyTerraformVersion) int {
groups := gatherResources(s)
group_names := []string{}
for group, _ := range groups {
Expand Down Expand Up @@ -190,7 +294,7 @@ func checkErr(err error, stderr io.Writer) int {
return 0
}

func cmdHost(stdout io.Writer, stderr io.Writer, s *state, hostname string) int {
func cmdHost(stdout io.Writer, stderr io.Writer, s *stateAnyTerraformVersion, hostname string) int {
for _, res := range s.resources() {
if hostname == res.Hostname() {
attributes := res.Attributes()
Expand Down
32 changes: 22 additions & 10 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func main() {
os.Exit(1)
}

var s state
var s stateAnyTerraformVersion

if !f.IsDir() {
stateFile, err := os.Open(path)
Expand All @@ -69,39 +69,51 @@ func main() {
}

if f.IsDir() {
cmd := exec.Command("terraform", "state", "pull")
cmd := exec.Command("terraform", "show", "-json")
cmd.Dir = path
var out bytes.Buffer
cmd.Stdout = &out

err = cmd.Run()
if err != nil {
fmt.Fprintf(os.Stderr, "Error running `terraform state pull` in directory %s, %s\n", path, err)
os.Exit(1)
fmt.Fprintf(os.Stderr, "Error running `terraform show -json` in directory %s, %s, falling back to trying Terraform pre-0.12 command\n", path, err)

cmd = exec.Command("terraform", "state", "pull")
cmd.Dir = path
out.Reset()
cmd.Stdout = &out
err = cmd.Run()

if err != nil {
fmt.Fprintf(os.Stderr, "Error running `terraform state pull` in directory %s, %s\n", path, err)
os.Exit(1)
}
}

err = s.read(&out)

if err != nil {
fmt.Fprintf(os.Stderr, "Error reading `terraform state pull` output: %s\n", err)
fmt.Fprintf(os.Stderr, "Error reading Terraform state: %s\n", err)
os.Exit(1)
}
}

if s.TerraformVersion == TerraformVersionUnknown {
fmt.Fprintf(os.Stderr, "Unknown state format\n\nUsage: %s [options] path\npath: this is either a path to a state file or a folder from which `terraform commands` are valid\n", os.Args[0])
os.Exit(1)
}

if s.Modules == nil {
fmt.Printf("Usage: %s [options] path\npath: this is either a path to a state file or a folder from which `terraform commands` are valid\n", os.Args[0])
if (s.TerraformVersion == TerraformVersionPre0dot12 && s.StatePre0dot12.Modules == nil) ||
(s.TerraformVersion == TerraformVersion0dot12 && s.State0dot12.Values.RootModule == nil) {
fmt.Fprintf(os.Stderr, "No modules found in state\n\nUsage: %s [options] path\npath: this is either a path to a state file or a folder from which `terraform commands` are valid\n", os.Args[0])
os.Exit(1)
}

if *list {
os.Exit(cmdList(os.Stdout, os.Stderr, &s))

} else if *inventory {
os.Exit(cmdInventory(os.Stdout, os.Stderr, &s))

} else if *host != "" {
os.Exit(cmdHost(os.Stdout, os.Stderr, &s, *host))

}
}
2 changes: 1 addition & 1 deletion output.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func NewOutput(keyName string, value interface{}) (*Output, error) {

// TODO: Warn instead of silently ignore error?
if len(keyName) == 0 {
return nil, fmt.Errorf("couldn't parse keyName: %s", keyName)
return nil, fmt.Errorf("couldn't parse output keyName: %s", keyName)
}

return &Output{
Expand Down
Loading