mirror of
https://github.com/adammck/terraform-inventory
synced 2024-11-26 07:43:46 +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:
parent
898a7270c4
commit
313d404eb3
16
README.md
16
README.md
@ -94,11 +94,25 @@ Can be provisioned separately with:
|
|||||||
## More Usage
|
## More Usage
|
||||||
|
|
||||||
Ansible doesn't seem to support calling a dynamic inventory script with params,
|
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:
|
environment variable before running `ansible-playbook`, like:
|
||||||
|
|
||||||
|
|
||||||
TF_STATE=deploy/terraform.tfstate ansible-playbook --inventory-file=/path/to/terraform-inventory deploy/playbook.yml
|
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
|
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
|
file from S3 before running), you might wrap this tool with a shell script, and
|
||||||
call that instead. Something like:
|
call that instead. Something like:
|
||||||
|
8
input.go
8
input.go
@ -25,11 +25,5 @@ func GetInputPath(fs vfs.Filesystem, env venv.Env) string {
|
|||||||
return fn
|
return fn
|
||||||
}
|
}
|
||||||
|
|
||||||
fn = ".terraform/terraform.tfstate"
|
return "."
|
||||||
_, err = fs.Stat(fn)
|
|
||||||
if err == nil {
|
|
||||||
return fn
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestGetInputPath(t *testing.T) {
|
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, "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, "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.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 {
|
func envWith(env map[string]string) venv.Env {
|
||||||
@ -53,6 +54,21 @@ func fsWithFiles(filenames []string) vfs.Filesystem {
|
|||||||
return fs
|
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.
|
// TODO: Upgrade this later with file contents.
|
||||||
func touchFile(fs vfs.Filesystem, filename string) error {
|
func touchFile(fs vfs.Filesystem, filename string) error {
|
||||||
return writeFile(fs, filename, []byte{}, 0600)
|
return writeFile(fs, filename, []byte{}, 0600)
|
||||||
|
58
main.go
58
main.go
@ -1,11 +1,13 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/adammck/venv"
|
"github.com/adammck/venv"
|
||||||
"github.com/blang/vfs"
|
"github.com/blang/vfs"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -23,17 +25,13 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fs := vfs.OS()
|
||||||
if file == "" {
|
if file == "" {
|
||||||
fs := vfs.OS()
|
|
||||||
env := venv.OS()
|
env := venv.OS()
|
||||||
file = GetInputPath(fs, env)
|
file = GetInputPath(fs, env)
|
||||||
}
|
}
|
||||||
|
|
||||||
if file == "" {
|
|
||||||
fmt.Printf("Usage: %s [options] path\n", os.Args[0])
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !*list && *host == "" && !*inventory {
|
if !*list && *host == "" && !*inventory {
|
||||||
fmt.Fprint(os.Stderr, "Either --host or --list must be specified")
|
fmt.Fprint(os.Stderr, "Either --host or --list must be specified")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -45,17 +43,53 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
stateFile, err := os.Open(path)
|
f, err := fs.Stat(path)
|
||||||
defer stateFile.Close()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error opening tfstate file: %s\n", err)
|
fmt.Fprintf(os.Stderr, "Invalid file: %s\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
var s state
|
var s state
|
||||||
err = s.read(stateFile)
|
|
||||||
if err != nil {
|
if !f.IsDir() {
|
||||||
fmt.Fprintf(os.Stderr, "Error reading tfstate file: %s\n", err)
|
stateFile, err := os.Open(path)
|
||||||
|
defer stateFile.Close()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error opening tfstate file: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user