1
1
mirror of https://github.com/adammck/terraform-inventory synced 2024-11-22 15:52:01 +01:00

Add support for remote state (#60)

* add support for remote state in version 0.9
* remove .terraform/terraform.tfstate as a source
This commit is contained in:
Husayn Arrah 2017-06-09 21:25:34 +02:00 committed by Adam Mckaig
parent 898a7270c4
commit 313d404eb3
4 changed files with 80 additions and 22 deletions

@ -94,11 +94,25 @@ Can be provisioned separately with:
## More Usage
Ansible doesn't seem to support calling a dynamic inventory script with params,
so if you need to specify the location of your state file, set the `TF_STATE`
so if you need to specify the location of your state file or terraform directory, set the `TF_STATE`
environment variable before running `ansible-playbook`, like:
TF_STATE=deploy/terraform.tfstate ansible-playbook --inventory-file=/path/to/terraform-inventory deploy/playbook.yml
or
TF_STATE=../terraform ansible-playbook --inventory-file=/path/to/terraform-inventory deploy/playbook.yml
If `TF_STATE` is a file, it parses the file as json, if `TF_STATE` is a directory, it runs `terraform state pull` inside the directory, which is supports both local and remote terraform state.
It looks for state config in this order
- `TF_STATE`: environment variable of where to find either a statefile or a terraform project
- `TI_TFSTATE`: another environment variable similar to TF_STATE
- `terraform.tfstate`: it looks in the state file in the current directory.
- `.`: lastly it assumes you are at the root of a terraform project.
Alternately, if you need to do something fancier (like downloading your state
file from S3 before running), you might wrap this tool with a shell script, and
call that instead. Something like:

@ -25,11 +25,5 @@ func GetInputPath(fs vfs.Filesystem, env venv.Env) string {
return fn
}
fn = ".terraform/terraform.tfstate"
_, err = fs.Stat(fn)
if err == nil {
return fn
}
return ""
return "."
}

@ -13,11 +13,12 @@ import (
)
func TestGetInputPath(t *testing.T) {
assert.Equal(t, "", GetInputPath(memfs.Create(), venv.Mock()))
assert.Equal(t, ".", GetInputPath(memfs.Create(), venv.Mock()))
assert.Equal(t, "aaa", GetInputPath(memfs.Create(), envWith(map[string]string{"TF_STATE": "aaa"})))
assert.Equal(t, "bbb", GetInputPath(memfs.Create(), envWith(map[string]string{"TI_TFSTATE": "bbb"})))
assert.Equal(t, "terraform.tfstate", GetInputPath(fsWithFiles([]string{"terraform.tfstate"}), venv.Mock()))
assert.Equal(t, ".terraform/terraform.tfstate", GetInputPath(fsWithFiles([]string{".terraform/terraform.tfstate"}), venv.Mock()))
assert.Equal(t, ".", GetInputPath(fsWithFiles([]string{".terraform/terraform.tfstate"}), venv.Mock()))
assert.Equal(t, "terraform", GetInputPath(fsWithDirs([]string{"terraform"}), envWith(map[string]string{"TF_STATE": "terraform"})))
}
func envWith(env map[string]string) venv.Env {
@ -53,6 +54,21 @@ func fsWithFiles(filenames []string) vfs.Filesystem {
return fs
}
func fsWithDirs(dirs []string) vfs.Filesystem {
fs := memfs.Create()
var err error
for _, fp := range dirs {
err = vfs.MkdirAll(fs, fp, 0700)
if err != nil {
panic(err)
}
}
return fs
}
// TODO: Upgrade this later with file contents.
func touchFile(fs vfs.Filesystem, filename string) error {
return writeFile(fs, filename, []byte{}, 0600)

48
main.go

@ -1,11 +1,13 @@
package main
import (
"bytes"
"flag"
"fmt"
"github.com/adammck/venv"
"github.com/blang/vfs"
"os"
"os/exec"
"path/filepath"
)
@ -23,17 +25,13 @@ func main() {
return
}
if file == "" {
fs := vfs.OS()
if file == "" {
env := venv.OS()
file = GetInputPath(fs, env)
}
if file == "" {
fmt.Printf("Usage: %s [options] path\n", os.Args[0])
os.Exit(1)
}
if !*list && *host == "" && !*inventory {
fmt.Fprint(os.Stderr, "Either --host or --list must be specified")
os.Exit(1)
@ -45,6 +43,16 @@ func main() {
os.Exit(1)
}
f, err := fs.Stat(path)
if err != nil {
fmt.Fprintf(os.Stderr, "Invalid file: %s\n", err)
os.Exit(1)
}
var s state
if !f.IsDir() {
stateFile, err := os.Open(path)
defer stateFile.Close()
if err != nil {
@ -52,12 +60,38 @@ func main() {
os.Exit(1)
}
var s state
err = s.read(stateFile)
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading tfstate file: %s\n", err)
os.Exit(1)
}
}
if f.IsDir() {
cmd := exec.Command("terraform", "state", "pull")
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)
}
err = s.read(&out)
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading `terraform state pull` output: %s\n", err)
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])
os.Exit(1)
}
if *list {
os.Exit(cmdList(os.Stdout, os.Stderr, &s))