Skip to content

Commit 0bf1cd0

Browse files
committed
Implement structures to parse the new spec schema
1 parent be6b55c commit 0bf1cd0

File tree

14 files changed

+650
-0
lines changed

14 files changed

+650
-0
lines changed

go.mod

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
11
module github.com/paloaltonetworks/pan-os-codegen
22

33
require (
4+
github.com/onsi/ginkgo/v2 v2.19.1
5+
github.com/onsi/gomega v1.34.0
46
github.com/stretchr/testify v1.9.0
57
gopkg.in/yaml.v3 v3.0.1
68
)
79

810
require (
911
github.com/davecgh/go-spew v1.1.1 // indirect
12+
github.com/go-logr/logr v1.4.2 // indirect
13+
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
14+
github.com/google/go-cmp v0.6.0 // indirect
15+
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
1016
github.com/pmezard/go-difflib v1.0.0 // indirect
17+
golang.org/x/net v0.25.0 // indirect
18+
golang.org/x/sys v0.21.0 // indirect
19+
golang.org/x/text v0.15.0 // indirect
20+
golang.org/x/tools v0.21.0 // indirect
1121
)
1222

1323
go 1.21.4

go.sum

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,31 @@
11
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
22
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
4+
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
5+
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
6+
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
7+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
8+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
9+
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
10+
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
11+
github.com/onsi/ginkgo/v2 v2.19.1 h1:QXgq3Z8Crl5EL1WBAC98A5sEBHARrAJNzAmMxzLcRF0=
12+
github.com/onsi/ginkgo/v2 v2.19.1/go.mod h1:O3DtEWQkPa/F7fBMgmZQKKsluAy8pd3rEQdrjkPb9zA=
13+
github.com/onsi/gomega v1.34.0 h1:eSSPsPNp6ZpsG8X1OVmOTxig+CblTc4AxpPBykhe2Os=
14+
github.com/onsi/gomega v1.34.0/go.mod h1:MIKI8c+f+QLWk+hxbePD4i0LMJSExPaZOVfkoex4cAo=
315
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
416
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
517
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
618
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
19+
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
20+
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
21+
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
22+
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
23+
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
24+
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
25+
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
26+
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
27+
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
28+
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
729
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
830
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
931
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

pkg/schema/imports/import.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package imports
2+
3+
import "github.com/paloaltonetworks/pan-os-codegen/pkg/schema/xpath"
4+
5+
type Import struct {
6+
Name string `yaml:"name"`
7+
Xpath xpathschema.Xpath `yaml:"xpath"`
8+
OnlyForParams []string `yaml:"only_for_params"`
9+
}

pkg/schema/location/location.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package location
2+
3+
import (
4+
validatorschema "github.com/paloaltonetworks/pan-os-codegen/pkg/schema/validator"
5+
xpathschema "github.com/paloaltonetworks/pan-os-codegen/pkg/schema/xpath"
6+
)
7+
8+
type Device string
9+
10+
const (
11+
DevicePanorama Device = "panorama"
12+
DeviceNgfw = "ngfw"
13+
)
14+
15+
type Location struct {
16+
Name string `yaml:"name"`
17+
Description string `yaml:"description"`
18+
Devices []Device `yaml:"devices"`
19+
Xpath xpathschema.Xpath `yaml:"xpath"`
20+
Validators []*validatorschema.Validator `yaml:"validators"`
21+
}

