Skip to content

Commit

Permalink
Implement archive provider and "archive_file" resource.
Browse files Browse the repository at this point in the history
  • Loading branch information
Brad Sickles committed Jun 25, 2016
1 parent b68eca5 commit 910ac03
Show file tree
Hide file tree
Showing 19 changed files with 647 additions and 0 deletions.
12 changes: 12 additions & 0 deletions builtin/bins/provider-archive/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package main

import (
"github.com/hashicorp/terraform/builtin/providers/archive"
"github.com/hashicorp/terraform/plugin"
)

func main() {
plugin.Serve(&plugin.ServeOpts{
ProviderFunc: archive.Provider,
})
}
Binary file added builtin/providers/archive/archive-content.zip
Binary file not shown.
Binary file added builtin/providers/archive/archive-dir.zip
Binary file not shown.
Binary file added builtin/providers/archive/archive-file.zip
Binary file not shown.
47 changes: 47 additions & 0 deletions builtin/providers/archive/archiver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package archive

import (
"fmt"
"os"
)

type Archiver interface {
ArchiveContent(content []byte, infilename string) error
ArchiveFile(infilename string) error
ArchiveDir(indirname string) error
}

type ArchiverBuilder func(filepath string) Archiver

var archiverBuilders = map[string]ArchiverBuilder{
"zip": NewZipArchiver,
}

func getArchiver(archiveType string, filepath string) Archiver {
if builder, ok := archiverBuilders[archiveType]; ok {
return builder(filepath)
}
return nil
}

func assertValidFile(infilename string) (os.FileInfo, error) {
fi, err := os.Stat(infilename)
if err != nil && os.IsNotExist(err) {
return fi, fmt.Errorf("could not archive missing file: %s", infilename)
}
return fi, err
}

func assertValidDir(indirname string) (os.FileInfo, error) {
fi, err := os.Stat(indirname)
if err != nil {
if os.IsNotExist(err) {
return fi, fmt.Errorf("could not archive missing directory: %s", indirname)
}
return fi, err
}
if !fi.IsDir() {
return fi, fmt.Errorf("could not archive directory that is a file: %s", indirname)
}
return fi, nil
}
16 changes: 16 additions & 0 deletions builtin/providers/archive/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package archive

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{
"archive_file": resourceArchiveFile(),
},
}
}
18 changes: 18 additions & 0 deletions builtin/providers/archive/provider_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package archive

import (
"testing"

"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)

var testProviders = map[string]terraform.ResourceProvider{
"archive": Provider(),
}

func TestProvider(t *testing.T) {
if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
t.Fatalf("err: %s", err)
}
}
174 changes: 174 additions & 0 deletions builtin/providers/archive/resource_archive_file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package archive

import (
"crypto/sha1"
"encoding/hex"
"fmt"
"github.com/hashicorp/terraform/helper/schema"
"io/ioutil"
"os"
)

func resourceArchiveFile() *schema.Resource {
return &schema.Resource{
Create: resourceArchiveFileCreate,
Read: resourceArchiveFileRead,
Update: resourceArchiveFileUpdate,
Delete: resourceArchiveFileDelete,
Exists: resourceArchiveFileExists,

Schema: map[string]*schema.Schema{
"type": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"source_content": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"source_file", "source_dir"},
},
"source_content_filename": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"source_file", "source_dir"},
},
"source_file": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"source_content", "source_content_filename", "source_dir"},
},
"source_dir": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"source_content", "source_content_filename", "source_file"},
},
"output_path": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"output_size": &schema.Schema{
Type: schema.TypeInt,
Computed: true,
ForceNew: true,
},
"output_sha": &schema.Schema{
Type: schema.TypeString,
Computed: true,
ForceNew: true,
Description: "SHA1 checksum of output file",
},
},
}
}

func resourceArchiveFileCreate(d *schema.ResourceData, meta interface{}) error {
if err := resourceArchiveFileUpdate(d, meta); err != nil {
return err
}
return resourceArchiveFileRead(d, meta)
}

func resourceArchiveFileRead(d *schema.ResourceData, meta interface{}) error {
output_path := d.Get("output_path").(string)
fi, err := os.Stat(output_path)
if os.IsNotExist(err) {
d.SetId("")
d.MarkNewResource()
return nil
}

sha, err := genFileSha1(fi.Name())
if err != nil {
return fmt.Errorf("could not generate file checksum sha: %s", err)
}
d.Set("output_sha", sha)
d.Set("output_size", fi.Size())
d.SetId(d.Get("output_sha").(string))

return nil
}

