Skip to content

Commit 780eaac

Browse files
committed
feat(codegen): Add support for terraform lists/queries
1 parent d00b54d commit 780eaac

36 files changed

Lines changed: 500 additions & 55 deletions

assets/terraform/internal/manager/entry_import.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type SDKImportableEntryService[E EntryObject, L EntryLocation, IL ImportLocation
1818
Delete(context.Context, L, []IL, ...string) error
1919
ImportToLocations(context.Context, L, []IL, string) error
2020
UnimportFromLocations(context.Context, L, []IL, []string) error
21+
ListWithXpath(context.Context, string, string, string, string) ([]E, error)
2122
}
2223

2324
type ImportableEntryObjectManager[E EntryObject, L EntryLocation, IL ImportLocation, IS SDKImportableEntryService[E, L, IL]] struct {
@@ -38,8 +39,22 @@ func NewImportableEntryObjectManager[E EntryObject, L EntryLocation, IL ImportLo
3839
}
3940
}
4041

41-
func (o *ImportableEntryObjectManager[E, L, IL, IS]) ReadMany(ctx context.Context, location L, entries []E) ([]E, error) {
42-
return nil, &Error{err: ErrInternal, message: "called ReadMany on an importable singular resource"}
42+
func (o *ImportableEntryObjectManager[E, L, IL, IS]) ReadMany(ctx context.Context, location L, components []string) ([]E, error) {
43+
xpath, err := location.XpathWithComponents(o.client.Versioning(), append(components, util.AsEntryXpath(""))...)
44+
if err != nil {
45+
return nil, err
46+
}
47+
48+
existing, err := o.service.ListWithXpath(ctx, util.AsXpath(xpath), "get", "", "")
49+
if err != nil {
50+
if sdkerrors.IsObjectNotFound(err) {
51+
return nil, ErrObjectNotFound
52+
} else {
53+
return nil, &Error{err: err, message: "Failed to read entries from the server"}
54+
}
55+
}
56+
57+
return existing, nil
4358
}
4459

4560
func (o *ImportableEntryObjectManager[E, L, IL, IS]) Read(ctx context.Context, location L, components []string, name string) (E, error) {

pkg/commands/codegen/codegen.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ func (c *Command) Execute() error {
7676
var dataSourceList []string
7777
var ephemeralResourceList []string
7878
var actionsList []string
79+
var listResourceList []string
7980
specMetadata := make(map[string]properties.TerraformProviderSpecMetadata)
8081

8182
for _, specPath := range c.specs {
@@ -138,6 +139,7 @@ func (c *Command) Execute() error {
138139
dataSourceList = append(dataSourceList, data.DataSources...)
139140
ephemeralResourceList = append(ephemeralResourceList, data.EphemeralResources...)
140141
actionsList = append(actionsList, data.Actions...)
142+
listResourceList = append(listResourceList, data.ListResources...)
141143

142144
for k, v := range data.SpecMetadata {
143145
specMetadata[k] = v
@@ -168,6 +170,7 @@ func (c *Command) Execute() error {
168170
dataSourceList = append(dataSourceList, data.DataSources...)
169171
ephemeralResourceList = append(ephemeralResourceList, data.EphemeralResources...)
170172
actionsList = append(actionsList, data.Actions...)
173+
listResourceList = append(listResourceList, data.ListResources...)
171174

172175
for k, v := range data.SpecMetadata {
173176
specMetadata[k] = v
@@ -190,6 +193,7 @@ func (c *Command) Execute() error {
190193
newProviderObject.DataSources = append(newProviderObject.DataSources, dataSourceList...)
191194
newProviderObject.Resources = append(newProviderObject.Resources, resourceList...)
192195
newProviderObject.EphemeralResources = append(newProviderObject.EphemeralResources, ephemeralResourceList...)
196+
newProviderObject.ListResources = append(newProviderObject.ListResources, listResourceList...)
193197
newProviderObject.Actions = append(newProviderObject.Actions, actionsList...)
194198
newProviderObject.SpecMetadata = specMetadata
195199

pkg/generate/generator.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ func (c *Creator) RenderTemplate() error {
6060

6161
type TerraformProviderFileData struct {
6262
EphemeralResources []string
63+
ListResources []string
6364
Resources []string
6465
DataSources []string
6566
Actions []string
@@ -90,6 +91,12 @@ func (c *Creator) RenderTerraformProviderFile(spec *properties.Normalization, ty
9091
return nil, err
9192
}
9293

94+
if !*spec.TerraformProviderConfig.SkipListResource && (typ == properties.ResourceEntry || typ == properties.ResourceUuid) {
95+
if err := tfp.GenerateTerraformListResource(typ, spec, terraformProvider); err != nil {
96+
return nil, err
97+
}
98+
}
99+
93100
if spec.TerraformProviderConfig.Action {
94101
if err := tfp.GenerateTerraformAction(spec, terraformProvider); err != nil {
95102
return nil, err
@@ -126,6 +133,7 @@ func (c *Creator) RenderTerraformProviderFile(spec *properties.Normalization, ty
126133
EphemeralResources: terraformProvider.EphemeralResources,
127134
Actions: terraformProvider.Actions,
128135
SpecMetadata: terraformProvider.SpecMetadata,
136+
ListResources: terraformProvider.ListResources,
129137
}
130138
return data, nil
131139
}

pkg/properties/normalized.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ type TerraformProviderConfig struct {
8585
Action bool `json:"action" yaml:"action"`
8686
CustomValidation bool `json:"custom_validation" yaml:"custom_validation"`
8787
SkipResource bool `json:"skip_resource" yaml:"skip_resource"`
88+
SkipListResource *bool `json:"skip_list_resource", yaml:"skip_list_resource"`
8889
SkipDatasource bool `json:"skip_datasource" yaml:"skip_datasource"`
8990
SkipDatasourceListing bool `json:"skip_datasource_listing" yaml:"skip_datasource_listing"`
9091
ResourceType TerraformResourceType `json:"resource_type" yaml:"resource_type"`
@@ -722,6 +723,7 @@ func schemaToSpec(object object.Object) (*Normalization, error) {
722723
Action: object.TerraformConfig.Action,
723724
CustomValidation: object.TerraformConfig.CustomValidation,
724725
SkipResource: object.TerraformConfig.SkipResource,
726+
SkipListResource: object.TerraformConfig.SkipListResource,
725727
SkipDatasource: object.TerraformConfig.SkipDatasource,
726728
SkipDatasourceListing: object.TerraformConfig.SkipdatasourceListing,
727729
ResourceType: TerraformResourceType(object.TerraformConfig.ResourceType),

pkg/properties/provider_file.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ func (o TerraformNameProvider) IdentityModelStructName() string {
6868
return fmt.Sprintf("%sIdentityModel", o.ResourceStructName)
6969
}
7070

71+
func (o TerraformNameProvider) ListResourceStructName() string {
72+
return fmt.Sprintf("%sListResource", o.StructName)
73+
}
74+
7175
type TerraformSpecFlags uint
7276

7377
const (
@@ -91,6 +95,7 @@ type TerraformProviderFile struct {
9195
DataSources []string
9296
Resources []string
9397
EphemeralResources []string
98+
ListResources []string
9499
Actions []string
95100
SpecMetadata map[string]TerraformProviderSpecMetadata
96101
Code *strings.Builder

pkg/properties/resourcetype.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ type SchemaType string
1616
const (
1717
SchemaResource SchemaType = "resource"
1818
SchemaEphemeralResource SchemaType = "ephemeral-resource"
19+
SchemaListResource SchemaType = "list-resource"
1920
SchemaAction SchemaType = "action"
2021
SchemaDataSource SchemaType = "datasource"
2122
SchemaCommon SchemaType = "common"
2223
SchemaProvider SchemaType = "provider"
24+
SchemaCustom SchemaType = "custom"
2325
)

pkg/schema/object/object.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ type TerraformConfig struct {
4343
SkipResource bool `yaml:"skip_resource"`
4444
SkipDatasource bool `yaml:"skip_datasource"`
4545
SkipdatasourceListing bool `yaml:"skip_datasource_listing"`
46+
SkipListResource *bool `yaml:"skip_list_resource"`
4647
ResourceType TerraformResourceType `yaml:"resource_type"`
4748
XmlNode *string `yaml:"xml_node"`
4849
CustomFunctions map[string]bool `yaml:"custom_functions"`
@@ -167,6 +168,11 @@ func (o *Object) UnmarshalYAML(n *yaml.Node) error {
167168
return fmt.Errorf("failed to unmarshal yaml spec: plural_type must be list for uuid resource types")
168169
}
169170

171+
if obj.TerraformConfig.SkipListResource == nil {
172+
skipListResource := true
173+
obj.TerraformConfig.SkipListResource = &skipListResource
174+
}
175+
170176
return nil
171177
}
172178

pkg/translate/terraform_provider/crud_operations.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func ResourceCreateFunction(resourceTyp properties.ResourceType, names *NameProv
3535
return RendeCreateUpdateMovementRequired(state, entries)
3636
},
3737
"RenderLocationsStateToPango": func(source string, dest string) (string, error) {
38-
return RenderLocationsStateToPango(names, paramSpec, source, dest)
38+
return RenderLocationsStateToPango(names, paramSpec, source, dest, "resp.Diagnostics")
3939
},
4040
}
4141

@@ -151,7 +151,7 @@ func DataSourceReadFunction(resourceTyp properties.ResourceType, names *NameProv
151151
return RenderLocationsPangoToState(names, paramSpec, source, dest)
152152
},
153153
"RenderLocationsStateToPango": func(source string, dest string) (string, error) {
154-
return RenderLocationsStateToPango(names, paramSpec, source, dest)
154+
return RenderLocationsStateToPango(names, paramSpec, source, dest, "resp.Diagnostics")
155155
},
156156
}
157157

@@ -219,7 +219,7 @@ func ResourceReadFunction(resourceTyp properties.ResourceType, names *NameProvid
219219
return RenderLocationsPangoToState(names, paramSpec, source, dest)
220220
},
221221
"RenderLocationsStateToPango": func(source string, dest string) (string, error) {
222-
return RenderLocationsStateToPango(names, paramSpec, source, dest)
222+
return RenderLocationsStateToPango(names, paramSpec, source, dest, "resp.Diagnostics")
223223
},
224224
}
225225

@@ -281,7 +281,7 @@ func ResourceUpdateFunction(resourceTyp properties.ResourceType, names *NameProv
281281
return RendeCreateUpdateMovementRequired(state, entries)
282282
},
283283
"RenderLocationsStateToPango": func(source string, dest string) (string, error) {
284-
return RenderLocationsStateToPango(names, paramSpec, source, dest)
284+
return RenderLocationsStateToPango(names, paramSpec, source, dest, "resp.Diagnostics")
285285
},
286286
"RenderLocationsPangoToState": func(source string, dest string) (string, error) {
287287
return RenderLocationsPangoToState(names, paramSpec, source, dest)
@@ -345,7 +345,7 @@ func ResourceDeleteFunction(resourceTyp properties.ResourceType, names *NameProv
345345
return RenderImportLocationAssignment(names, paramSpec, source, dest)
346346
},
347347
"RenderLocationsStateToPango": func(source string, dest string) (string, error) {
348-
return RenderLocationsStateToPango(names, paramSpec, source, dest)
348+
return RenderLocationsStateToPango(names, paramSpec, source, dest, "resp.Diagnostics")
349349
},
350350
}
351351

pkg/translate/terraform_provider/entity_generators.go

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,15 @@ func (g *GenerateTerraformProvider) GenerateTerraformResource(resourceTyp proper
9191
"RenderXpathComponentsGetter": func() (string, error) {
9292
return RenderXpathComponentsGetter(names.ResourceStructName, spec)
9393
},
94+
"RenderMainStruct": func() (string, error) {
95+
return RenderMainStruct(resourceTyp, schemaTyp, names, spec)
96+
},
97+
"RenderConfigureFunc": func() (string, error) {
98+
return RenderConfigureFunc(resourceTyp, schemaTyp, names, spec)
99+
},
100+
"RenderModelStructs": func() (string, error) {
101+
return RenderModelStructs(resourceTyp, schemaTyp, names, spec)
102+
},
94103
"ResourceCreateFunction": func(structName string, serviceName string) (string, error) {
95104
return ResourceCreateFunction(resourceTyp, names, serviceName, spec, terraformProvider, names.PackageName)
96105
},
@@ -265,6 +274,37 @@ func (g *GenerateTerraformProvider) GenerateTerraformResource(resourceTyp proper
265274
return nil
266275
}
267276

277+
// GenerateTerraformListResource generates a Terraform list resource template.
278+
func (o *GenerateTerraformProvider) GenerateTerraformListResource(resourceTyp properties.ResourceType, spec *properties.Normalization, provider *properties.TerraformProviderFile) error {
279+
provider.ImportManager.AddHashicorpImport("github.com/hashicorp/terraform-plugin-framework/list", "")
280+
provider.ImportManager.AddHashicorpImport("github.com/hashicorp/terraform-plugin-framework/list/schema", "listschema")
281+
282+
names := NewNameProvider(spec, resourceTyp)
283+
284+
funcMap := template.FuncMap{
285+
"structName": func() string { return names.StructName },
286+
"metaName": func() string { return names.MetaName },
287+
288+
"RenderModelStructs": func() (string, error) {
289+
return RenderModelStructs(resourceTyp, properties.SchemaListResource, names, spec)
290+
},
291+
"RenderMainStruct": func() (string, error) {
292+
return RenderMainStruct(resourceTyp, properties.SchemaListResource, names, spec)
293+
},
294+
"RenderConfigureFunc": func() (string, error) {
295+
return RenderConfigureFunc(resourceTyp, properties.SchemaListResource, names, spec)
296+
},
297+
"RenderSchema": func() (string, error) {
298+
return RenderSchema(resourceTyp, properties.SchemaListResource, names, spec, provider.ImportManager)
299+
},
300+
"RenderListFunc": func() (string, error) {
301+
return RenderListFunc(resourceTyp, properties.SchemaListResource, names, spec, provider.ImportManager)
302+
},
303+
}
304+
305+
return o.generateTerraformEntityTemplate(resourceTyp, properties.SchemaListResource, names, spec, provider, "resource/list_resource.tmpl", funcMap)
306+
}
307+
268308
// GenerateTerraformAction generates a Terraform action template.
269309
func (o *GenerateTerraformProvider) GenerateTerraformAction(spec *properties.Normalization, provider *properties.TerraformProviderFile) error {
270310
provider.ImportManager.AddStandardImport("context", "")
@@ -288,7 +328,7 @@ func (o *GenerateTerraformProvider) GenerateTerraformAction(spec *properties.Nor
288328
"metaName": func() string { return names.MetaName },
289329
"HasCustomValidation": func() bool { return spec.TerraformProviderConfig.CustomValidation },
290330

291-
"RenderStructs": func() (string, error) { return RenderStructs(resourceTyp, properties.SchemaAction, names, spec) },
331+
"RenderModelStructs": func() (string, error) { return RenderModelStructs(resourceTyp, properties.SchemaAction, names, spec) },
292332
"RenderSchema": func() (string, error) {
293333
return RenderSchema(resourceTyp, properties.SchemaAction, names, spec, provider.ImportManager)
294334
},
@@ -387,6 +427,15 @@ func (g *GenerateTerraformProvider) GenerateTerraformDataSource(resourceTyp prop
387427
"RenderDataSourceSchema": func() (string, error) {
388428
return RenderDataSourceSchema(resourceTyp, names, spec, terraformProvider.ImportManager)
389429
},
430+
"RenderMainStruct": func() (string, error) {
431+
return RenderMainStruct(resourceTyp, properties.SchemaDataSource, names, spec)
432+
},
433+
"RenderConfigureFunc": func() (string, error) {
434+
return RenderConfigureFunc(resourceTyp, properties.SchemaDataSource, names, spec)
435+
},
436+
"RenderModelStructs": func() (string, error) {
437+
return RenderModelStructs(resourceTyp, properties.SchemaDataSource, names, spec)
438+
},
390439
}
391440
err := g.generateTerraformEntityTemplate(resourceTyp, properties.SchemaDataSource, names, spec, terraformProvider, "datasource/datasource.tmpl", funcMap)
392441
if err != nil {

pkg/translate/terraform_provider/generator.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"strings"
99
"text/template"
1010

11+
"github.com/paloaltonetworks/pan-os-codegen/pkg/imports"
1112
"github.com/paloaltonetworks/pan-os-codegen/pkg/properties"
1213
)
1314

@@ -80,6 +81,8 @@ func (g *GenerateTerraformProvider) appendResourceType(spec *properties.Normaliz
8081
case properties.SchemaEphemeralResource:
8182
flags |= properties.TerraformSpecEphemeralResource
8283
terraformProvider.EphemeralResources = append(terraformProvider.EphemeralResources, names.ResourceStructName)
84+
case properties.SchemaListResource:
85+
terraformProvider.ListResources = append(terraformProvider.ListResources, names.ListResourceStructName())
8386
case properties.SchemaAction:
8487
terraformProvider.Actions = append(terraformProvider.Actions, names.ActionStructName())
8588
case properties.SchemaProvider, properties.SchemaCommon:
@@ -117,6 +120,8 @@ func (g *GenerateTerraformProvider) generateTerraformEntityTemplate(resourceTyp
117120
resourceType = "Resource"
118121
case properties.SchemaEphemeralResource:
119122
resourceType = "EphemeralResource"
123+
case properties.SchemaListResource:
124+
resourceType = "ListResource"
120125
case properties.SchemaCommon:
121126
resourceType = "Common"
122127
case properties.SchemaProvider:
@@ -134,3 +139,86 @@ func (g *GenerateTerraformProvider) generateTerraformEntityTemplate(resourceTyp
134139
}
135140
return g.executeTemplate(template, spec, terraformProvider, resourceTyp, schemaTyp, names)
136141
}
142+
143+
func renderMainAndConfigureTmpls(tmpl string, resourceTyp properties.ResourceType, schemaTyp properties.SchemaType, names *NameProvider, spec *properties.Normalization) (string, error) {
144+
type context struct {
145+
BareStructName string
146+
StructName string
147+
PackageName string
148+
SDKName string
149+
IsCustom bool
150+
IsImportableEntry bool
151+
IsEntry bool
152+
IsUuid bool
153+
IsConfig bool
154+
}
155+
156+
data := context{}
157+
158+
switch resourceTyp {
159+
case properties.ResourceConfig:
160+
data.IsConfig = true
161+
case properties.ResourceCustom:
162+
data.IsCustom = true
163+
case properties.ResourceEntry, properties.ResourceEntryPlural:
164+
if len(spec.Imports) > 0 {
165+
data.IsImportableEntry = true
166+
} else {
167+
data.IsEntry = true
168+
}
169+
case properties.ResourceUuid, properties.ResourceUuidPlural:
170+
data.IsUuid = true
171+
}
172+
173+
data.BareStructName = names.StructName
174+
data.SDKName = names.PackageName
175+
176+
switch schemaTyp {
177+
case properties.SchemaListResource:
178+
data.StructName = names.ListResourceStructName()
179+
case properties.SchemaDataSource:
180+
data.StructName = names.DataSourceStructName
181+
case properties.SchemaResource, properties.SchemaEphemeralResource:
182+
data.StructName = names.ResourceStructName
183+
}
184+
185+
if spec.TerraformProviderConfig.Ephemeral == true {
186+
data.PackageName = "ephemeral"
187+
} else {
188+
data.PackageName = "resource"
189+
}
190+
191+
return processTemplate(tmpl, "render-main-and-configure-tmpls", data, commonFuncMap)
192+
}
193+
194+
func RenderMainStruct(resourceTyp properties.ResourceType, schemaTyp properties.SchemaType, names *NameProvider, spec *properties.Normalization) (string, error) {
195+
return renderMainAndConfigureTmpls("common/structure.tmpl", resourceTyp, schemaTyp, names, spec)
196+
}
197+
198+
func RenderConfigureFunc(resourceTyp properties.ResourceType, schemaTyp properties.SchemaType, names *NameProvider, spec *properties.Normalization) (string, error) {
199+
return renderMainAndConfigureTmpls("common/configure_func.tmpl", resourceTyp, schemaTyp, names, spec)
200+
}
201+
202+
func RenderListFunc(resourceTyp properties.ResourceType, schemaTyp properties.SchemaType, names *NameProvider, spec *properties.Normalization, manager *imports.Manager) (string, error) {
203+
type context struct {
204+
StructName string
205+
IdentityModel string
206+
ResourceStructName string
207+
SDKName string
208+
}
209+
210+
data := context{
211+
StructName: names.ListResourceStructName(),
212+
IdentityModel: fmt.Sprintf("%sIdentityModel", names.ResourceStructName),
213+
ResourceStructName: names.ResourceStructName,
214+
SDKName: names.PackageName,
215+
}
216+
217+
funcMap := template.FuncMap{
218+
"RenderLocationsStateToPango": func(source string, dest string) (string, error) {
219+
return RenderLocationsStateToPango(names, spec, source, dest, "diags")
220+
},
221+
}
222+
223+
return processTemplate("resource/list_func.tmpl", "render-list-func", data, funcMap)
224+
}

0 commit comments

Comments
 (0)