Skip to content
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

feat: add terraform provider template #41

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
92f0aae
Refactor template processing in generator
Mar 15, 2024
8fd2a2c
Add option for creating terraform provider alongside gosdk
Mar 29, 2024
5071a16
Add static files for terraform provider
Mar 29, 2024
e7e3648
Generate exclusive file feature
Mar 29, 2024
0788bc3
Copy assets to the correct directories
Mar 29, 2024
a6e3205
add test files for further template
pimielowski Apr 2, 2024
89e075a
Merge branch 'refs/heads/main' into feat-add-terraform-provider-template
Apr 4, 2024
23af4d9
Distinguish which type of run CodeGen is running
Apr 4, 2024
5987571
Add Makefile and update go.sum dependencies
Apr 4, 2024
5ff1321
Fix CI
Apr 4, 2024
7f4c5f5
Fix normalized_test.go tests
Apr 4, 2024
be7c72b
Refactor imports and implement Terraform provider template
pimielowski Apr 8, 2024
a4d039f
add go.sum for assets
pimielowski Apr 8, 2024
5605f33
fix ci/cd
pimielowski Apr 8, 2024
ff1c464
add test go file
pimielowski Apr 8, 2024
386bfee
Update configuration and add scripts for the Terraform provider
pimielowski Apr 8, 2024
dcde5ca
rename test file
pimielowski Apr 8, 2024
e8bf556
fix cicd
pimielowski Apr 8, 2024
7511854
Update package paths in config and move files to SDK folder
pimielowski Apr 8, 2024
1c12558
add license to the sdk assets
pimielowski Apr 8, 2024
0548e23
Refactor and update tests for improved code
pimielowski Apr 8, 2024
9c54dd2
Improve template file filter in generator
pimielowski Apr 8, 2024
7d0da05
Update generator and terraform template, fix logging and remove debug…
pimielowski Apr 9, 2024
8f7889e
Remove spec params from provider.yaml
pimielowski Apr 9, 2024
d5fc6d4
Remove unnecessary comments in code
pimielowski Apr 10, 2024
f581520
Merge branch 'refs/heads/main' into feat-add-terraform-provider-template
pimielowski Apr 10, 2024
f4aba9d
Update RenderImports to accept multiple template types
pimielowski Apr 10, 2024
dc9bd3d
Refactor CreateResourceList function in terraform/provider.go
pimielowski Apr 10, 2024
477d1ab
Update Terraform Provider code generation and release workflow
pimielowski Apr 10, 2024
a38d4ff
Remove tf_provider_scripts from codegen config
pimielowski Apr 10, 2024
af2f5d5
Remove exclusive field and add custom templates support
pimielowski Apr 10, 2024
70e3a35
Simplify codegen test and modify error handling
pimielowski Apr 10, 2024
a0ff119
Merge branch 'refs/heads/main' into feat-add-terraform-provider-template
pimielowski Apr 12, 2024
5801ce6
Remove redundant code files in the tf_provider directory
pimielowski Apr 12, 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
14 changes: 14 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
all: test_go build test_sdk

build:
go run ./cmd/codegen/main.go

test_sdk:
cd ../generated/pango && \
go run ./example/main.go

clean:
rm -rf ../generated

test_go:
go test -v ./...
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
373 changes: 373 additions & 0 deletions assets/sdk/sdk/LICENSE

Large diffs are not rendered by default.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
23 changes: 23 additions & 0 deletions assets/tf_provider/examples/data-sources/panos_tfid/data-source.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Note that IDs created by this data source may not exactly match IDs created
# by a given resource, but it will be compatible and a valid ID for
# doing state imports.

# Example of how to create the ID for panos_nested_address_object named
# "foo" in vsys1.
#
# All variables should be specified for a given location, default value or not.
data "panos_tfid" "example1" {
name = "foo"
location = "vsys"
variables = {
"name" : "vsys1",
"ngfw_device" : "localhost.localdomain",
}
}

# Example of how to create the ID for panos_nested_address_object
# named "foo" in shared.
data "panos_tfid" "example2" {
name = "foo"
location = "shared"
}
24 changes: 24 additions & 0 deletions assets/tf_provider/examples/provider/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Traditional provider example.
provider "panos" {
hostname = "10.1.1.1"
username = "admin"
password = "secret"
}

