Skip to content

Commit

Permalink
provider/template: convert resources to data sources
Browse files Browse the repository at this point in the history
The template resources don't actually need to retain any state, so they
are good candidates to be data sources.

This includes a few tweaks to the acceptance tests -- now configured to
run as unit tests -- since it seems that they have been slightly broken
for a while now. In particular, the "update" cases are no longer tested
because updating is not a meaningful operation for a data source.
  • Loading branch information
apparentlymart committed Jun 6, 2016
1 parent a1021f3 commit caab819
Show file tree
Hide file tree
Showing 11 changed files with 122 additions and 262 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,9 @@ import (
"github.com/sthulb/mime/multipart"
)

func resourceCloudinitConfig() *schema.Resource {
func dataSourceCloudinitConfig() *schema.Resource {
return &schema.Resource{
Create: resourceCloudinitConfigCreate,
Delete: resourceCloudinitConfigDelete,
Update: resourceCloudinitConfigCreate,
Exists: resourceCloudinitConfigExists,
Read: resourceCloudinitConfigRead,
Read: dataSourceCloudinitConfigRead,

Schema: map[string]*schema.Schema{
"part": &schema.Schema{
Expand Down Expand Up @@ -52,13 +48,11 @@ func resourceCloudinitConfig() *schema.Resource {
Type: schema.TypeBool,
Optional: true,
Default: true,
ForceNew: true,
},
"base64_encode": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: true,
ForceNew: true,
},
"rendered": &schema.Schema{
Type: schema.TypeString,
Expand All @@ -69,7 +63,7 @@ func resourceCloudinitConfig() *schema.Resource {
}
}

func resourceCloudinitConfigCreate(d *schema.ResourceData, meta interface{}) error {
func dataSourceCloudinitConfigRead(d *schema.ResourceData, meta interface{}) error {
rendered, err := renderCloudinitConfig(d)
if err != nil {
return err
Expand All @@ -80,24 +74,6 @@ func resourceCloudinitConfigCreate(d *schema.ResourceData, meta interface{}) err
return nil
}

func resourceCloudinitConfigDelete(d *schema.ResourceData, meta interface{}) error {
d.SetId("")
return nil
}

func resourceCloudinitConfigExists(d *schema.ResourceData, meta interface{}) (bool, error) {
rendered, err := renderCloudinitConfig(d)
if err != nil {
return false, err
}

return strconv.Itoa(hashcode.String(rendered)) == d.Id(), nil
}

func resourceCloudinitConfigRead(d *schema.ResourceData, meta interface{}) error {
return nil
}

func renderCloudinitConfig(d *schema.ResourceData) (string, error) {
gzipOutput := d.Get("gzip").(bool)
base64Output := d.Get("base64_encode").(bool)
Expand Down
80 changes: 80 additions & 0 deletions builtin/providers/template/datasource_cloudinit_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package template

import (
"testing"

r "github.com/hashicorp/terraform/helper/resource"
)

func TestRender(t *testing.T) {
testCases := []struct {
ResourceBlock string
Expected string
}{
{
`data "template_cloudinit_config" "foo" {
gzip = false
base64_encode = false
part {
content_type = "text/x-shellscript"
content = "baz"
}
}`,
"Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\n\r\nbaz\r\n--MIMEBOUNDARY--\r\n",
},
{
`data "template_cloudinit_config" "foo" {
gzip = false
base64_encode = false
part {
content_type = "text/x-shellscript"
content = "baz"
filename = "foobar.sh"
}
}`,
"Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n--MIMEBOUNDARY\r\nContent-Disposition: attachment; filename=\"foobar.sh\"\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\n\r\nbaz\r\n--MIMEBOUNDARY--\r\n",
},
{
`data "template_cloudinit_config" "foo" {
gzip = false
base64_encode = false
part {
content_type = "text/x-shellscript"
content = "baz"
}
part {
content_type = "text/x-shellscript"
content = "ffbaz"
}
}`,
"Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\n\r\nbaz\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\n\r\nffbaz\r\n--MIMEBOUNDARY--\r\n",
},
}

for _, tt := range testCases {
r.UnitTest(t, r.TestCase{
Providers: testProviders,
Steps: []r.TestStep{
r.TestStep{
Config: tt.ResourceBlock,
Check: r.ComposeTestCheckFunc(
r.TestCheckResourceAttr("data.template_cloudinit_config.foo", "rendered", tt.Expected),
),
},
},
})
}
}

var testCloudInitConfig_basic = `
data "template_cloudinit_config" "config" {
part {
content_type = "text/x-shellscript"
content = "baz"
}
}`

var testCloudInitConfig_basic_expected = `Content-Type: multipart/mixed; boundary=\"MIMEBOUNDARY\"\nMIME-Version: 1.0\r\n--MIMEBOUNDARY\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/x-shellscript\r\nMime-Version: 1.0\r\n\r\nbaz\r\n--MIMEBOUNDARY--\r\n`
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"crypto/sha256"
"encoding/hex"
"fmt"
"log"
"os"
"path/filepath"

Expand All @@ -15,27 +14,22 @@ import (
"github.com/hashicorp/terraform/helper/schema"
)

func resourceFile() *schema.Resource {
func dataSourceFile() *schema.Resource {
return &schema.Resource{
Create: resourceFileCreate,
Delete: resourceFileDelete,
Exists: resourceFileExists,
Read: resourceFileRead,
Read: dataSourceFileRead,

Schema: map[string]*schema.Schema{
"template": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "Contents of the template",
ForceNew: true,
ConflictsWith: []string{"filename"},
ValidateFunc: validateTemplateAttribute,
},
"filename": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Description: "file to read template from",
ForceNew: true,
// Make a "best effort" attempt to relativize the file path.
StateFunc: func(v interface{}) string {
if v == nil || v.(string) == "" {
Expand All @@ -59,7 +53,6 @@ func resourceFile() *schema.Resource {
Optional: true,
Default: make(map[string]interface{}),
Description: "variables to substitute",
ForceNew: true,
},
"rendered": &schema.Schema{
Type: schema.TypeString,
Expand All @@ -70,7 +63,7 @@ func resourceFile() *schema.Resource {
}
}

func resourceFileCreate(d *schema.ResourceData, meta interface{}) error {
func dataSourceFileRead(d *schema.ResourceData, meta interface{}) error {
rendered, err := renderFile(d)
if err != nil {
return err
Expand All @@ -80,32 +73,6 @@ func resourceFileCreate(d *schema.ResourceData, meta interface{}) error {
return nil
}

func resourceFileDelete(d *schema.ResourceData, meta interface{}) error {
d.SetId("")
return nil
}

func resourceFileExists(d *schema.ResourceData, meta interface{}) (bool, error) {
rendered, err := renderFile(d)
if err != nil {
if _, ok := err.(templateRenderError); ok {
log.Printf("[DEBUG] Got error while rendering in Exists: %s", err)
log.Printf("[DEBUG] Returning false so the template re-renders using latest variables from config.")
return false, nil
} else {
return false, err
}
}
return hash(rendered) == d.Id(), nil
}

func resourceFileRead(d *schema.ResourceData, meta interface{}) error {
// Logic is handled in Exists, which only returns true if the rendered
// contents haven't changed. That means if we get here there's nothing to
// do.
return nil
}

type templateRenderError error

func renderFile(d *schema.ResourceData) (string, error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ func TestTemplateRendering(t *testing.T) {
want string
}{
{`{}`, `ABC`, `ABC`},
{`{a="foo"}`, `${a}`, `foo`},
{`{a="hello"}`, `${replace(a, "ello", "i")}`, `hi`},
{`{a="foo"}`, `$${a}`, `foo`},
{`{a="hello"}`, `$${replace(a, "ello", "i")}`, `hi`},
{`{}`, `${1+2+3}`, `6`},
}

for _, tt := range cases {
r.Test(t, r.TestCase{
r.UnitTest(t, r.TestCase{
Providers: testProviders,
Steps: []r.TestStep{
r.TestStep{
Expand All @@ -47,39 +47,6 @@ func TestTemplateRendering(t *testing.T) {
}
}

// https://github.com/hashicorp/terraform/issues/2344
func TestTemplateVariableChange(t *testing.T) {
steps := []struct {
vars string
template string
want string
}{
{`{a="foo"}`, `${a}`, `foo`},
{`{b="bar"}`, `${b}`, `bar`},
}

var testSteps []r.TestStep
for i, step := range steps {
testSteps = append(testSteps, r.TestStep{
Config: testTemplateConfig(step.template, step.vars),
Check: func(i int, want string) r.TestCheckFunc {
return func(s *terraform.State) error {
got := s.RootModule().Outputs["rendered"]
if want != got.Value {
return fmt.Errorf("[%d] got:\n%q\nwant:\n%q\n", i, got, want)
}
return nil
}
}(i, step.want),
})
}

r.Test(t, r.TestCase{
Providers: testProviders,
Steps: testSteps,
})
}

func TestValidateTemplateAttribute(t *testing.T) {
file, err := ioutil.TempFile("", "testtemplate")
if err != nil {
Expand Down Expand Up @@ -129,11 +96,11 @@ func TestTemplateSharedMemoryRace(t *testing.T) {

func testTemplateConfig(template, vars string) string {
return fmt.Sprintf(`
resource "template_file" "t0" {
data "template_file" "t0" {
template = "%s"
vars = %s
}
output "rendered" {
value = "${template_file.t0.rendered}"
value = "${data.template_file.t0.rendered}"
}`, template, vars)
}
14 changes: 12 additions & 2 deletions builtin/providers/template/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,19 @@ import (

func Provider() terraform.ResourceProvider {
return &schema.Provider{
DataSourcesMap: map[string]*schema.Resource{
"template_file": dataSourceFile(),
"template_cloudinit_config": dataSourceCloudinitConfig(),
},
ResourcesMap: map[string]*schema.Resource{
"template_file": resourceFile(),
"template_cloudinit_config": resourceCloudinitConfig(),
"template_file": schema.DataSourceResourceShim(
"template_file",
dataSourceFile(),
),
"template_cloudinit_config": schema.DataSourceResourceShim(
"template_cloudinit_config",
dataSourceCloudinitConfig(),
),
},
}
}
Loading

0 comments on commit caab819

Please sign in to comment.