Merge #12757: local_file resource

This commit is contained in:
Martin Atkins 2017-04-17 10:48:15 -07:00 committed by GitHub
commit d515c2efc4
10 changed files with 272 additions and 0 deletions

View File

@ -0,0 +1,12 @@
package main
import (
"github.com/hashicorp/terraform/builtin/providers/localfile"
"github.com/hashicorp/terraform/plugin"
)
func main() {
plugin.Serve(&plugin.ServeOpts{
ProviderFunc: localfile.Provider,
})
}

View File

@ -0,0 +1,15 @@
package local
import (
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)
func Provider() terraform.ResourceProvider {
return &schema.Provider{
Schema: map[string]*schema.Schema{},
ResourcesMap: map[string]*schema.Resource{
"local_file": resourceLocalFile(),
},
}
}

View File

@ -0,0 +1,18 @@
package local
import (
"testing"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)
var testProviders = map[string]terraform.ResourceProvider{
"local": Provider(),
}
func TestProvider(t *testing.T) {
if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
t.Fatalf("err: %s", err)
}
}

View File

@ -0,0 +1,84 @@
package local
import (
"crypto/sha1"
"encoding/hex"
"io/ioutil"
"os"
"path"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceLocalFile() *schema.Resource {
return &schema.Resource{
Create: resourceLocalFileCreate,
Read: resourceLocalFileRead,
Delete: resourceLocalFileDelete,
Schema: map[string]*schema.Schema{
"content": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"filename": {
Type: schema.TypeString,
Description: "Path to the output file",
Required: true,
ForceNew: true,
},
},
}
}
func resourceLocalFileRead(d *schema.ResourceData, _ interface{}) error {
// If the output file doesn't exist, mark the resource for creation.
outputPath := d.Get("filename").(string)
if _, err := os.Stat(outputPath); os.IsNotExist(err) {
d.SetId("")
return nil
}
// Verify that the content of the destination file matches the content we
// expect. Otherwise, the file might have been modified externally and we
// must reconcile.
outputContent, err := ioutil.ReadFile(outputPath)
if err != nil {
return err
}
outputChecksum := sha1.Sum([]byte(outputContent))
if hex.EncodeToString(outputChecksum[:]) != d.Id() {
d.SetId("")
return nil
}
return nil
}
func resourceLocalFileCreate(d *schema.ResourceData, _ interface{}) error {
content := d.Get("content").(string)
destination := d.Get("filename").(string)
destinationDir := path.Dir(destination)
if _, err := os.Stat(destinationDir); err != nil {
if err := os.MkdirAll(destinationDir, 0777); err != nil {
return err
}
}
if err := ioutil.WriteFile(destination, []byte(content), 0777); err != nil {
return err
}
checksum := sha1.Sum([]byte(content))
d.SetId(hex.EncodeToString(checksum[:]))
return nil
}
func resourceLocalFileDelete(d *schema.ResourceData, _ interface{}) error {
os.Remove(d.Get("filename").(string))
return nil
}

View File

@ -0,0 +1,56 @@
package local
import (
"errors"
"fmt"
"io/ioutil"
"os"
"testing"
r "github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestLocalFile_Basic(t *testing.T) {
var cases = []struct {
path string
content string
config string
}{
{
"local_file",
"This is some content",
`resource "local_file" "file" {
content = "This is some content"
filename = "local_file"
}`,
},
}
for _, tt := range cases {
r.UnitTest(t, r.TestCase{
Providers: testProviders,
Steps: []r.TestStep{
{
Config: tt.config,
Check: func(s *terraform.State) error {
content, err := ioutil.ReadFile(tt.path)
if err != nil {
return fmt.Errorf("config:\n%s\n,got: %s\n", tt.config, err)
}
if string(content) != tt.content {
return fmt.Errorf("config:\n%s\ngot:\n%s\nwant:\n%s\n", tt.config, content, tt.content)
}
return nil
},
},
},
CheckDestroy: func(*terraform.State) error {
if _, err := os.Stat(tt.path); os.IsNotExist(err) {
return nil
}
return errors.New("local_file did not get destroyed")
},
})
}
}

View File

@ -39,6 +39,7 @@ import (
influxdbprovider "github.com/hashicorp/terraform/builtin/providers/influxdb"
kubernetesprovider "github.com/hashicorp/terraform/builtin/providers/kubernetes"
libratoprovider "github.com/hashicorp/terraform/builtin/providers/librato"
localprovider "github.com/hashicorp/terraform/builtin/providers/local"
logentriesprovider "github.com/hashicorp/terraform/builtin/providers/logentries"
mailgunprovider "github.com/hashicorp/terraform/builtin/providers/mailgun"
mysqlprovider "github.com/hashicorp/terraform/builtin/providers/mysql"
@ -115,6 +116,7 @@ var InternalProviders = map[string]plugin.ProviderFunc{
"influxdb": influxdbprovider.Provider,
"kubernetes": kubernetesprovider.Provider,
"librato": libratoprovider.Provider,
"local": localprovider.Provider,
"logentries": logentriesprovider.Provider,
"mailgun": mailgunprovider.Provider,
"mysql": mysqlprovider.Provider,

View File

@ -0,0 +1,20 @@
---
layout: "local"
page_title: "Provider: Local"
sidebar_current: "docs-local-index"
description: |-
The Local provider is used to manage local resources, such as files.
---
# Local Provider
The Local provider is used to manage local resources, such as files.
Use the navigation to the left to read about the available resources.
~> **Note** Terraform primarily deals with remote resources which are able
to outlive a single Terraform run, and so local resources can sometimes violate
its assumptions. The resources here are best used with care, since depending
on local state can make it hard to apply the same Terraform configuration on
many different local systems where the local resources may not be universally
available. See specific notes in each resource for more information.

View File

@ -0,0 +1,37 @@
---
layout: "local"
page_title: "Local: local_file"
sidebar_current: "docs-local-resource-file"
description: |-
Generates a local file from content.
---
# local_file
Generates a local file with the given content.
~> **Note** When working with local files, Terraform will detect the resource
as having been deleted each time a configuration is applied on a new machine
where the file is not present and will generate a diff to re-create it. This
may cause "noise" in diffs in environments where configurations are routinely
applied by many different users or within automation systems.
## Example Usage
```hcl
resource "local_file" "foo" {
content = "foo!"
filename = "${path.module}/foo.bar"
}
```
## Argument Reference
The following arguments are supported:
* `content` - (Required) The content of file to create.
* `filename` - (Required) The path of the file to create.
Any required parent directories will be created automatically, and any existing
file with the given name will be overwritten.

View File

@ -304,6 +304,10 @@
<a href="/docs/providers/librato/index.html">Librato</a>
</li>
<li<%= sidebar_current("docs-providers-local") %>>
<a href="/docs/providers/local/index.html">Local</a>
</li>
<li<%= sidebar_current("docs-providers-logentries") %>>
<a href="/docs/providers/logentries/index.html">Logentries</a>
</li>

View File

@ -0,0 +1,24 @@
<% wrap_layout :inner do %>
<% content_for :sidebar do %>
<ul class="nav docs-sidenav">
<li<%= sidebar_current("docs-home") %>>
<a href="/docs/providers/index.html">All Providers</a>
</li>
<li<%= sidebar_current("docs-local-index") %>>
<a href="/docs/providers/local/index.html">Local Provider</a>
</li>
<li<%= sidebar_current("docs-local-resource") %>>
<a href="#">Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-local-resource-file") %>>
<a href="/docs/providers/local/r/file.html">file</a>
</li>
</ul>
</li>
</ul>
<% end %>
<%= yield %>
<% end %>