# Local inspection mode provider example.
provider "panos" {
config_file = file("/tmp/candidate-config.xml")

# This is only used if a "detail-version" attribute is not present in
# the exported XML schema. If it's there, this can be omitted.
panos_version = "10.2.0"
}

terraform {
required_providers {
panos = {
source = "paloaltonetworks/terraform-provider-panos"
version = "2.0.0"
}
}
}
16 changes: 16 additions & 0 deletions assets/tf_provider/internal/provider/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package provider

import (
"github.com/PaloAltoNetworks/pango/errors"
)

var InspectionModeError = "Resources are unavailable when the provider is in inspection mode. Resources are only available in API mode."

func IsObjectNotFound(e error) bool {
e2, ok := e.(errors.Panos)
if ok && e2.ObjectNotFound() {
return true
}

return false
}
141 changes: 141 additions & 0 deletions assets/tf_provider/internal/provider/tfid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package provider

import (
"context"

"github.com/PaloAltoNetworks/pango"

"github.com/hashicorp/terraform-plugin-framework/datasource"
dsschema "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
)

type genericTfid struct {
Name *string `json:"name,omitempty"`
Names []string `json:"names,omitempty"`
Location map[string]any `json:"location"`
}

func (g genericTfid) IsValid() error { return nil }

// Data source.
var (
_ datasource.DataSource = &tfidDataSource{}
_ datasource.DataSourceWithConfigure = &tfidDataSource{}
)

func NewTfidDataSource() datasource.DataSource {
return &tfidDataSource{}
}

type tfidDataSource struct {
client *pango.XmlApiClient
}

type tfidDsModel struct {
Location types.String `tfsdk:"location"`
Variables types.Map `tfsdk:"variables"`
Name types.String `tfsdk:"name"`
Names types.List `tfsdk:"names"`

Tfid types.String `tfsdk:"tfid"`
}

func (d *tfidDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_tfid"
}

func (d *tfidDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = dsschema.Schema{
Description: "Helper data source: create a tfid from the given information. Note that the tfid ouptut from this data source may not exactly match what a resource uses, but it will still be a valid ID to use for resource imports.",

Attributes: map[string]dsschema.Attribute{
"location": dsschema.StringAttribute{
Description: "The location path name.",
Required: true,
},
"variables": dsschema.MapAttribute{
Description: "The variables and values for the specified location.",
Optional: true,
ElementType: types.StringType,
},
"name": dsschema.StringAttribute{
Description: "(Singleton resource) The config's name.",
Optional: true,
},
"names": dsschema.ListAttribute{
Description: "(Grouping resources) The names of the configs.",
Optional: true,
ElementType: types.StringType,
},
"tfid": dsschema.StringAttribute{
Description: "The tfid created from the given parts.",
Computed: true,
},
},
}
}

func (d *tfidDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
if req.ProviderData == nil {
return
}

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

func (d *tfidDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var state tfidDsModel
resp.Diagnostics.Append(req.Config.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}

// Basic logging.
tflog.Info(ctx, "performing data source read", map[string]any{
"data_source_name": "panos_tfid",
})

vars := make(map[string]types.String, len(state.Variables.Elements()))
resp.Diagnostics.Append(state.Variables.ElementsAs(ctx, &vars, false).Errors()...)

var names []string
resp.Diagnostics.Append(state.Names.ElementsAs(ctx, &names, false)...)

if resp.Diagnostics.HasError() {
return
}

loc := genericTfid{
Name: state.Name.ValueStringPointer(),
Names: append([]string(nil), names...),
}

if len(vars) == 0 {
loc.Location = map[string]any{
state.Location.ValueString(): true,
}
} else {
content := make(map[string]string)
for key, value := range vars {
content[key] = value.ValueString()
}
loc.Location = map[string]any{
state.Location.ValueString(): content,
}
}

// Encode the tfid from the info given.
idstr, err := EncodeLocation(loc)
if err != nil {
resp.Diagnostics.AddError("error encoding tfid", err.Error())
return
}

// Set the tfid param.
state.Tfid = types.StringValue(idstr)

// Done.
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
}
77 changes: 77 additions & 0 deletions assets/tf_provider/internal/provider/tools.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package provider

import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"strings"
)

type Locationer interface {
IsValid() error
}

func EncodeLocation(loc Locationer) (string, error) {
b, err := json.Marshal(loc)
if err != nil {
return "", err
}

return base64.StdEncoding.EncodeToString(b), nil
}

func DecodeLocation(s string, loc Locationer) error {
b, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return err
}

if err = json.Unmarshal(b, loc); err != nil {
return err
}

return loc.IsValid()
}

func base64Encode(v []string) string {
var buf bytes.Buffer

for i := range v {
if i != 0 {
buf.WriteString("\n")
}
buf.WriteString(v[i])
}

return base64.StdEncoding.EncodeToString(buf.Bytes())
}

func base64Decode(v string) []string {
joined, err := base64.StdEncoding.DecodeString(v)
if err != nil {
return nil
}

return strings.Split(string(joined), "\n")
}

func ProviderParamDescription(desc, defaultValue, envName, jsonName string) string {
var b strings.Builder

b.WriteString(desc)

if defaultValue != "" {
b.WriteString(fmt.Sprintf(" Default: `%s`.", defaultValue))
}

if envName != "" {
b.WriteString(fmt.Sprintf(" Environment variable: `%s`.", envName))
}

if jsonName != "" {
b.WriteString(fmt.Sprintf(" JSON config file variable: `%s`.", jsonName))
}

return b.String()
}
11 changes: 11 additions & 0 deletions assets/tf_provider/tf_provider/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module github.com/paloaltonetworks/terraform-provider-panos

require (
github.com/PaloAltoNetworks/pango v0.10.3-0.20240408115758-216d8509e7cf
github.com/hashicorp/terraform-plugin-docs v0.18.0
github.com/hashicorp/terraform-plugin-framework v1.5.0
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0
github.com/hashicorp/terraform-plugin-log v0.9.0
)

go 1.21.4
7 changes: 7 additions & 0 deletions assets/tf_provider/tf_provider/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
github.com/PaloAltoNetworks/pango v0.10.2/go.mod h1:GztcRnVLur7G+VFG7Z5ZKNFgScLtsycwPMp1qVebE5g=
github.com/PaloAltoNetworks/pango v0.10.3-0.20240308150716-638262802beb/go.mod h1:rBeRstPWCr2u0qi8w4kbBAzbnhtdq33OLrJBg6sJSqk=
github.com/PaloAltoNetworks/pango v0.10.3-0.20240408115758-216d8509e7cf/go.mod h1:rBeRstPWCr2u0qi8w4kbBAzbnhtdq33OLrJBg6sJSqk=
github.com/hashicorp/terraform-plugin-docs v0.18.0/go.mod h1:iIUfaJpdUmpi+rI42Kgq+63jAjI8aZVTyxp3Bvk9Hg8=
github.com/hashicorp/terraform-plugin-framework v1.5.0/go.mod h1:6waavirukIlFpVpthbGd2PUNYaFedB0RwW3MDzJ/rtc=
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0/go.mod h1:jfHGE/gzjxYz6XoUwi/aYiiKrJDeutQNUtGQXkaHklg=
github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow=
48 changes: 48 additions & 0 deletions assets/tf_provider/tf_provider/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package main

import (
"context"
"flag"
"log"

"github.com/paloaltonetworks/terraform-provider-panos/internal/provider"

"github.com/hashicorp/terraform-plugin-framework/providerserver"
)

// Run "go generate" to format example terraform files and generate the docs for the registry/website

// If you do not have terraform installed, you can remove the formatting command, but its suggested to
// ensure the documentation is formatted properly.
//go:generate terraform fmt -recursive ./examples/

// Run the docs generation tool, check its repository for more information on how it works and how docs
// can be customized.
//go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs generate --provider-name panos

var (
// these will be set by the goreleaser configuration
// to appropriate values for the compiled binary.
version string = "dev"

// goreleaser can pass other information to the main package, such as the specific commit
// https://goreleaser.com/cookbooks/using-main.version/
)

func main() {
var debug bool

flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve")
flag.Parse()

opts := providerserver.ServeOpts{
Address: "registry.terraform.io/paloaltonetworks/panos",
Debug: debug,
}

err := providerserver.Serve(context.Background(), provider.New(version), opts)

if err != nil {
log.Fatal(err.Error())
}
}
Loading