func resourceArchiveFileUpdate(d *schema.ResourceData, meta interface{}) error {
archiveType := d.Get("type").(string)
outputPath := d.Get("output_path").(string)

archiver := getArchiver(archiveType, outputPath)
if archiver == nil {
return fmt.Errorf("archive type not supported: %s", archiveType)
}

if dir, ok := d.GetOk("source_dir"); ok {
if err := archiver.ArchiveDir(dir.(string)); err != nil {
return fmt.Errorf("error archiving directory: %s", err)
}
} else if file, ok := d.GetOk("source_file"); ok {
if err := archiver.ArchiveFile(file.(string)); err != nil {
return fmt.Errorf("error archiving file: %s", err)
}
} else if filename, ok := d.GetOk("source_content_filename"); ok {
content := d.Get("source_content").(string)
if err := archiver.ArchiveContent([]byte(content), filename.(string)); err != nil {
return fmt.Errorf("error archiving content: %s", err)
}
} else {
return fmt.Errorf("one of 'source_dir', 'source_file', 'source_content_filename' must be specified")
}

// Generate archived file stats
output_path := d.Get("output_path").(string)
fi, err := os.Stat(output_path)
if err != nil {
return err
}

sha, err := genFileSha1(fi.Name())
if err != nil {
return fmt.Errorf("could not generate file checksum sha: %s", err)
}
d.Set("output_sha", sha)
d.Set("output_size", fi.Size())
d.SetId(d.Get("output_sha").(string))

return nil
}

func resourceArchiveFileDelete(d *schema.ResourceData, meta interface{}) error {
output_path := d.Get("output_path").(string)
fi, err := os.Stat(output_path)
if os.IsNotExist(err) {
return nil
}

if err := os.Remove(fi.Name()); err != nil {
return fmt.Errorf("could not delete zip file '%s': %s", fi.Name(), err)
}

return nil
}

func resourceArchiveFileExists(d *schema.ResourceData, meta interface{}) (bool, error) {
output_path := d.Get("output_path").(string)
_, err := os.Stat(output_path)
if os.IsNotExist(err) {
return false, nil
}
if err != nil {
return false, err
}
return true, nil
}

func genFileSha1(filename string) (string, error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
return "", fmt.Errorf("could not compute file '%s' checksum: %s", filename, err)
}
h := sha1.New()
h.Write([]byte(data))
return hex.EncodeToString(h.Sum(nil)), nil
}
92 changes: 92 additions & 0 deletions builtin/providers/archive/resource_archive_file_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package archive

import (
"fmt"
r "github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"os"
"testing"
)

func TestAccArchiveFile_Basic(t *testing.T) {
var fileSize string
r.Test(t, r.TestCase{
Providers: testProviders,
CheckDestroy: r.ComposeTestCheckFunc(
testAccArchiveFileMissing("zip_file_acc_test.zip"),
),
Steps: []r.TestStep{
r.TestStep{
Config: testAccArchiveFileContentConfig,
Check: r.ComposeTestCheckFunc(
testAccArchiveFileExists("zip_file_acc_test.zip", &fileSize),
r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize),
),
},
r.TestStep{
Config: testAccArchiveFileFileConfig,
Check: r.ComposeTestCheckFunc(
testAccArchiveFileExists("zip_file_acc_test.zip", &fileSize),
r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize),
),
},
r.TestStep{
Config: testAccArchiveFileDirConfig,
Check: r.ComposeTestCheckFunc(
testAccArchiveFileExists("zip_file_acc_test.zip", &fileSize),
r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize),
),
},
},
})
}

func testAccArchiveFileExists(filename string, fileSize *string) r.TestCheckFunc {
return func(s *terraform.State) error {
*fileSize = ""
fi, err := os.Stat(filename)
if err != nil {
return err
}
*fileSize = fmt.Sprintf("%d", fi.Size())
return nil
}
}

func testAccArchiveFileMissing(filename string) r.TestCheckFunc {
return func(s *terraform.State) error {
_, err := os.Stat(filename)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
return fmt.Errorf("found file expected to be deleted: %s", filename)
}
}

var testAccArchiveFileContentConfig = `
resource "archive_file" "foo" {
type = "zip"
source_content = "This is some content"
source_content_filename = "content.txt"
output_path = "zip_file_acc_test.zip"
}
`

var testAccArchiveFileFileConfig = `
resource "archive_file" "foo" {
type = "zip"
source_file = "test-fixtures/test-file.txt"
output_path = "zip_file_acc_test.zip"
}
`

var testAccArchiveFileDirConfig = `
resource "archive_file" "foo" {
type = "zip"
source_dir = "test-fixtures/test-dir"
output_path = "zip_file_acc_test.zip"
}
`
1 change: 1 addition & 0 deletions builtin/providers/archive/test-fixtures/test-dir/file1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This is file 1
1 change: 1 addition & 0 deletions builtin/providers/archive/test-fixtures/test-dir/file2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This is file 2
1 change: 1 addition & 0 deletions builtin/providers/archive/test-fixtures/test-dir/file3.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This is file 3
1 change: 1 addition & 0 deletions builtin/providers/archive/test-fixtures/test-file.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This is test content
Loading

0 comments on commit 910ac03

Please sign in to comment.