package mocks import ( gojson "encoding/json" "errors" "fmt" "sort" "github.com/snyk/driftctl/test/goldenfile" "github.com/hashicorp/terraform/providers" "github.com/snyk/driftctl/pkg/terraform" "github.com/zclconf/go-cty/cty" ctyjson "github.com/zclconf/go-cty/cty/json" ) type MockedGoldenTFProvider struct { name string realProvider terraform.TerraformProvider update bool } func NewMockedGoldenTFProvider(name string, realProvider terraform.TerraformProvider, update bool) *MockedGoldenTFProvider { return &MockedGoldenTFProvider{name: name, realProvider: realProvider, update: update} } func (m *MockedGoldenTFProvider) Schema() map[string]providers.Schema { if m.update { schema := m.realProvider.Schema() m.writeSchema(schema) return schema } return m.readSchema() } func (m *MockedGoldenTFProvider) ReadResource(args terraform.ReadResourceArgs) (*cty.Value, error) { if m.update { readResource, err := m.realProvider.ReadResource(args) m.writeReadResource(args, readResource, err) return readResource, err } return m.readReadResource(args) } func (m *MockedGoldenTFProvider) writeSchema(schema map[string]providers.Schema) { marshal, err := gojson.Marshal(schema) if err != nil { panic(err) } goldenfile.WriteFile(m.name, marshal, "schema.golden.json") } func (m *MockedGoldenTFProvider) readSchema() map[string]providers.Schema { content := goldenfile.ReadFile(m.name, "schema.golden.json") var schema map[string]providers.Schema if err := gojson.Unmarshal(content, &schema); err != nil { panic(err) } return schema } func (m *MockedGoldenTFProvider) writeReadResource(args terraform.ReadResourceArgs, readResource *cty.Value, err error) { var readRes = ReadResource{ Value: readResource, Err: err, } marshalled, err := gojson.Marshal(&readRes) if err != nil { panic(err) } fileName := getFileName(args) goldenfile.WriteFile(m.name, marshalled, fileName) } func (m *MockedGoldenTFProvider) readReadResource(args terraform.ReadResourceArgs) (*cty.Value, error) { fileName := getFileName(args) // TODO I'm putting this here for compatibility reason... if !goldenfile.FileExists(m.name, fileName) { fileName = fmt.Sprintf("%s-%s.res.golden.json", args.Ty, args.ID) } content := goldenfile.ReadFile(m.name, fileName) var readRes ReadResource if err := gojson.Unmarshal(content, &readRes); err != nil { panic(err) } return readRes.Value, readRes.Err } type ReadResource struct { Value *cty.Value Err error } func (m *ReadResource) UnmarshalJSON(bytes []byte) error { var unm struct { Typ []byte Val []byte Err *string } if err := gojson.Unmarshal(bytes, &unm); err != nil { return err } if unm.Typ != nil { unmarshalType, err := ctyjson.UnmarshalType(unm.Typ) if err != nil { return err } if unm.Val != nil { unmarshal, err := ctyjson.Unmarshal(unm.Val, unmarshalType) if err != nil { return err } m.Value = &unmarshal } } if unm.Err != nil { m.Err = errors.New(*unm.Err) } return nil } func (m *ReadResource) MarshalJSON() ([]byte, error) { var unm struct { Typ []byte Val []byte Err *string } if m.Value != nil { var err error unm.Typ, err = ctyjson.MarshalType(m.Value.Type()) if err != nil { return nil, err } unm.Val, err = ctyjson.Marshal(*m.Value, m.Value.Type()) if err != nil { return nil, err } } if m.Err != nil { e := m.Err.Error() unm.Err = &e } return gojson.Marshal(unm) } func getFileName(args terraform.ReadResourceArgs) string { suffix := getFileNameSuffix(args) fileName := fmt.Sprintf("%s-%s%s.res.golden.json", args.Ty, args.ID, suffix) return fileName } func getFileNameSuffix(args terraform.ReadResourceArgs) string { suffix := "" keys := make([]string, 0, len(args.Attributes)) for k := range args.Attributes { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { suffix = fmt.Sprintf("%s-%s", suffix, args.Attributes[k]) } return suffix } func (p MockedGoldenTFProvider) Cleanup() {} func (p *MockedGoldenTFProvider) Name() string { return p.realProvider.Name() } func (p *MockedGoldenTFProvider) Version() string { return p.realProvider.Version() }