pkg/data_provider_schema.go (90 lines of code) (raw):

package pkg import ( "context" "encoding/json" "fmt" "github.com/Azure/golden" "github.com/hashicorp/go-multierror" tfjson "github.com/hashicorp/terraform-json" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/function/stdlib" ) var _ Data = &ProviderSchemaData{} var SchemaRetrieverFactory = func(ctx context.Context) TerraformProviderSchemaRetriever { return NewTerraformCliProviderSchemaRetriever(ctx) } type ProviderSchemaData struct { *BaseData *golden.BaseBlock Source string `hcl:"provider_source"` Version string `hcl:"provider_version"` Resources cty.Value `attribute:"resources"` } func (r *ProviderSchemaData) Type() string { return "provider_schema" } func (r *ProviderSchemaData) ExecuteDuringPlan() error { schemas, err := SchemaRetrieverFactory(r.Context()).Get(r.Source, r.Version) if err != nil { return fmt.Errorf("cannot read `terraform prviders schema` for source %s with version %s: %+v", r.Source, r.Version, err) } r.Resources, err = r.Convert(schemas.ResourceSchemas) return err } func (r *ProviderSchemaData) Convert(schemas map[string]*tfjson.Schema) (cty.Value, error) { resourcesMap := make(map[string]cty.Value) var convertErr error for resourceName, schema := range schemas { attributesMap, err := r.convertAttributeSchemas(schema.Block.Attributes) if err != nil { convertErr = multierror.Append(err, fmt.Errorf("cannot convert attribute schemas for resource %s: %+v", resourceName, err)) continue } nestedBlocksMap, err := r.convertNestedBlockSchemas(schema.Block.NestedBlocks) if err != nil { convertErr = multierror.Append(err, fmt.Errorf("cannot convert nested block schemas for resource %s: %+v", resourceName, err)) continue } resourcesMap[resourceName] = cty.ObjectVal(map[string]cty.Value{ "version": cty.NumberUIntVal(schema.Version), "block": cty.ObjectVal(map[string]cty.Value{ "attributes": cty.ObjectVal(attributesMap), "block_types": cty.ObjectVal(nestedBlocksMap), "description": cty.StringVal(schema.Block.Description), }), }) } if convertErr != nil { return cty.Value{}, convertErr } return cty.ObjectVal(resourcesMap), nil } func (r *ProviderSchemaData) convertAttributeSchemas(attrs map[string]*tfjson.SchemaAttribute) (map[string]cty.Value, error) { attributesMap := make(map[string]cty.Value) for attrName, attr := range attrs { marshal, err := json.Marshal(attr) if err != nil { return nil, fmt.Errorf("cannot marshal attribute schema for %s: %+v", attrName, err) } attrObj, err := stdlib.JSONDecode(cty.StringVal(string(marshal))) if err != nil { return nil, fmt.Errorf("cannot decode attribute schema for %s: %+v", attrName, err) } attributesMap[attrName] = attrObj } return attributesMap, nil } func (r *ProviderSchemaData) convertNestedBlockSchemas(blocks map[string]*tfjson.SchemaBlockType) (map[string]cty.Value, error) { nestedBlocksMap := make(map[string]cty.Value) for blockName, block := range blocks { marshal, err := json.Marshal(block) if err != nil { return nil, fmt.Errorf("cannot marshal block schema for %s: %+v", blockName, err) } nestedBlocksMap[blockName], err = stdlib.JSONDecode(cty.StringVal(string(marshal))) if err != nil { return nil, fmt.Errorf("cannot decode block schema for %s: %+v", blockName, err) } } return nestedBlocksMap, nil }