-
Notifications
You must be signed in to change notification settings - Fork 9.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
command/jsonprovider: export providers schemas to json (#20446)
* command/jsonprovider: a new package for exporting providers schemas as JSON
- Loading branch information
1 parent
2b9e2b4
commit 16823f4
Showing
14 changed files
with
790 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package jsonprovider | ||
|
||
import ( | ||
"encoding/json" | ||
|
||
"github.com/hashicorp/terraform/configs/configschema" | ||
) | ||
|
||
type attribute struct { | ||
AttributeType json.RawMessage `json:"type,omitempty"` | ||
Description string `json:"description,omitempty"` | ||
Required bool `json:"required,omitempty"` | ||
Optional bool `json:"optional,omitempty"` | ||
Computed bool `json:"computed,omitempty"` | ||
Sensitive bool `json:"sensitive,omitempty"` | ||
} | ||
|
||
func marshalAttribute(attr *configschema.Attribute) *attribute { | ||
// we're not concerned about errors because at this point the schema has | ||
// already been checked and re-checked. | ||
attrTy, _ := attr.Type.MarshalJSON() | ||
|
||
return &attribute{ | ||
AttributeType: attrTy, | ||
Description: attr.Description, | ||
Required: attr.Required, | ||
Optional: attr.Optional, | ||
Computed: attr.Computed, | ||
Sensitive: attr.Sensitive, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package jsonprovider | ||
|
||
import ( | ||
"encoding/json" | ||
"testing" | ||
|
||
"github.com/google/go-cmp/cmp" | ||
"github.com/zclconf/go-cty/cty" | ||
|
||
"github.com/hashicorp/terraform/configs/configschema" | ||
) | ||
|
||
func TestMarshalAttribute(t *testing.T) { | ||
tests := []struct { | ||
Input *configschema.Attribute | ||
Want *attribute | ||
}{ | ||
{ | ||
&configschema.Attribute{Type: cty.String, Optional: true, Computed: true}, | ||
&attribute{ | ||
AttributeType: json.RawMessage(`"string"`), | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
}, | ||
{ // collection types look a little odd. | ||
&configschema.Attribute{Type: cty.Map(cty.String), Optional: true, Computed: true}, | ||
&attribute{ | ||
AttributeType: json.RawMessage(`["map","string"]`), | ||
Optional: true, | ||
Computed: true, | ||
}, | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
got := marshalAttribute(test.Input) | ||
if !cmp.Equal(got, test.Want) { | ||
t.Fatalf("wrong result:\n %v\n", cmp.Diff(got, test.Want)) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package jsonprovider | ||
|
||
import ( | ||
"github.com/hashicorp/terraform/configs/configschema" | ||
) | ||
|
||
type block struct { | ||
Attributes map[string]*attribute `json:"attributes,omitempty"` | ||
BlockTypes map[string]*blockType `json:"block_types,omitempty"` | ||
} | ||
|
||
type blockType struct { | ||
NestingMode string `json:"nesting_mode,omitempty"` | ||
Block *block `json:"block,omitempty"` | ||
MinItems uint64 `json:"min_items,omitempty"` | ||
MaxItems uint64 `json:"max_items,omitempty"` | ||
} | ||
|
||
func marshalBlockTypes(nestedBlock *configschema.NestedBlock) *blockType { | ||
if nestedBlock == nil { | ||
return &blockType{} | ||
} | ||
ret := &blockType{ | ||
Block: marshalBlock(&nestedBlock.Block), | ||
MinItems: uint64(nestedBlock.MinItems), | ||
MaxItems: uint64(nestedBlock.MaxItems), | ||
} | ||
|
||
switch nestedBlock.Nesting { | ||
case configschema.NestingSingle: | ||
ret.NestingMode = "single" | ||
case configschema.NestingList: | ||
ret.NestingMode = "list" | ||
case configschema.NestingSet: | ||
ret.NestingMode = "set" | ||
case configschema.NestingMap: | ||
ret.NestingMode = "map" | ||
default: | ||
ret.NestingMode = "invalid" | ||
} | ||
return ret | ||
} | ||
|
||
func marshalBlock(configBlock *configschema.Block) *block { | ||
if configBlock == nil { | ||
return &block{} | ||
} | ||
|
||
var ret block | ||
if len(configBlock.Attributes) > 0 { | ||
attrs := make(map[string]*attribute, len(configBlock.Attributes)) | ||
for k, attr := range configBlock.Attributes { | ||
attrs[k] = marshalAttribute(attr) | ||
} | ||
ret.Attributes = attrs | ||
} | ||
|
||
if len(configBlock.BlockTypes) > 0 { | ||
blockTypes := make(map[string]*blockType, len(configBlock.BlockTypes)) | ||
for k, bt := range configBlock.BlockTypes { | ||
blockTypes[k] = marshalBlockTypes(bt) | ||
} | ||
ret.BlockTypes = blockTypes | ||
} | ||
|
||
return &ret | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package jsonprovider | ||
|
||
import ( | ||
"encoding/json" | ||
"testing" | ||
|
||
"github.com/google/go-cmp/cmp" | ||
"github.com/zclconf/go-cty/cty" | ||
|
||
"github.com/hashicorp/terraform/configs/configschema" | ||
) | ||
|
||
func TestMarshalBlock(t *testing.T) { | ||
tests := []struct { | ||
Input *configschema.Block | ||
Want *block | ||
}{ | ||
{ | ||
nil, | ||
&block{}, | ||
}, | ||
{ | ||
Input: &configschema.Block{ | ||
Attributes: map[string]*configschema.Attribute{ | ||
"id": {Type: cty.String, Optional: true, Computed: true}, | ||
"ami": {Type: cty.String, Optional: true}, | ||
}, | ||
BlockTypes: map[string]*configschema.NestedBlock{ | ||
"network_interface": { | ||
Nesting: configschema.NestingList, | ||
Block: configschema.Block{ | ||
Attributes: map[string]*configschema.Attribute{ | ||
"device_index": {Type: cty.String, Optional: true}, | ||
"description": {Type: cty.String, Optional: true}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
Want: &block{ | ||
Attributes: map[string]*attribute{ | ||
"ami": {AttributeType: json.RawMessage(`"string"`), Optional: true}, | ||
"id": {AttributeType: json.RawMessage(`"string"`), Optional: true, Computed: true}, | ||
}, | ||
BlockTypes: map[string]*blockType{ | ||
"network_interface": { | ||
NestingMode: "list", | ||
Block: &block{ | ||
Attributes: map[string]*attribute{ | ||
"description": {AttributeType: json.RawMessage(`"string"`), Optional: true}, | ||
"device_index": {AttributeType: json.RawMessage(`"string"`), Optional: true}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
got := marshalBlock(test.Input) | ||
if !cmp.Equal(got, test.Want) { | ||
t.Fatalf("wrong result:\n %v\n", cmp.Diff(got, test.Want)) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// Package jsonprovider contains types and functions to marshal terraform | ||
// provider schemas into a json formatted output. | ||
package jsonprovider |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package jsonprovider | ||
|
||
import ( | ||
"encoding/json" | ||
|
||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
// FormatVersion represents the version of the json format and will be | ||
// incremented for any change to this format that requires changes to a | ||
// consuming parser. | ||
const FormatVersion = "0.1" | ||
|
||
// providers is the top-level object returned when exporting provider schemas | ||
type providers struct { | ||
FormatVersion string `json:"format_version"` | ||
Schemas map[string]Provider `json:"provider_schemas"` | ||
} | ||
|
||
type Provider struct { | ||
Provider *schema `json:"provider,omitempty"` | ||
ResourceSchemas map[string]*schema `json:"resource_schemas,omitempty"` | ||
DataSourceSchemas map[string]*schema `json:"data_source_schemas,omitempty"` | ||
} | ||
|
||
func newProviders() *providers { | ||
schemas := make(map[string]Provider) | ||
return &providers{ | ||
FormatVersion: FormatVersion, | ||
Schemas: schemas, | ||
} | ||
} | ||
|
||
func Marshal(s *terraform.Schemas) ([]byte, error) { | ||
if len(s.Providers) == 0 { | ||
return nil, nil | ||
} | ||
|
||
providers := newProviders() | ||
|
||
for k, v := range s.Providers { | ||
providers.Schemas[k] = marshalProvider(v) | ||
} | ||
|
||
// add some polish for the human consumers | ||
ret, err := json.MarshalIndent(providers, "", " ") | ||
return ret, err | ||
} | ||
|
||
func marshalProvider(tps *terraform.ProviderSchema) Provider { | ||
if tps == nil { | ||
return Provider{} | ||
} | ||
|
||
var ps *schema | ||
var rs, ds map[string]*schema | ||
|
||
if tps.Provider != nil { | ||
ps = marshalSchema(tps.Provider) | ||
} | ||
|
||
if tps.ResourceTypes != nil { | ||
rs = marshalSchemas(tps.ResourceTypes, tps.ResourceTypeSchemaVersions) | ||
} | ||
|
||
if tps.DataSources != nil { | ||
ds = marshalSchemas(tps.DataSources, tps.ResourceTypeSchemaVersions) | ||
} | ||
|
||
return Provider{ | ||
Provider: ps, | ||
ResourceSchemas: rs, | ||
DataSourceSchemas: ds, | ||
} | ||
} |
Oops, something went wrong.