Skip to content

Commit de1b58a

Browse files
committed
Add runtime conversion from new spec schema to the old one
This allows us to work on pango and terraform codegen with a single codebase.
1 parent 7e8c1fc commit de1b58a

File tree

2 files changed

+375
-479
lines changed

2 files changed

+375
-479
lines changed

pkg/properties/normalized.go

Lines changed: 371 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import (
99

1010
"github.com/paloaltonetworks/pan-os-codegen/pkg/content"
1111
"github.com/paloaltonetworks/pan-os-codegen/pkg/naming"
12+
"github.com/paloaltonetworks/pan-os-codegen/pkg/schema/object"
13+
"github.com/paloaltonetworks/pan-os-codegen/pkg/schema/parameter"
14+
"github.com/paloaltonetworks/pan-os-codegen/pkg/schema/validator"
15+
"github.com/paloaltonetworks/pan-os-codegen/pkg/schema/xpath"
1216
)
1317

1418
type Normalization struct {
@@ -176,11 +180,375 @@ func GetNormalizations() ([]string, error) {
176180
return files, nil
177181
}
178182

183+
func schemaParameterToSpecParameter(schemaSpec *parameter.Parameter) (*SpecParam, error) {
184+
var specType string
185+
if schemaSpec.Type == "object" {
186+
specType = ""
187+
} else if schemaSpec.Type == "enum" {
188+
specType = "string"
189+
} else {
190+
specType = schemaSpec.Type
191+
}
192+
193+
var defaultVal string
194+
var requiredVal bool
195+
196+
var innerSpec *Spec
197+
var itemsSpec SpecParamItems
198+
199+
generateInnerSpec := func(spec *parameter.ParameterStructSpec) (*Spec, error) {
200+
params := make(map[string]*SpecParam)
201+
oneofs := make(map[string]*SpecParam)
202+
203+
for _, elt := range spec.Parameters {
204+
param, err := schemaParameterToSpecParameter(elt)
205+
if err != nil {
206+
return nil, err
207+
}
208+
params[elt.Name] = param
209+
}
210+
211+
for _, elt := range spec.Variants {
212+
param, err := schemaParameterToSpecParameter(elt)
213+
if err != nil {
214+
return nil, err
215+
}
216+
oneofs[elt.Name] = param
217+
}
218+
219+
return &Spec{
220+
Params: params,
221+
OneOf: oneofs,
222+
}, nil
223+
}
224+
225+
switch spec := schemaSpec.Spec.(type) {
226+
case *parameter.ParameterStructSpec:
227+
requiredVal = spec.Required
228+
229+
var err error
230+
innerSpec, err = generateInnerSpec(spec)
231+
if err != nil {
232+
return nil, err
233+
}
234+
235+
case *parameter.ParameterListSpec:
236+
requiredVal = spec.Required
237+
if spec.Items.Type == "object" {
238+
itemsSpec.Type = "entry"
239+
var err error
240+
innerSpec, err = generateInnerSpec(&spec.Items.Spec)
241+
if err != nil {
242+
return nil, err
243+
}
244+
} else {
245+
itemsSpec.Type = spec.Items.Type
246+
}
247+
for _, v := range schemaSpec.Validators {
248+
switch spec := v.Spec.(type) {
249+
case *validator.CountSpec:
250+
min := int64(spec.Min)
251+
max := int64(spec.Max)
252+
itemsSpec.Length = &SpecParamItemsLength{
253+
Min: &min,
254+
Max: &max,
255+
}
256+
}
257+
}
258+
case *parameter.ParameterEnumSpec:
259+
requiredVal = spec.Required
260+
defaultVal = spec.Default
261+
case *parameter.ParameterNilSpec:
262+
specType = "string"
263+
case *parameter.ParameterSimpleSpec:
264+
requiredVal = spec.Required
265+
if typed, ok := spec.Default.(string); ok {
266+
defaultVal = typed
267+
}
268+
}
269+
270+
var profiles []*SpecParamProfile
271+
for _, profile := range schemaSpec.Profiles {
272+
var notPresent bool
273+
var version string
274+
if profile.MaximumVersion != nil {
275+
notPresent = true
276+
version = profile.MaximumVersion.String()
277+
} else if profile.MinimumVersion != nil {
278+
version = profile.MinimumVersion.String()
279+
}
280+
profiles = append(profiles, &SpecParamProfile{
281+
Xpath: profile.Xpath,
282+
Type: profile.Type,
283+
NotPresent: notPresent,
284+
FromVersion: version,
285+
})
286+
}
287+
288+
specParameter := &SpecParam{
289+
Description: schemaSpec.Description,
290+
Type: specType,
291+
Default: defaultVal,
292+
Required: requiredVal,
293+
Profiles: profiles,
294+
Spec: innerSpec,
295+
}
296+
297+
for _, v := range schemaSpec.Validators {
298+
switch spec := v.Spec.(type) {
299+
case *validator.RegexpSpec:
300+
specParameter.Regex = spec.Expr
301+
case *validator.StringLengthSpec:
302+
min := int64(spec.Min)
303+
max := int64(spec.Max)
304+
specParameter.Length = &SpecParamLength{
305+
Min: &min,
306+
Max: &max,
307+
}
308+
case *validator.CountSpec:
309+
min := int64(spec.Min)
310+
max := int64(spec.Max)
311+
specParameter.Count = &SpecParamCount{
312+
Min: &min,
313+
Max: &max,
314+
}
315+
}
316+
}
317+
318+
if schemaSpec.Type == "list" {
319+
specParameter.Items = &itemsSpec
320+
}
321+
322+
return specParameter, nil
323+
}
324+
325+
func generateXpathVariables(variables []xpathschema.Variable) map[string]*LocationVar {
326+
xpathVars := make(map[string]*LocationVar)
327+
for _, variable := range variables {
328+
entry := &LocationVar{
329+
Description: variable.Description,
330+
Default: variable.Default,
331+
Required: variable.Required,
332+
Validation: nil,
333+
}
334+
335+
for _, v := range variable.Validators {
336+
switch spec := v.Spec.(type) {
337+
case *validator.NotValuesSpec:
338+
notValues := make(map[string]string)
339+
for _, value := range spec.Values {
340+
notValues[value.Value] = value.Error
341+
342+
}
343+
entry.Validation = &LocationVarValidation{
344+
NotValues: notValues,
345+
}
346+
347+
}
348+
}
349+
350+
xpathVars[variable.Name] = entry
351+
}
352+
353+
return xpathVars
354+
}
355+
356+
func generateImportVariables(variables []xpathschema.Variable) map[string]*ImportVar {
357+
importVars := make(map[string]*ImportVar)
358+
for _, variable := range variables {
359+
entry := &ImportVar{
360+
Description: variable.Description,
361+
Default: variable.Default,
362+
}
363+
importVars[variable.Name] = entry
364+
}
365+
366+
return importVars
367+
}
368+
369+
func schemaToSpec(object schemaobject.Object) (*Normalization, error) {
370+
spec := &Normalization{
371+
Name: object.DisplayName,
372+
TerraformProviderConfig: TerraformProviderConfig{
373+
SkipResource: object.TerraformConfig.SkipResource,
374+
SkipDatasource: object.TerraformConfig.SkipDatasource,
375+
SkipDatasourceListing: object.TerraformConfig.SkipdatasourceListing,
376+
Suffix: object.TerraformConfig.Suffix,
377+
},
378+
Locations: make(map[string]*Location),
379+
Imports: make(map[string]*Import),
380+
GoSdkPath: object.GoSdkConfig.Package,
381+
XpathSuffix: object.XpathSuffix,
382+
Version: object.Version,
383+
Spec: &Spec{
384+
Params: make(map[string]*SpecParam),
385+
OneOf: make(map[string]*SpecParam),
386+
},
387+
}
388+
389+
for _, location := range object.Locations {
390+
var xpath []string
391+
392+
schemaXpathVars := make(map[string]xpathschema.Variable)
393+
for _, elt := range location.Xpath.Variables {
394+
schemaXpathVars[elt.Name] = elt
395+
}
396+
for _, elt := range location.Xpath.Elements {
397+
var eltEntry string
398+
if xpathVar, ok := schemaXpathVars[elt[1:]]; ok {
399+
if xpathVar.Type == "entry" {
400+
eltEntry = fmt.Sprintf("{{ Entry %s }}", elt)
401+
} else if xpathVar.Type == "object" {
402+
eltEntry = fmt.Sprintf("{{ Object %s }}", elt)
403+
}
404+
} else {
405+
if strings.HasPrefix(elt, "$") {
406+
panic(fmt.Sprintf("elt: %s", elt))
407+
}
408+
eltEntry = elt
409+
}
410+
xpath = append(xpath, eltEntry)
411+
}
412+
413+
locationDevice := &LocationDevice{}
414+
415+
for _, device := range location.Devices {
416+
if device == "panorama" {
417+
locationDevice.Panorama = true
418+
} else if device == "ngfw" {
419+
locationDevice.Ngfw = true
420+
}
421+
}
422+
423+
xpathVars := generateXpathVariables(location.Xpath.Variables)
424+
if len(xpathVars) == 0 {
425+
xpathVars = nil
426+
}
427+
428+
entry := &Location{
429+
Description: location.Description,
430+
Device: locationDevice,
431+
Xpath: xpath,
432+
Vars: xpathVars,
433+
}
434+
spec.Locations[location.Name] = entry
435+
}
436+
437+
for _, entry := range object.Entries {
438+
if entry.Name == "name" {
439+
specEntry := &Entry{
440+
Name: &EntryName{
441+
Description: entry.Description,
442+
},
443+
}
444+
445+
for _, v := range entry.Validators {
446+
switch spec := v.Spec.(type) {
447+
case *validator.StringLengthSpec:
448+
min := int64(spec.Min)
449+
max := int64(spec.Max)
450+
specEntry.Name.Length = &EntryNameLength{
451+
Min: &min,
452+
Max: &max,
453+
}
454+
}
455+
}
456+
spec.Entry = specEntry
457+
}
458+
459+
}
460+
461+
imports := make(map[string]*Import, len(object.Imports))
462+
for _, elt := range object.Imports {
463+
var xpath []string
464+
465+
schemaXpathVars := make(map[string]xpathschema.Variable)
466+
for _, xpathVariable := range elt.Xpath.Variables {
467+
schemaXpathVars[xpathVariable.Name] = xpathVariable
468+
}
469+
470+
for _, element := range elt.Xpath.Elements {
471+
var eltEntry string
472+
if xpathVar, ok := schemaXpathVars[element[1:]]; ok {
473+
if xpathVar.Type == "entry" {
474+
eltEntry = fmt.Sprintf("{{ Entry %s }}", elt.Name)
475+
} else if xpathVar.Type == "object" {
476+
eltEntry = fmt.Sprintf("{{ Object %s }}", elt.Name)
477+
}
478+
} else {
479+
eltEntry = element
480+
}
481+
xpath = append(xpath, eltEntry)
482+
}
483+
484+
importVariables := generateImportVariables(elt.Xpath.Variables)
485+
if len(importVariables) == 0 {
486+
importVariables = nil
487+
}
488+
489+
imports[elt.Name] = &Import{
490+
Xpath: xpath,
491+
Vars: importVariables,
492+
OnlyForParams: elt.OnlyForParams,
493+
}
494+
}
495+
496+
if len(imports) > 0 {
497+
spec.Imports = imports
498+
}
499+
500+
consts := make(map[string]*Const)
501+
for _, param := range object.Spec.Parameters {
502+
specParam, err := schemaParameterToSpecParameter(param)
503+
if err != nil {
504+
return nil, err
505+
}
506+
507+
switch spec := param.Spec.(type) {
508+
case *parameter.ParameterEnumSpec:
509+
constValues := make(map[string]*ConstValue)
510+
for _, elt := range spec.Values {
511+
if elt.Const == "" {
512+
continue
513+
}
514+
constValues[elt.Const] = &ConstValue{
515+
Value: elt.Value,
516+
}
517+
}
518+
if len(constValues) > 0 {
519+
consts[param.Name] = &Const{
520+
Values: constValues,
521+
}
522+
}
523+
524+
}
525+
spec.Spec.Params[param.Name] = specParam
526+
}
527+
528+
if len(consts) > 0 {
529+
spec.Const = consts
530+
}
531+
532+
for _, param := range object.Spec.Variants {
533+
specParam, err := schemaParameterToSpecParameter(param)
534+
if err != nil {
535+
return nil, err
536+
}
537+
spec.Spec.OneOf[param.Name] = specParam
538+
}
539+
540+
return spec, nil
541+
}
542+
179543
// ParseSpec parse single spec (unmarshal file), add name variants for locations and params, add default types for params.
180544
func ParseSpec(input []byte) (*Normalization, error) {
181-
var spec Normalization
545+
var object schemaobject.Object
546+
err := content.Unmarshal(input, &object)
547+
if err != nil {
548+
return nil, err
549+
}
182550

183-
err := content.Unmarshal(input, &spec)
551+
spec, err := schemaToSpec(object)
184552
if err != nil {
185553
return nil, err
186554
}
@@ -210,7 +578,7 @@ func ParseSpec(input []byte) (*Normalization, error) {
210578
return nil, err
211579
}
212580

213-
return &spec, err
581+
return spec, err
214582
}
215583

216584
// AddNameVariantsForLocation add name variants for location (under_score and CamelCase).

0 commit comments

Comments
 (0)