Skip to content

Commit

Permalink
Added initial boilerplate generator (#1496)
Browse files Browse the repository at this point in the history
  • Loading branch information
nfx committed Jul 25, 2022
1 parent bf5879f commit d341963
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ You can [run provider in a debug mode](https://www.terraform.io/plugin/sdkv2/deb

## Adding a new resource

Boilerplate for data sources could be generated via `go run provider/gen/main.go -name mws_workspaces -package mws -is-data -dry-run=false`.

The general process for adding a new resource is:

*Define the resource models.* The models for a resource are `struct`s defining the schemas of the objects in the Databricks REST API. Define structures used for multiple resources in a common `models.go` file; otherwise, you can define these directly in your resource file. An example model:
Expand Down
21 changes: 21 additions & 0 deletions provider/gen/data_x.go.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package {{.Package}}

import (
"context"

"github.com/databricks/terraform-provider-databricks/common"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func DataSource{{.CamelName}}() *schema.Resource {
type {{.LowerCamel}}Data struct {
// data resources output fields annotated with tf:"computed"
Ids []string {{.BT}}json:"ids,omitempty" tf:"computed,slice_set"{{.BT}}
}
return common.DataResource({{.LowerCamel}}Data{}, func(ctx context.Context, e any, c *common.DatabricksClient) error {
data := e.(*{{.LowerCamel}}Data)
// TODO: implement me
data.Ids = append(data.Ids, "..") // replace
return nil
})
}
32 changes: 32 additions & 0 deletions provider/gen/data_x.md.go.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
subcategory: "{{.DocCategory}}"
---
# databricks_{{.Name}} Data Source

-> **Note** If you have a fully automated setup with workspaces created by [databricks_mws_workspaces](../resources/mws_workspaces.md) or [azurerm_databricks_workspace](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/databricks_workspace), please make sure to add [depends_on attribute](../index.md#data-resources-and-authentication-is-not-configured-errors) in order to prevent _authentication is not configured for provider_ errors.

TODO: write me

## Example Usage

Doing X:

```hcl
data "databricks_{{.Name}}" "all" {}

output "all_{{.Name}}" {
value = data.databricks_{{.Name}}.all
}
```

## Attribute Reference

This data source exports the following attributes:

* `add_field_name` - write doc

## Related Resources

The following resources are used in the same context:

* TODO: write me
17 changes: 17 additions & 0 deletions provider/gen/data_x_acctest.go.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package acceptance

import (
"github.com/databricks/terraform-provider-databricks/internal/acceptance"

"testing"
)

func TestAccDataSource{{.CamelName}}(t *testing.T) {
acceptance.Test(t, []acceptance.Step{
{
Template: `
data "databricks_{{.Name}}" "this" {
}`,
},
})
}
16 changes: 16 additions & 0 deletions provider/gen/data_x_manual.go.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
In provider/provider.go, add an entry to `DataSourcesMap`

```go
func DatabricksProvider() *schema.Provider {
p := &schema.Provider{
DataSourcesMap: map[string]*schema.Resource{ // must be in alphabetical order
// ...
"databricks_{{.Name}}": {{.Package}}.DataSource{{.CamelName}}(),

```

In README.md, add an entry in alphabetical order:

```
| [databricks_{{.Name}}](docs/data-sources/{{.Name}}.md) data
```
29 changes: 29 additions & 0 deletions provider/gen/data_x_test.go.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package {{.Package}}

import (
"testing"

"github.com/databricks/terraform-provider-databricks/qa"
)

func TestDataSource{{.CamelName}}(t *testing.T) {
qa.ResourceFixture{
Fixtures: []qa.HTTPFixture{
// TODO: run this test to get fixtures
},
Resource: DataSource{{.CamelName}}(),
Read: true,
NonWritable: true,
ID: "_",
}.ApplyNoError(t)
}

func TestCatalogsData_Error(t *testing.T) {
qa.ResourceFixture{
Fixtures: qa.HTTPFailures,
Resource: DataSource{{.CamelName}}(),
Read: true,
NonWritable: true,
ID: "_",
}.ExpectError(t, "I'm a teapot")
}
120 changes: 120 additions & 0 deletions provider/gen/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//go:build ignore
// +build ignore

// Usage: go run provider/gen/main.go -name mws_workspaces -package mws -is-data -dry-run=false
//
package main

import (
"errors"
"flag"
"fmt"
"html/template"
"os"
"strings"
)

var ctx Context

func main() {
flag.StringVar(&ctx.Name, "name", "", "name of the resource, like `mws_workspaces`")
flag.StringVar(&ctx.Package, "package", "", "package of the resource, like `clusters`")
flag.StringVar(&ctx.DocCategory, "category", "Other", "documentation category")
flag.BoolVar(&ctx.DataSource, "is-data", false, "is this a data resource")
flag.BoolVar(&ctx.DryRun, "dry-run", true, "print to stdout instead of real files")

flag.Parse()

if ctx.Name == "" || ctx.Package == "" {
println("USAGE: go run provider/gen/main.go -name res -package pkg")
flag.PrintDefaults()
os.Exit(1)
}

err := ctx.Generate()
if err != nil {
fmt.Printf("ERROR: %s\n\n", err)
os.Exit(1)
}
}

type Context struct {
Package string
Name string
DocCategory string
DataSource bool
DryRun bool
BT string

tmpl *template.Template
}

func (c *Context) Generate() error {
if !c.DataSource {
return errors.New("only data sources are supported now")
}
c.BT = "`"
c.tmpl = template.Must(template.ParseGlob("provider/gen/*.go.tmpl"))
return c.FileSet(map[string]string{
"data_x.go.tmpl": "{{.Package}}/data_{{.Name}}.go",
"data_x_test.go.tmpl": "{{.Package}}/data_{{.Name}}_test.go",
"data_x_acctest.go.tmpl": "{{.Package}}/acceptance/data_{{.Name}}_test.go",
"data_x.md.go.tmpl": "docs/data-sources/{{.Name}}.md",
"data_x_manual.go.tmpl": "stdout",
})
}

func (c *Context) FileSet(m map[string]string) (err error) {
for k, v := range m {
err = c.File(k, v)
if err != nil {
return err
}
}
return nil
}

func (c *Context) File(contentTRef, nameT string) error {
nt, err := template.New("filename").Parse(nameT)
if err != nil {
return fmt.Errorf("parse %s: %w", nameT, err)
}
var filename, contents strings.Builder
err = nt.Execute(&filename, c)
if err != nil {
return fmt.Errorf("exec %s: %w", nameT, err)
}
err = c.tmpl.ExecuteTemplate(&contents, contentTRef, &ctx)
if err != nil {
return fmt.Errorf("exec %s: %w", contentTRef, err)
}
if c.DryRun {
fmt.Printf("\n---\nDRY RUN: %s\n---\n%s", &filename, &contents)
return nil
}
if nameT == "stdout" {
println(contents.String())
return nil
}
file, err := os.OpenFile(filename.String(), os.O_CREATE|os.O_WRONLY, 0755)
if err != nil {
return fmt.Errorf("open %s: %w", &filename, err)
}
_, err = file.WriteString(contents.String())
if err != nil {
return fmt.Errorf("write %s: %w", &filename, err)
}
return file.Close()
}

func (c *Context) CamelName() string {
return strings.ReplaceAll(
strings.Title(
strings.ReplaceAll(
c.Name, "_", " ")), " ", "")
}

func (c *Context) LowerCamel() string {
cc := c.CamelName()
return strings.ToLower(cc[0:1]) + cc[1:]
}

0 comments on commit d341963

Please sign in to comment.