@@ -4,11 +4,13 @@ import (
44 "encoding/json"
55 "errors"
66 "fmt"
7+ "log"
78 "strings"
89
910 "github.com/getkin/kin-openapi/openapi2"
1011 "github.com/getkin/kin-openapi/openapi3"
1112 "github.com/hashicorp/go-cty/cty"
13+ "github.com/mitchellh/hashstructure"
1214)
1315
1416// NewFoundryFromSpecV2 creates a new cty.Type foundry from an OpenAPI v2 spec document
@@ -24,7 +26,11 @@ func NewFoundryFromSpecV2(spec []byte) (Foundry, error) {
2426 return nil , fmt .Errorf ("failed to parse spec: %s" , err )
2527 }
2628
27- f := foapiv2 {& swg }
29+ f := foapiv2 {
30+ swagger : & swg ,
31+ typeCache : make (map [uint64 ]cty.Type ),
32+ recursionDepth : 50 , // arbitrarily large number - a type this big will likely kill Terraform anyway
33+ }
2834 d := f .swagger .Definitions
2935 if d == nil || len (d ) == 0 {
3036 return nil , errors .New ("spec has no type information" )
@@ -39,7 +45,9 @@ type Foundry interface {
3945}
4046
4147type foapiv2 struct {
42- swagger * openapi2.Swagger
48+ swagger * openapi2.Swagger
49+ typeCache map [uint64 ]cty.Type
50+ recursionDepth uint64
4351}
4452
4553// GetTypeById looks up a type by its fully qualified ID in the Definitions sections of
@@ -60,7 +68,7 @@ func (f foapiv2) GetTypeByID(id string) (cty.Type, error) {
6068 return cty .NilType , fmt .Errorf ("failed to resolve schema: %s" , err )
6169 }
6270
63- return f .getTypeFromSchema (sch )
71+ return f .getTypeFromSchema (sch , 0 )
6472}
6573
6674func (f foapiv2 ) resolveSchemaRef (ref * openapi3.SchemaRef ) (* openapi3.Schema , error ) {
@@ -77,6 +85,11 @@ func (f foapiv2) resolveSchemaRef(ref *openapi3.SchemaRef) (*openapi3.Schema, er
7785 Type : "integer" ,
7886 }
7987 return & t , nil
88+ case "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps" :
89+ t := openapi3.Schema {
90+ Type : "" ,
91+ }
92+ return & t , nil
8093 }
8194
8295 nref , ok := f .swagger .Definitions [sid ]
@@ -91,10 +104,27 @@ func (f foapiv2) resolveSchemaRef(ref *openapi3.SchemaRef) (*openapi3.Schema, er
91104 return f .resolveSchemaRef (nref )
92105}
93106
94- func (f foapiv2 ) getTypeFromSchema (elem * openapi3.Schema ) (cty.Type , error ) {
107+ func (f foapiv2 ) getTypeFromSchema (elem * openapi3.Schema , stackdepth uint64 ) (cty.Type , error ) {
108+ if stackdepth > f .recursionDepth {
109+ log .Println ("at stack depth" )
110+ // this is a hack to overcome the inability to express recursion in cty
111+ return cty .DynamicPseudoType , nil
112+ }
113+ log .Printf ("stack depth: %d\n " , stackdepth )
114+
95115 if elem == nil {
96116 return cty .NilType , errors .New ("nil type" )
97117 }
118+ h , herr := hashstructure .Hash (elem , nil )
119+
120+ var t cty.Type
121+
122+ // check if type is in cache
123+ if herr == nil {
124+ if t , ok := f .typeCache [h ]; ok {
125+ return t , nil
126+ }
127+ }
98128
99129 switch elem .Type {
100130
@@ -109,29 +139,41 @@ func (f foapiv2) getTypeFromSchema(elem *openapi3.Schema) (cty.Type, error) {
109139 if err != nil {
110140 return cty .NilType , fmt .Errorf ("failed to resolve schema: %s" , err )
111141 }
112- pType , err := f .getTypeFromSchema (schema )
142+ pType , err := f .getTypeFromSchema (schema , stackdepth + 1 )
113143 if err != nil {
114144 return cty .NilType , err
115145 }
116146 atts [p ] = pType
117147 }
118- return cty .Object (atts ), nil
148+ t = cty .Object (atts )
149+ if herr == nil {
150+ f .typeCache [h ] = t
151+ }
152+ return t , nil
119153
120154 case elem .Properties == nil && elem .AdditionalProperties != nil :
121155 // this is how OpenAPI defines associative arrays
122156 s , err := f .resolveSchemaRef (elem .AdditionalProperties )
123157 if err != nil {
124158 return cty .NilType , fmt .Errorf ("failed to resolve schema: %s" , err )
125159 }
126- pt , err := f .getTypeFromSchema (s )
160+ pt , err := f .getTypeFromSchema (s , stackdepth + 1 )
127161 if err != nil {
128162 return cty .NilType , err
129163 }
130- return cty .Map (pt ), nil
164+ t = cty .Map (pt )
165+ if herr == nil {
166+ f .typeCache [h ] = t
167+ }
168+ return t , nil
131169
132170 case elem .Properties == nil && elem .AdditionalProperties == nil :
133171 // this is a strange case, encountered with io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1
134- return cty .DynamicPseudoType , nil
172+ t = cty .DynamicPseudoType
173+ if herr == nil {
174+ f .typeCache [h ] = t
175+ }
176+ return t , nil
135177
136178 }
137179
@@ -140,11 +182,15 @@ func (f foapiv2) getTypeFromSchema(elem *openapi3.Schema) (cty.Type, error) {
140182 if err != nil {
141183 return cty .NilType , fmt .Errorf ("failed to resolve schema for items: %s" , err )
142184 }
143- t , err := f .getTypeFromSchema (it )
185+ t , err := f .getTypeFromSchema (it , stackdepth + 1 )
144186 if err != nil {
145187 return cty .NilType , err
146188 }
147- return cty .List (t ), nil
189+ t = cty .List (t )
190+ if herr == nil {
191+ f .typeCache [h ] = t
192+ }
193+ return t , nil
148194
149195 case "string" :
150196 return cty .String , nil
0 commit comments