Skip to content

feat: Add full rendering for terraform resources and data sources #106

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 56 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
d569d50
Update codegen config and add SDK imports
pimielowski Apr 24, 2024
b47746a
Update code generation and add provider configuration
pimielowski Apr 29, 2024
784efdf
Remove unused dependencies
pimielowski Apr 29, 2024
3827db6
Refactor code to improve code clarity and naming conventions
pimielowski Apr 29, 2024
44cb6cc
Extend example to create device group
sebastianczech Apr 29, 2024
800a887
Extend template and template stack spec
sebastianczech Apr 29, 2024
225cfe5
Add TfidStruct function and location encoding
pimielowski Apr 29, 2024
3e291ce
Enhance Terraform provider translation and model generation
pimielowski Apr 30, 2024
e54affd
Refactor Terraform provider template generation code
pimielowski May 6, 2024
7d39d51
Added debugging messages and refactored nested structure handling in …
pimielowski May 7, 2024
14b75a4
Refactor code for terraform provider and add related tests
pimielowski May 8, 2024
0e58587
Extend Terraform provider translation with location attributes
pimielowski May 14, 2024
82e59b1
Enhance code formatting and adjust schema descriptions
pimielowski May 28, 2024
ff1b732
Refactor code for template and file processing in generator
pimielowski May 28, 2024
ef9ed7d
Update generator and provider to support dynamic struct creation
pimielowski May 29, 2024
2c18fbb
Remove unused dependencies from go.mod and go.sum
pimielowski May 29, 2024
373fe9b
Improve structure templates and format for Terraform provider code ge…
pimielowski May 31, 2024
88dde8f
Refactor template execution and enhance Terraform provider generation
pimielowski Jun 20, 2024
320595f
Update struct types and functions in terraform provider
pimielowski Jun 21, 2024
4e09389
Fixed schema template
michalbil Jun 26, 2024
47d80fc
Remove duplicated attribute 'location' from schemas.
kklimonda-cl Jul 1, 2024
5faac23
Fix generated device_group.go in terraform provider
kklimonda-cl Jul 1, 2024
4b6eb76
Fix failing checks, adjust log messages
michalbil Jul 2, 2024
4e2fcf7
Update to pango.Client
michalbil Jul 17, 2024
4bd4ba4
Fix compilation errors
michalbil Jul 22, 2024
0c25d3c
Dependencies update
michalbil Jul 22, 2024
6e67f84
Remove obsolete provider assets
michalbil Jul 22, 2024
afeb805
Some initial work on nested spec copy
kklimonda-cl Jul 23, 2024
c17d07c
Fix generation of CopyFromTerraformToPango functions
kklimonda-cl Jul 23, 2024
69879d9
Rename templates consts
michalbil Jul 23, 2024
cc1d3df
Fix how we get SDK package name
kklimonda-cl Jul 23, 2024
58258a5
Fixes to the nested function calls
kklimonda-cl Jul 23, 2024
a7160ff
Handle nested lists of objects when copying from tf to pango
kklimonda-cl Jul 24, 2024
627d8fb
Generate structs for lists of objects
kklimonda-cl Jul 24, 2024
befe39b
Implement CopyToPango() on all terraform models and objects
kklimonda-cl Jul 24, 2024
85cdc16
Fix copying of lists with object item types
kklimonda-cl Jul 24, 2024
dd92e2e
Add an incomplete implementations of CopyFromPango functions
kklimonda-cl Jul 24, 2024
5626dc9
Update go.mod and go.sum
kklimonda-cl Jul 24, 2024
c5ae619
Merge remote-tracking branch 'origin/main' into feat-render-resource-…
kklimonda-cl Jul 25, 2024
3f06a70
Fix tests for terraform struct generation
kklimonda-cl Jul 25, 2024
11eb059
feat: Added generation of location-related code across all terraform …
kklimonda-cl Jul 29, 2024
ccf9ac2
feat: Generate Terraform DataSource structures (#120)
kklimonda-cl Jul 29, 2024
aa81c13
Create missing code for handling NTP, DNS and other configuration
kklimonda-cl Aug 2, 2024
6e9d428
Add more support for copying lists between pango and terraform
kklimonda-cl Aug 2, 2024
0111a61
Properly create local state when reading from PAN-OS
kklimonda-cl Aug 2, 2024
6a35366
Add support for updating resources
kklimonda-cl Aug 2, 2024
199e91f
Generate SpecMatches and Update code for configs
kklimonda-cl Aug 4, 2024
c326ded
Update terraform codegen for configs to make update work
kklimonda-cl Aug 4, 2024
6d61ed6
Make encrypted values working correctly for the "solo" case
kklimonda-cl Aug 4, 2024
b790f91
Implement reading of DataSource objects
kklimonda-cl Aug 4, 2024
4a9ea5d
Generate default values for resource schema
kklimonda-cl Aug 5, 2024
225dfe1
Fix lists of nested attributes
kklimonda-cl Aug 5, 2024
c9370b9
Merge remote-tracking branch 'origin/main' into feat-render-resource-…
kklimonda-cl Aug 6, 2024
8214aae
When checking if generated file is empty, ignore white spaces
kklimonda-cl Aug 6, 2024
f21fada
Fix errors reported by golangci-lint
kklimonda-cl Aug 6, 2024
aea8772
Fix unmarshaling/marshaling cycle tests
kklimonda-cl Aug 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions assets/terraform/internal/provider/tfid.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func NewTfidDataSource() datasource.DataSource {
}

type tfidDataSource struct {
client *pango.XmlApiClient
client *pango.Client
}

type tfidDsModel struct {
Expand Down Expand Up @@ -105,7 +105,7 @@ func (d *tfidDataSource) Configure(_ context.Context, req datasource.ConfigureRe
return
}

d.client = req.ProviderData.(*pango.XmlApiClient)
d.client = req.ProviderData.(*pango.Client)
}

func (d *tfidDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
Expand Down
4 changes: 4 additions & 0 deletions cmd/codegen/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ terraform_provider_config:
env_name: "PANOS_HEADERS"
optional: true
type: map
items:
type: string
length:
max: 60
skip_verify_certificate:
description: "(For https protocol) Skip verifying the HTTPS certificate."
env_name: "PANOS_SKIP_VERIFY_CERTIFICATE"
Expand Down
2 changes: 2 additions & 0 deletions cmd/codegen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,6 @@ func main() {
// Execute Terraform
runCommand(ctx, properties.CommandTypeTerraform, cfg.ConfigFile)
}

log.Println("Generation complete.")
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module github.com/paloaltonetworks/pan-os-codegen

require (
github.com/stretchr/testify v1.9.0
golang.org/x/text v0.16.0
gopkg.in/yaml.v3 v3.0.1
)

Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Expand Down
30 changes: 17 additions & 13 deletions pkg/commands/codegen/codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ import (
"github.com/paloaltonetworks/pan-os-codegen/pkg/properties"
)

type CommandType string

const (
CommandTypeSDK CommandType = "sdk"
CommandTypeTerraform CommandType = "terraform"
)

type Command struct {
ctx context.Context
args []string
Expand Down Expand Up @@ -42,7 +49,7 @@ func (c *Command) Setup() error {
if c.specs == nil {
c.specs, err = properties.GetNormalizations()
if err != nil {
return fmt.Errorf("error getting normalizations: %w", err)
return fmt.Errorf("error getting normalizations: %s", err)
}
}
return nil
Expand All @@ -58,12 +65,12 @@ func (c *Command) Execute() error {

content, err := load.File(configPath)
if err != nil {
return fmt.Errorf("error loading %s: %w", configPath, err)
return fmt.Errorf("error loading %s - %s", configPath, err)
}

config, err := properties.ParseConfig(content)
if err != nil {
return fmt.Errorf("error parsing %s: %w", configPath, err)
return fmt.Errorf("error parsing %s - %s", configPath, err)
}
var resourceList []string
var dataSourceList []string
Expand All @@ -72,16 +79,16 @@ func (c *Command) Execute() error {
log.Printf("Parsing %s...\n", specPath)
content, err := load.File(specPath)
if err != nil {
return fmt.Errorf("error loading %s: %w", specPath, err)
return fmt.Errorf("error loading %s - %s", specPath, err)
}

spec, err := properties.ParseSpec(content)
if err != nil {
return fmt.Errorf("error parsing %s: %w", specPath, err)
return fmt.Errorf("error parsing %s - %s", specPath, err)
}

if err = spec.Sanity(); err != nil {
return fmt.Errorf("%s sanity failed: %w", specPath, err)
return fmt.Errorf("%s sanity failed: %s", specPath, err)
}

if c.commandType == properties.CommandTypeTerraform {
Expand All @@ -90,7 +97,7 @@ func (c *Command) Execute() error {
terraformGenerator := generate.NewCreator(config.Output.TerraformProvider, c.templatePath, spec)
err = terraformGenerator.RenderTerraformProviderFile(newProviderObject, spec)
if err != nil {
return fmt.Errorf("error generating Terraform provider: %w", err)
return fmt.Errorf("error rendering Terraform provider file for %s - %s", specPath, err)
}

resourceList = append(resourceList, newProviderObject.Resources...)
Expand All @@ -99,7 +106,7 @@ func (c *Command) Execute() error {
} else if c.commandType == properties.CommandTypeSDK {
generator := generate.NewCreator(config.Output.GoSdk, c.templatePath, spec)
if err = generator.RenderTemplate(); err != nil {
return fmt.Errorf("error rendering %s: %w", specPath, err)
return fmt.Errorf("error rendering %s - %s", specPath, err)
}
}

Expand All @@ -116,16 +123,13 @@ func (c *Command) Execute() error {
terraformGenerator := generate.NewCreator(config.Output.TerraformProvider, c.templatePath, providerSpec)
err = terraformGenerator.RenderTerraformProvider(newProviderObject, providerSpec, config.TerraformProviderConfig)
if err != nil {
return err
return fmt.Errorf("error generating terraform code: %w", err)
}
log.Printf("Generated Terraform resources: %s\n Generated dataSources: %s", resourceList, dataSourceList)
}

if err = generate.CopyAssets(config, c.commandType); err != nil {
return fmt.Errorf("error copying assets %w", err)
}

log.Println("Generation complete.")

log.Printf("Generated resources: %s\n Generated dataSources: %s", resourceList, dataSourceList)
return nil
}
97 changes: 58 additions & 39 deletions pkg/generate/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package generate
import (
"bytes"
"fmt"
"go/format"
"io"
"log"
"os"
Expand All @@ -22,7 +23,7 @@ type Creator struct {
Spec *properties.Normalization
}

// NewCreator initialize Creator instance.
// NewCreator initializes a Creator instance.
func NewCreator(goOutputDir, templatesDir string, spec *properties.Normalization) *Creator {
return &Creator{
GoOutputDir: goOutputDir,
Expand All @@ -31,7 +32,7 @@ func NewCreator(goOutputDir, templatesDir string, spec *properties.Normalization
}
}

// RenderTemplate loop through all templates, parse them and render content, which is saved to output file.
// RenderTemplate loops through all templates, parses them, and renders content, which is saved to the output file.
func (c *Creator) RenderTemplate() error {
log.Println("Start rendering templates")

Expand All @@ -48,28 +49,14 @@ func (c *Creator) RenderTemplate() error {
return fmt.Errorf("error creating directories for %s: %w", filePath, err)
}

tmpl, err := c.parseTemplate(templateName)
if err != nil {
return fmt.Errorf("error parsing template %s: %w", templateName, err)
}

var data bytes.Buffer
if err := tmpl.Execute(&data, c.Spec); err != nil {
return fmt.Errorf("error executing template %s: %w", templateName, err)
}
// If from template no data was rendered (e.g. for DNS spec entry should not be created),
// then we don't need to create empty file (e.g. `entry.go`) with no content
if data.Len() > 0 {
if err := c.createAndWriteFile(filePath, &data); err != nil {
return fmt.Errorf("error creating and writing to file %s: %w", filePath, err)
}
if err := c.processTemplate(templateName, filePath); err != nil {
return err
}
}
return nil
}

// RenderTerraformProviderFile generates a Go file for a Terraform provider based on the provided TerraformProviderFile and Normalization arguments.
// It calls terraform.GenerateTerraformResource() passing the Normalization specification and the TerraformProviderFile.
func (c *Creator) RenderTerraformProviderFile(terraformProvider *properties.TerraformProviderFile, spec *properties.Normalization) error {
tfp := terraform_provider.GenerateTerraformProvider{}

Expand All @@ -81,42 +68,72 @@ func (c *Creator) RenderTerraformProviderFile(terraformProvider *properties.Terr
return err
}

if err := tfp.GenerateTerraformProviderFile(spec, terraformProvider); err != nil {
if err := tfp.GenerateCommonCode(spec, terraformProvider); err != nil {
return err
}
filePath := c.createTerraformProviderFilePath(spec.TerraformProviderConfig.Suffix)

content := bytes.NewBufferString(terraformProvider.Code.String())
if err := c.createFileAndWriteContent(filePath, content); err != nil {
if err := tfp.GenerateTerraformProviderFile(spec, terraformProvider); err != nil {
return err
}
filePath := c.createTerraformProviderFilePath(spec.TerraformProviderConfig.Suffix)

return nil
return c.writeFormattedContentToFile(filePath, terraformProvider.Code.String())
}

// RenderTerraformProvider generates and writes a Terraform provider file.
func (c *Creator) RenderTerraformProvider(terraformProvider *properties.TerraformProviderFile, spec *properties.Normalization, providerConfig properties.TerraformProvider) error {
tfp := terraform_provider.GenerateTerraformProvider{}
if err := tfp.GenerateTerraformProvider(terraformProvider, spec, providerConfig); err != nil {
return err
}
filePath := c.createTerraformProviderFilePath(spec.Name)

content := bytes.NewBufferString(terraformProvider.Code.String())
if err := c.createFileAndWriteContent(filePath, content); err != nil {
return err
return c.writeFormattedContentToFile(filePath, terraformProvider.Code.String())
}

// processTemplate processes a single template and writes the rendered content to a file.
func (c *Creator) processTemplate(templateName, filePath string) error {
tmpl, err := c.parseTemplate(templateName)
if err != nil {
return fmt.Errorf("error parsing template %s: %w", templateName, err)
}

var data bytes.Buffer
if err := tmpl.Execute(&data, c.Spec); err != nil {
return fmt.Errorf("error executing template %s: %w", templateName, err)
}

// If no data was rendered from the template, skip creating an empty file.
dataLength := len(bytes.TrimSpace(data.Bytes()))
if dataLength > 0 {
formattedCode, err := format.Source(data.Bytes())
if err != nil {
return fmt.Errorf("error formatting code %w", err)
}
formattedBuf := bytes.NewBuffer(formattedCode)

if err := c.createAndWriteFile(filePath, formattedBuf); err != nil {
return fmt.Errorf("error creating and writing to file %s: %w", filePath, err)
}
}
return nil
}

// createTerraformProviderFilePath returns a file path for a Terraform provider based on the provided suffix.
func (c *Creator) createTerraformProviderFilePath(terraformProviderFileName string) string {
terraformProviderFileNameSuffix := "_object"

if terraformProviderFileName == "provider" {
terraformProviderFileNameSuffix = ""
// writeFormattedContentToFile formats the content and writes it to a file.
func (c *Creator) writeFormattedContentToFile(filePath, content string) error {
formattedCode, err := format.Source([]byte(content))
if err != nil {
log.Printf("provided content: %s", content)
return fmt.Errorf("error formatting code: %w", err)
}
formattedBuf := bytes.NewBuffer(formattedCode)

fileName := fmt.Sprintf("%s%s.go", terraformProviderFileName, terraformProviderFileNameSuffix)
return c.createFileAndWriteContent(filePath, formattedBuf)
}

// createTerraformProviderFilePath returns a file path for a Terraform provider based on the provided suffix.
func (c *Creator) createTerraformProviderFilePath(terraformProviderFileName string) string {
fileName := fmt.Sprintf("%s.go", terraformProviderFileName)
return filepath.Join(c.GoOutputDir, "internal/provider", fileName)
}

Expand All @@ -138,18 +155,20 @@ func (c *Creator) createAndWriteFile(filePath string, content *bytes.Buffer) err
if err != nil {
return err
}
defer outputFile.Close()
defer func(outputFile *os.File) {
_ = outputFile.Close()
}(outputFile)

return writeContentToFile(content, outputFile)
}

// createFullFilePath returns a full path for output file generated from template passed as argument to function.
// createFullFilePath returns a full path for the output file generated from the template passed as an argument.
func (c *Creator) createFullFilePath(templateName string) string {
fileBaseName := strings.TrimSuffix(templateName, filepath.Ext(templateName))
return filepath.Join(c.GoOutputDir, filepath.Join(c.Spec.GoSdkPath...), fmt.Sprintf("%s.go", fileBaseName))
}

// listOfTemplates return list of templates defined in TemplatesDir.
// listOfTemplates returns a list of templates defined in TemplatesDir.
func (c *Creator) listOfTemplates() ([]string, error) {
var files []string
err := filepath.WalkDir(c.TemplatesDir, func(path string, entry os.DirEntry, err error) error {
Expand All @@ -170,13 +189,13 @@ func (c *Creator) listOfTemplates() ([]string, error) {
return files, nil
}

// makeAllDirs creates all required directories, which are in the file path.
// makeAllDirs creates all required directories in the file path.
func (c *Creator) makeAllDirs(filePath string) error {
dirPath := filepath.Dir(filePath)
return os.MkdirAll(dirPath, os.ModePerm)
}

// createFile just create a file and return it.
// createFile creates a file and returns it.
func (c *Creator) createFile(filePath string) (*os.File, error) {
outputFile, err := os.Create(filePath)
if err != nil {
Expand All @@ -193,7 +212,7 @@ func writeContentToFile(content *bytes.Buffer, file *os.File) error {
return nil
}

// parseTemplate parse template passed as argument and with function map defined below.
// parseTemplate parses the template passed as an argument with the function map defined below.
func (c *Creator) parseTemplate(templateName string) (*template.Template, error) {
templatePath := filepath.Join(c.TemplatesDir, templateName)
funcMap := template.FuncMap{
Expand Down
Loading