From 56d427dc244036356ea38650a2014fdd551792ef Mon Sep 17 00:00:00 2001 From: fujiwara Date: Tue, 3 Sep 2024 13:21:24 +0900 Subject: [PATCH] add -dump flag and Dump() method. --- README.md | 32 ++++++++++++++++++++++++++++++-- tfstate/lookup.go | 18 ++++++++++++++++-- tfstate/lookup_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6e8c088..dd8bedb 100644 --- a/README.md +++ b/README.md @@ -16,12 +16,14 @@ $ brew install fujiwara/tap/tfstate-lookup ``` Usage of tfstate-lookup: + -dump + dump all resources -i interactive mode -j run jid after selecting an item -s string - tfstate file path or URL (default "terraform.tfstate") + tfstate file path or URL (default ".terraform/terraform.tfstate") -state string - tfstate file path or URL (default "terraform.tfstate") + tfstate file path or URL (default ".terraform/terraform.tfstate") -timeout duration timeout for reading tfstate ``` @@ -90,6 +92,32 @@ tfstate-lookup integrates jid as a library, so you don't need to install jid com See also [simiji/jid](https://github.com/simeji/jid). +### Dump all resources, outputs, and data sources in tfstate + +You can dump all resources, outputs, and data sources in tfstate with `-dump` option. + +```console +$ tfstate-lookup -dump +``` + +The output is a JSON object. The keys are the address of the resource, output, or data source. The values are the same as the lookup result. + +```jsonnet +{ + "aws_vpc.main": { + "arn": "arn:aws:ec2:ap-northeast-1:123456789012:vpc/vpc-1a2b3c4d", + "assign_generated_ipv6_cidr_block": false, + // ... + }, + "data.aws_ami.foo": { + "arn": "arn:aws:ec2:ap-northeast-1:123456789012:ami/ami-1a2b3c4d", + // ... + }, + "output.foo": "bar", + // ... +} +``` + ## Usage (Go package) See details in [godoc](https://pkg.go.dev/github.com/fujiwara/tfstate-lookup/tfstate). diff --git a/tfstate/lookup.go b/tfstate/lookup.go index 591bfe9..92dec34 100644 --- a/tfstate/lookup.go +++ b/tfstate/lookup.go @@ -28,7 +28,11 @@ type Object struct { Value interface{} } -func (a Object) Bytes() []byte { +func (a *Object) MarshalJSON() ([]byte, error) { + return json.Marshal(a.Value) +} + +func (a *Object) Bytes() []byte { switch v := (a.Value).(type) { case string: return []byte(v) @@ -38,7 +42,7 @@ func (a Object) Bytes() []byte { } } -func (a Object) String() string { +func (a *Object) String() string { return string(a.Bytes()) } @@ -271,6 +275,16 @@ func (s *TFState) List() ([]string, error) { return names, nil } +// Dump dumps all resources, outputs, and data sources in tfstate +func (s *TFState) Dump() (map[string]*Object, error) { + s.once.Do(s.scan) + res := make(map[string]*Object, len(s.scanned)) + for key, ins := range s.scanned { + res[key] = &Object{noneNil(ins.data, ins.Attributes, ins.AttributesFlat)} + } + return res, nil +} + func (s *TFState) scan() { s.scanned = make(map[string]instance, len(s.state.Resources)) for key, value := range s.state.Outputs { diff --git a/tfstate/lookup_test.go b/tfstate/lookup_test.go index 0b2bda7..6cbce7b 100644 --- a/tfstate/lookup_test.go +++ b/tfstate/lookup_test.go @@ -251,3 +251,43 @@ func TestList(t *testing.T) { t.Errorf("unexpected list names %s", diff) } } + +func TestDump(t *testing.T) { + f, err := os.Open("test/terraform.tfstate") + if err != nil { + t.Error(err) + } + state, err := tfstate.Read(context.Background(), f) + if err != nil { + t.Error(err) + } + dump, _ := state.Dump() + if len(dump) != len(TestNames) { + t.Errorf("unexpected dump length %d", len(dump)) + } + + t.Run("compare dump keys with List()", func(t *testing.T) { + dumpKeys := make([]string, 0, len(dump)) + for key := range dump { + dumpKeys = append(dumpKeys, key) + } + listKeys, _ := state.List() + sort.Strings(dumpKeys) + sort.Strings(listKeys) + if diff := cmp.Diff(dumpKeys, listKeys); diff != "" { + t.Errorf("unexpected dump keys %s", diff) + } + }) + + t.Run("compare dump values with Lookup()", func(t *testing.T) { + for key, dv := range dump { + lv, err := state.Lookup(key) + if err != nil { + t.Error(err) + } + if diff := cmp.Diff(dv, lv); diff != "" { + t.Errorf("unexpected dump value %s", diff) + } + } + }) +}