Skip to content

Instantly share code, notes, and snippets.

@ikatson
Forked from ArthurHlt/gen-custom.sh
Created February 16, 2019 17:56
Show Gist options
  • Save ikatson/cee88cc50c52f10abdd4ea02d29cc644 to your computer and use it in GitHub Desktop.
Save ikatson/cee88cc50c52f10abdd4ea02d29cc644 to your computer and use it in GitHub Desktop.
Generate json schema for intellij terraform plugin on a community provider
#!/usr/bin/env bash
# Run `./gen-custom.sh github.com/myorg/myprovider provider-name version`
# e.g.: `./gen-custom.sh github.com/mevansam/terraform-provider-cf cloudfoundry v1.0.0`
# This will place a provider-name.json directly in $HOME/.terraform.d/schemas
set -e
CUR="$(pwd)"
out="$CUR/schemas"
mkdir -p "$out"
rm -f "$CUR/failure.txt"
p="$2"
provider="$1/$2"
echo
echo "========================================"
echo "Waiting for update processes to finish"
go get -v "${1}"
echo "All providers updated"
echo
pushd "$GOPATH/src/$1" >/dev/null
# checkout latest tag
git checkout "$(git describe --tags "$(git rev-list --tags --max-count=1)")"
revision="$(git describe --tags)"
pushd "$p" >/dev/null
generate_one() {
go run generate-schema/generate-schema.go
if [[ $? -ne 0 ]]; then
echo "$1" >> "$2/failure.txt"
fi
echo "Finished $1"
}
echo "Preparing $p"
pwd
rm -rf generate-schema
mkdir generate-schema
cp -r "$CUR/template/generate-schema.go" generate-schema/generate-schema.go
find generate-schema -type f -exec sed -i "s@__REPO__@$provider@g" {} +
find generate-schema -type f -exec sed -i "s@__NAME__@$2@g" {} +
find generate-schema -type f -exec sed -i "s@__REVISION__@$revision@g" {} +
find generate-schema -type f -exec sed -i "s~__OUT__~$out~g" {} +
#echo "Building $p"
#make
echo "Generating schema for $p"
if [[ "$KILL_CPU" == "1" ]]; then
(
generate_one "$p" "$CUR"
)&
else
generate_one "$p" "$CUR"
fi
rm -Rf generate-schema
popd >/dev/null
if [[ "$KILL_CPU" == "1" ]]; then
echo
echo "========================================"
echo "Waiting for 'generate-schemas' processes to finish"
wait
echo
fi
echo "========================================"
echo "Everything done!"
echo
popd >/dev/null
mkdir -p "$HOME/.terraform.d/schemas" >/dev/null
cp "$CUR/schemas/$2.json" "$HOME/.terraform.d/schemas"
package main
import (
"github.com/hashicorp/terraform/helper/schema"
tf "github.com/hashicorp/terraform/terraform"
"__REPO__"
"encoding/json"
"fmt"
"os"
"path/filepath"
"reflect"
)
// ExportSchema should be called to export the structure
// of the provider.
func Export(p *schema.Provider) *ResourceProviderSchema {
result := new(ResourceProviderSchema)
result.Name = "__NAME__"
result.Type = "provider"
result.Version = "__REVISION__"
result.Provider = schemaMap(p.Schema).Export()
result.Resources = make(map[string]SchemaInfo)
result.DataSources = make(map[string]SchemaInfo)
for k, r := range p.ResourcesMap {
result.Resources[k] = ExportResource(r)
}
for k, ds := range p.DataSourcesMap {
result.DataSources[k] = ExportResource(ds)
}
return result
}
func ExportResource(r *schema.Resource) SchemaInfo {
return schemaMap(r.Schema).Export()
}
// schemaMap is a wrapper that adds nice functions on top of schemas.
type schemaMap map[string]*schema.Schema
// Export exports the format of this schema.
func (m schemaMap) Export() SchemaInfo {
result := make(SchemaInfo)
for k, v := range m {
item := export(v)
result[k] = item
}
return result
}
func export(v *schema.Schema) SchemaDefinition {
item := SchemaDefinition{}
item.Type = fmt.Sprintf("%s", v.Type)
item.Optional = v.Optional
item.Required = v.Required
item.Description = v.Description
item.InputDefault = v.InputDefault
item.Computed = v.Computed
item.MaxItems = v.MaxItems
item.MinItems = v.MinItems
item.PromoteSingle = v.PromoteSingle
item.ComputedWhen = v.ComputedWhen
item.ConflictsWith = v.ConflictsWith
item.Deprecated = v.Deprecated
item.Removed = v.Removed
if v.Elem != nil {
item.Elem = exportValue(v.Elem, fmt.Sprintf("%T", v.Elem))
}
// TODO: Find better solution
if defValue, err := v.DefaultValue(); err == nil && defValue != nil && !reflect.DeepEqual(defValue, v.Default) {
item.Default = exportValue(defValue, fmt.Sprintf("%T", defValue))
}
return item
}
func exportValue(value interface{}, t string) SchemaElement {
s2, ok := value.(*schema.Schema)
if ok {
return SchemaElement{Type: "SchemaElements", ElementsType: fmt.Sprintf("%s", s2.Type)}
}
r2, ok := value.(*schema.Resource)
if ok {
return SchemaElement{Type: "SchemaInfo", Info: ExportResource(r2)}
}
return SchemaElement{Type: t, Value: fmt.Sprintf("%v", value)}
}
func Generate(provider *schema.Provider, name string, outputPath string) {
outputFilePath := filepath.Join(outputPath, fmt.Sprintf("%s.json", name))
if err := DoGenerate(provider, name, outputFilePath); err != nil {
fmt.Fprintln(os.Stderr, "Error: ", err.Error())
os.Exit(255)
}
}
func DoGenerate(provider *schema.Provider, providerName string, outputFilePath string) error {
providerJson, err := json.MarshalIndent(Export(provider), "", " ")
if err != nil {
return err
}
file, err := os.Create(outputFilePath)
if err != nil {
return err
}
defer file.Close()
_, err = file.Write(providerJson)
if err != nil {
return err
}
return file.Sync()
}
type SchemaElement struct {
// One of ValueType or "SchemaElements" or "SchemaInfo"
Type string `json:",omitempty"`
// Set for simple types (from ValueType)
Value string `json:",omitempty"`
// Set if Type == "SchemaElements"
ElementsType string `json:",omitempty"`
// Set if Type == "SchemaInfo"
Info SchemaInfo `json:",omitempty"`
}
type SchemaDefinition struct {
Type string `json:",omitempty"`
Optional bool `json:",omitempty"`
Required bool `json:",omitempty"`
Description string `json:",omitempty"`
InputDefault string `json:",omitempty"`
Computed bool `json:",omitempty"`
MaxItems int `json:",omitempty"`
MinItems int `json:",omitempty"`
PromoteSingle bool `json:",omitempty"`
ComputedWhen []string `json:",omitempty"`
ConflictsWith []string `json:",omitempty"`
Deprecated string `json:",omitempty"`
Removed string `json:",omitempty"`
Default SchemaElement `json:",omitempty"`
Elem SchemaElement `json:",omitempty"`
}
type SchemaInfo map[string]SchemaDefinition
// ResourceProviderSchema
type ResourceProviderSchema struct {
Name string `json:"name"`
Type string `json:"type"`
Version string `json:"version"`
Provider SchemaInfo `json:"provider"`
Resources map[string]SchemaInfo `json:"resources"`
DataSources map[string]SchemaInfo `json:"data-sources"`
}
func main() {
var provider tf.ResourceProvider
provider = __NAME__.Provider()
Generate(provider.(*schema.Provider), "__NAME__", "__OUT__")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment