Skip to content

Commit

Permalink
working templated typed resource
Browse files Browse the repository at this point in the history
  • Loading branch information
jackofallops committed Jul 24, 2021
1 parent 170a120 commit 15c62e5
Show file tree
Hide file tree
Showing 16 changed files with 399 additions and 2 deletions.
7 changes: 5 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,8 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/
# Dependency directories
vendor/

#generated outputs
internal/
1 change: 1 addition & 0 deletions .go-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.16.6
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/tfpdk.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
# tfpdk
Terraform Provider Development Kit

## TODO
- [ ] untyped resource template
- [ ] `go fmt` outputs
- [ ] typed and untyped Data Sources
- [ ] init a new provider - git clone [scaffold](https://github.com/hashicorp/terraform-provider-scaffolding)?
- [ ] optionally populate the Typed SDK into the init step?
- [ ] Dynamically read provider name for item generation
- [ ] Populate `IDValidationFunc()` in template for IDs (Pandora)
17 changes: 17 additions & 0 deletions commands/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package commands

import (
"strings"
"text/template"

"github.com/iancoleman/strcase"
"github.com/jackofallops/tfpdk/helpers"
)

var TplFuncMap = template.FuncMap{
"ToLower": strings.ToLower,
"ToTitle": strings.Title,
"ToCamel": strcase.ToCamel,
"ToSnake": strcase.ToSnake,
"TfName": helpers.TerraformResourceName,
}
16 changes: 16 additions & 0 deletions commands/initialise.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package commands

type InitialiseCommand struct{}

func (i InitialiseCommand) Run(args []string) int {
// TODO - Clone the scaffold project to path from `args`?
return 0
}

func (InitialiseCommand) Help() string {
return ""
}

func (InitialiseCommand) Synopsis() string {
return ""
}
107 changes: 107 additions & 0 deletions commands/resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package commands

import (
"fmt"
"os"
"strings"
"text/template"

"github.com/iancoleman/strcase"
)

type ResourceCommand struct {
}

type TypedResourceData struct {
Name string
ServicePackage string
NoUpdate bool
ProviderName string
Typed bool
}

func (r ResourceCommand) Run(args []string) int {
data := TypedResourceData{
ProviderName: "azurerm", // Defaulting for now, will need to eval from os.path later
Typed: true, //defaulting to true for now, since AzureRM is the prototype target
}
if len(args) == 0 {
fmt.Print(r.Help())
return 1
}
for _, v := range args {
arg := strings.Split(v, "=")
if len(arg) > 2 {
fmt.Printf("malformed argument %q", arg)
return 1
}

switch strings.ToLower(strings.TrimLeft(arg[0], "-")) {
case "name":
data.Name = arg[1]
case "no-update":
data.NoUpdate = true
case "servicepackage":
data.ServicePackage = arg[1]
case "typed":
data.Typed = true
default:
fmt.Printf("unrecognised option %q", arg[0])
return 1
}
}

tpl := template.Must(template.New("resource.gotpl").Funcs(TplFuncMap).ParseFS(Templatedir, "templates/resource.gotpl"))

// output file - Note we assume that the PATH to the file already exists, having been through init and, optionally, service package creation
outputPath := ""
if data.ServicePackage != "" {
outputPath = fmt.Sprintf("%s/internal/services/%s/%s_resource.go", strings.ToLower(data.ProviderName), strings.ToLower(strcase.ToCamel(data.ServicePackage)), strcase.ToSnake(data.Name))
} else {
outputPath = fmt.Sprintf("%s/internal/%s_resource.go", data.ProviderName, strcase.ToSnake(data.Name))
}

if _, err := os.Stat(outputPath); err == nil {
fmt.Printf("Error: A resource with this name already exists and will not be overwritten. Please remove this file if you wish to regenerate.")
return 1
}
f, err := os.Create(outputPath)
if err != nil {
fmt.Printf("Error: failed opening output resource file for writing: %+v", err.Error())
return 1
}

err = tpl.Execute(f, data)
if err != nil {
fmt.Println(err.Error())
return 1
}
if err := f.Close(); err != nil {
fmt.Printf("Error: Writing to file: %+v", err.Error())
}

return 0
}

func (r ResourceCommand) Help() string {
return fmt.Sprintf(`
Usage: tfpdk resource [options]
Generates a scaffolded resource, optionally under a service package for this provider
Options:
-name=string (Required) the name of the new resource, can be in the form resource_name, ResourceName, or resource-name
-servicepackage=string (Optional) place the resource under the named service package
-no-update (Optional) Don't generate an update func. Use this for resources that cannot be updated in place. Note all schema properties must be 'ForceNew: true'
-typed (Optional) Generate a resource for use with the Typed Resource SDK
`)
}

func (r ResourceCommand) Synopsis() string {
return ""
}
9 changes: 9 additions & 0 deletions commands/templatefs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package commands

import (
"embed"
)

//go:embed templates/*
var Templatedir embed.FS

90 changes: 90 additions & 0 deletions commands/templates/resource.gotpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package {{if .Typed}} {{- ToLower .ServicePackage}}

import (
"context"
"time"

"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/sdk"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/pluginsdk"
)

type {{ToCamel .Name}}Resource struct{}

type {{ToCamel .Name}}Model struct {
// TODO - Schema in Go Types format here
}

{{if .NoUpdate}}var _ sdk.Resource = {{else}}var _ sdk.ResourceWithUpdate = {{end}}{{ToCamel .Name}}Resource{}

func (r {{ToCamel .Name}}Resource) ModelObject() interface{} {
return {{ToCamel .Name}}Model{}
}

func (r {{ToCamel .Name}}Resource) ResourceType() string {
return "{{- TfName .ProviderName .Name -}}"
}

func (r {{ToCamel .Name}}Resource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
panic("Implement me") // TODO - Add Validation func return here
}

func (r {{ToCamel .Name}}Resource) Arguments() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
/*
TODO - This sections is for configurable items, `Required: true` items first, followed by`Optional: true`,
both in alphabetical order
*/
}
}

func (r {{ToCamel .Name}}Resource) Attributes() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
/*
TODO - This section is for `Computed: true` only items, i.e. useful values that are returned by the resource
that can be used as outputs or passed programmatically to other resources or data sources.
*/
}
}

func (r {{ToCamel .Name}}Resource) Create() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
// TODO - Create Func
return nil
},
}
}

func (r {{ToCamel .Name}}Resource) Read() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 5 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
// TODO - Read Func
return nil
},
}
}

func (r {{ToCamel .Name}}Resource) Delete() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 5 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
// TODO - Delete Func
return nil
},
}
}
{{if not .NoUpdate}}
func (r {{ToCamel .Name}}Resource) Update() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
// TODO - Delete Func
return nil
},
}
}
{{end}}
{{else}} {{- ToLower .ProviderName}} {{end}}
{{/* TODO - Untyped resource */}}
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/jackofallops/tfpdk

go 1.16

require (
github.com/iancoleman/strcase v0.2.0
github.com/mitchellh/cli v1.1.2
)
56 changes: 56 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg=
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60=
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mitchellh/cli v1.1.2 h1:PvH+lL2B7IQ101xQL63Of8yFS2y+aDlsFcsqNc+u/Kw=
github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4=
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
13 changes: 13 additions & 0 deletions helpers/formatters.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package helpers

import (
"fmt"
"strings"

"github.com/iancoleman/strcase"
)

func TerraformResourceName(provider, resourceName string) string {
fmtStr := "%s_%s"
return fmt.Sprintf(fmtStr, strings.ToLower(provider), strcase.ToSnake(resourceName))
}
Loading

0 comments on commit 15c62e5

Please sign in to comment.