pkg/schema/object/object.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package object
2+
3+
import (
4+
"gopkg.in/yaml.v3"
5+
6+
"github.com/paloaltonetworks/pan-os-codegen/pkg/schema/imports"
7+
"github.com/paloaltonetworks/pan-os-codegen/pkg/schema/location"
8+
"github.com/paloaltonetworks/pan-os-codegen/pkg/schema/parameter"
9+
"github.com/paloaltonetworks/pan-os-codegen/pkg/schema/validator"
10+
)
11+
12+
type TerraformConfig struct {
13+
SkipResource bool `yaml:"skip_resource"`
14+
SkipDatasource bool `yaml:"skip_datasource"`
15+
SkipdatasourceListing bool `yaml:"skip_datasource_listing"`
16+
Suffix string `yaml:"suffix"`
17+
}
18+
19+
type GoSdkConfig struct {
20+
Package []string
21+
}
22+
23+
type Entry struct {
24+
Name string `yaml:"name"`
25+
Description string `yaml:"descripion"`
26+
Validators []*validator.Validator `yaml:"validators"`
27+
}
28+
29+
type Spec struct {
30+
Required bool `yaml:"required"`
31+
Parameters []*parameter.Parameter `yaml:"params"`
32+
Variants []*parameter.Parameter `yaml:"variants"`
33+
}
34+
35+
type Object struct {
36+
Name string `yaml:"-"`
37+
DisplayName string `yaml:"name"`
38+
XpathSuffix []string `yaml:"xpath_suffix"`
39+
TerraformConfig *TerraformConfig `yaml:"terraform_provider_config"`
40+
Version string `yaml:"version"`
41+
GoSdkConfig *GoSdkConfig `yaml:"go_sdk_config"`
42+
Locations []location.Location `yaml:"locations"`
43+
Entries []Entry `yaml:"entries"`
44+
Imports []imports.Import `yaml:"imports"`
45+
Spec *Spec `yaml:"spec"`
46+
}
47+
48+
func NewFromBytes(name string, objectBytes []byte) (*Object, error) {
49+
var object Object
50+
51+
err := yaml.Unmarshal(objectBytes, &object)
52+
if err != nil {
53+
return nil, err
54+
}
55+
56+
object.Name = name
57+
58+
return &object, nil
59+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package object_test
2+
3+
import (
4+
"log/slog"
5+
"testing"
6+
7+
. "github.com/onsi/ginkgo/v2"
8+
. "github.com/onsi/gomega"
9+
)
10+
11+
func TestObject(t *testing.T) {
12+
slog.SetDefault(slog.New(slog.NewTextHandler(GinkgoWriter, &slog.HandlerOptions{Level: slog.LevelDebug})))
13+
RegisterFailHandler(Fail)
14+
RunSpecs(t, "Object Suite")
15+
}

pkg/schema/parameter/parameter.go

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package parameter
2+
3+
import (
4+
"fmt"
5+
"log/slog"
6+
7+
"gopkg.in/yaml.v3"
8+
9+
"github.com/paloaltonetworks/pan-os-codegen/pkg/errors"
10+
"github.com/paloaltonetworks/pan-os-codegen/pkg/schema/profile"
11+
"github.com/paloaltonetworks/pan-os-codegen/pkg/schema/validator"
12+
)
13+
14+
// Parameter describes a single parameter for the given object spec.
15+
//
16+
// Parameter can describe both a top-level parameter of the object
17+
// or nested parameter of another object parameter.
18+
//
19+
// Spec type is any, as its unmarshalling is done in a custom
20+
// UnmarshalYAML function.
21+
type Parameter struct {
22+
Name string `yaml:"name"`
23+
Description string `yaml:"description"`
24+
Type string `yaml:"type"`
25+
Profiles []profile.Profile `yaml:"profiles"`
26+
Validators []validator.Validator `yaml:"validators"`
27+
Spec any `yaml:"-"`
28+
}
29+
30+
// SimpleSpec describes a parameter of a simple type.
31+
type SimpleSpec struct {
32+
Default any `yaml:"default"`
33+
Required bool `yaml:"required"`
34+
}
35+
36+
// EnumSpecValue describes a single enum value.
37+
type EnumSpecValue struct {
38+
Value string `yaml:"value"`
39+
Const string `yaml:"const"`
40+
}
41+
42+
// EnumSpec describes a parameter of type enum
43+
//
44+
// Values is a list of ParameterEnumSpecValue, where each one consisting of the PAN-OS Value
45+
// and its optional Const representation. This allows to generate a more meaningful
46+
// types, for example when spec value is "color1", and its const is "red", the following
47+
// type will be marshalled for pan-os-go SDK:
48+
//
49+
// ParameterRed ParameterType = "color1"
50+
//
51+
// when spec value is "up" and spec const is empty, the following type will be marshalled
52+
// instead:
53+
//
54+
// ParameterUp ParameterType = "up"
55+
type EnumSpec struct {
56+
Required bool `yaml:"required"`
57+
Default string `yaml:"default"`
58+
Values []EnumSpecValue `yaml:"values"`
59+
}
60+
61+
type StructSpec struct {
62+
Required bool `yaml:"required"`
63+
Parameters []*Parameter `yaml:"params"`
64+
Variants []*Parameter `yaml:"variants"`
65+
}
66+
67+
type ListSpecElement struct {
68+
Type string `yaml:"type"`
69+
Required bool `yaml:"required"`
70+
Spec StructSpec `yaml:"spec"`
71+
}
72+
73+
type ListSpec struct {
74+
Required bool `yaml:"required"`
75+
Items ListSpecElement `yaml:"items"`
76+
}
77+
78+
type NilSpec struct{}
79+
80+
// UnmarshalYAML implements custom unmarshalling logic for parameters
81+
//
82+
// When unmarshalling yaml objects into parameters, their spec is unmarshalled
83+
// into different structures based on the spec type. This custom UnmarshalYAML
84+
// functions handles this logic.
85+
func (p *Parameter) UnmarshalYAML(n *yaml.Node) error {
86+
// Create an empty Parameter value and a new temporary structure
87+
// that is based on the parameter, but overrides Spec field to be
88+
// of a generic yaml.Node type.
89+
// This new type, and the casting below is needed so that yaml
90+
// unmarshaller doesn't recursively call UnmarshalYAML.
91+
type P Parameter
92+
type S struct {
93+
*P `yaml:",inline"`
94+
Spec yaml.Node `yaml:"spec"`
95+
}
96+
97+
// Cast "this" parameter pointer to a new S type and then decode
98+
// entire parameter yaml object into this new object. This will
99+
// unmarshal spec field from yaml into yaml.Node Spec field in the
100+
// temporary structure.
101+
obj := &S{P: (*P)(p)}
102+
if err := n.Decode(obj); err != nil {
103+
return err
104+
}
105+
106+
// Now that we have unmarshalled entire parameter object into a temporary
107+
// structure, we can assign proper structure to the parameter Spec field.
108+
switch p.Type {
109+
case "object":
110+
p.Spec = new(StructSpec)
111+
case "list":
112+
p.Spec = new(ListSpec)
113+
case "enum":
114+
p.Spec = new(EnumSpec)
115+
case "nil":
116+
p.Spec = new(NilSpec)
117+
case "string", "bool", "int64":
118+
p.Spec = new(SimpleSpec)
119+
default:
120+
return errors.NewSchemaError(fmt.Sprintf("unsupported parameter type: '%s'", p.Type))
121+
}
122+
123+
// Finally, decode obj.Spec (which is yaml.Node type) into the parameter
124+
// spec structure
125+
return obj.Spec.Decode(p.Spec)
126+
}
127+
128+
// Required returns whether given parameter is required, by checking its spec.
129+
func (p *Parameter) Required() bool {
130+
switch spec := p.Spec.(type) {
131+
case *SimpleSpec:
132+
return spec.Required
133+
case *ListSpec:
134+
return spec.Required
135+
case *StructSpec:
136+
return spec.Required
137+
case *EnumSpec:
138+
return spec.Required
139+
case *NilSpec:
140+
return false
141+
}
142+
143+
return false
144+
}
145+
146+
// SingularName returns a singular name for parameter.
147+
//
148+
// When Parameter type is list, and list profile type is either
149+
// "entry" or "member", we us first path of an profile xpath array
150+
// to determine a singular name for the given parameter.
151+
//
152+
// When called for non-list parameters, parameter name is returned
153+
// instead.
154+
func (p *Parameter) SingularName() string {
155+
switch p.Spec.(type) {
156+
case *ListSpec:
157+
var singularName string
158+
for _, profile := range p.Profiles {
159+
switch profile.Type {
160+
case "entry", "member":
161+
if len(profile.Xpath) >= 1 {
162+
singularName = profile.Xpath[0]
163+
}
164+
}
165+
}
166+
167+
if singularName == "" {
168+
slog.Warn("Couldn't generate singular name for list parameter", "parameter", p.Name)
169+
}
170+
171+
return singularName
172+
}
173+
174+
return p.Name
175+
}
176+
177+
// SpecItemsType is a shorthand accessor to list items type
178+
//
179+
// When checking parameter list item type, parameter's spec has to
180+
// be first cast to the ParameterListSpec type. This function gives
181+
// a quick access to the type without having to do casting manually.
182+
//
183+
// When called on any other parameter type, it returns an empty
184+
// string, so the caller (mostly templates) must ensure that it's
185+
// being called on list parameters.
186+
func (p *Parameter) SpecItemsType() string {
187+
switch spec := p.Spec.(type) {
188+
case *ListSpec:
189+
return spec.Items.Type
190+
default:
191+
return ""
192+
}
193+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package parameter_test
2+
3+
import (
4+
"testing"
5+
6+
. "github.com/onsi/ginkgo/v2"
7+
. "github.com/onsi/gomega"
8+
)
9+
10+
func TestParameter(t *testing.T) {
11+
RegisterFailHandler(Fail)
12+
RunSpecs(t, "Parameter Suite")
13+
}

0 commit comments

Comments
 (0)