Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add source_dirs field #113

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions internal/provider/archiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type Archiver interface {
ArchiveFile(infilename string) error
ArchiveDir(indirname string, excludes []string) error
ArchiveMultiple(content map[string][]byte) error
ArchiveMultipleDirs(dirs []interface{}, rootDirs []interface{}, excludes []string) error
SetOutputFileMode(outputFileMode string)
}

Expand Down
55 changes: 49 additions & 6 deletions internal/provider/data_source_archive_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,33 @@ func dataSourceFile() *schema.Resource {
Required: true,
ForceNew: true,
},
"source_dirs": {
Type: schema.TypeSet,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"dirs": {
Type: schema.TypeList,
Optional: true,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"root_dirs": {
Type: schema.TypeList,
Optional: true,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
},
ConflictsWith: []string{"source_file", "source_dir", "source_content", "source_content_filename"},
},
"source": {
Type: schema.TypeSet,
Optional: true,
Expand All @@ -44,7 +71,7 @@ func dataSourceFile() *schema.Resource {
},
},
},
ConflictsWith: []string{"source_file", "source_dir", "source_content", "source_content_filename"},
ConflictsWith: []string{"source_file", "source_dir", "source_dirs", "source_content", "source_content_filename"},
Set: func(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
Expand All @@ -57,31 +84,31 @@ func dataSourceFile() *schema.Resource {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"source_file", "source_dir"},
ConflictsWith: []string{"source_file", "source_dir", "source_dirs"},
},
"source_content_filename": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"source_file", "source_dir"},
ConflictsWith: []string{"source_file", "source_dir", "source_dirs"},
},
"source_file": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"source_content", "source_content_filename", "source_dir"},
ConflictsWith: []string{"source_content", "source_content_filename", "source_dir", "source_dirs"},
},
"source_dir": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"source_content", "source_content_filename", "source_file"},
ConflictsWith: []string{"source_content", "source_content_filename", "source_file", "source_dirs"},
},
"excludes": {
Type: schema.TypeSet,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"source_content", "source_content_filename", "source_file"},
ConflictsWith: []string{"source_content", "source_content_filename", "source_file", "source_dirs"},
Elem: &schema.Schema{
Type: schema.TypeString,
},
Expand Down Expand Up @@ -212,6 +239,22 @@ func archive(d *schema.ResourceData) error {
if err := archiver.ArchiveMultiple(content); err != nil {
return fmt.Errorf("error archiving content: %s", err)
}
} else if v, ok := d.GetOk("source_dirs"); ok {
vL := v.(*schema.Set).List()
for _, v := range vL {
src := v.(map[string]interface{})
dirs := src["dirs"].([]interface{})
rootDirs := src["root_dirs"].([]interface{})
if excludes, ok := d.GetOk("excludes"); ok {
excludeList := expandStringList(excludes.(*schema.Set).List())

archiver.ArchiveMultipleDirs(dirs, rootDirs, excludeList)
return nil
} else {
archiver.ArchiveMultipleDirs(dirs, rootDirs, []string{""})
return nil
}
}
} else {
return fmt.Errorf("one of 'source_dir', 'source_file', 'source_content_filename' must be specified")
}
Expand Down
144 changes: 144 additions & 0 deletions internal/provider/zip_archiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,150 @@ func checkMatch(fileName string, excludes []string) (value bool) {
return false
}

func (a *ZipArchiver) ArchiveMultipleDirs(dirs []interface{}, rootDirs []interface{}, excludes []string) error {
if err := a.open(); err != nil {
return err
}

defer a.close()

// ensure exclusions are OS compatible paths
for i := range excludes {
excludes[i] = filepath.FromSlash(excludes[i])
}

for _, indirname := range rootDirs {
indirname := indirname.(string)
_, err := assertValidDir(indirname)
if err != nil {
return err
}

filepath.Walk(indirname, func(path string, info os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("error encountered during file walk: %s", err)
}

relname, err := filepath.Rel(indirname, path)
if err != nil {
return fmt.Errorf("error relativizing file for archival: %s", err)
}

isMatch := checkMatch(relname, excludes)

if info.IsDir() {
if isMatch {
return filepath.SkipDir
}
return nil
}

if isMatch {
return nil
}

if err != nil {
return err
}

fh, err := zip.FileInfoHeader(info)
if err != nil {
return fmt.Errorf("error creating file header: %s", err)
}
fh.Name = filepath.ToSlash(relname)
fh.Method = zip.Deflate
// fh.Modified alone isn't enough when using a zero value
fh.SetModTime(time.Time{})

if a.outputFileMode != "" {
filemode, err := strconv.ParseUint(a.outputFileMode, 0, 32)
if err != nil {
return fmt.Errorf("error parsing output_file_mode value: %s", a.outputFileMode)
}
fh.SetMode(os.FileMode(filemode))
}

f, err := a.writer.CreateHeader(fh)
if err != nil {
return fmt.Errorf("error creating file inside archive: %s", err)
}
content, err := ioutil.ReadFile(path)
if err != nil {
return fmt.Errorf("error reading file for archival: %s", err)
}
_, err = f.Write(content)
return err
})
}

for _, indirname := range dirs {
indirname := indirname.(string)
_, err := assertValidDir(indirname)
if err != nil {
return err
}

filepath.Walk(indirname, func(path string, info os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("error encountered during file walk: %s", err)
}

relname, err := filepath.Rel(indirname, path)
if err != nil {
return fmt.Errorf("error relativizing file for archival: %s", err)
}

relname = fmt.Sprintf("%s/%s", filepath.Base(indirname), relname)

isMatch := checkMatch(relname, excludes)

if info.IsDir() {
if isMatch {
return filepath.SkipDir
}
return nil
}

if isMatch {
return nil
}

if err != nil {
return err
}

fh, err := zip.FileInfoHeader(info)
if err != nil {
return fmt.Errorf("error creating file header: %s", err)
}
fh.Name = filepath.ToSlash(relname)
fh.Method = zip.Deflate
// fh.Modified alone isn't enough when using a zero value
fh.SetModTime(time.Time{})

if a.outputFileMode != "" {
filemode, err := strconv.ParseUint(a.outputFileMode, 0, 32)
if err != nil {
return fmt.Errorf("error parsing output_file_mode value: %s", a.outputFileMode)
}
fh.SetMode(os.FileMode(filemode))
}

f, err := a.writer.CreateHeader(fh)
if err != nil {
return fmt.Errorf("error creating file inside archive: %s", err)
}
content, err := ioutil.ReadFile(path)
if err != nil {
return fmt.Errorf("error reading file for archival: %s", err)
}
_, err = f.Write(content)
return err
})
}
return nil
}

func (a *ZipArchiver) ArchiveDir(indirname string, excludes []string) error {
_, err := assertValidDir(indirname)
if err != nil {
Expand Down
10 changes: 9 additions & 1 deletion website/docs/d/archive_file.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ data "archive_file" "lambda_my_function" {

The following arguments are supported:

NOTE: One of `source`, `source_content_filename` (with `source_content`), `source_file`, or `source_dir` must be specified.
NOTE: One of `source`, `source_content_filename` (with `source_content`), `source_file`, `source_dir`, or `source_dirs` must be specified.

* `type` - (Required) The type of archive to generate.
NOTE: `zip` is supported.
Expand All @@ -78,6 +78,8 @@ NOTE: One of `source`, `source_content_filename` (with `source_content`), `sourc

* `source_dir` - (Optional) Package entire contents of this directory into the archive.

* `source_dirs` - (Optional) Specifies attributes of multiple directory listing into the archive

* `source` - (Optional) Specifies attributes of a single source file to include into the archive.

* `excludes` - (Optional) Specify files to ignore when reading the `source_dir`.
Expand All @@ -88,6 +90,12 @@ The `source` block supports the following:

* `filename` - (Required) Set this as the filename when declaring a `source`.

The `source_dirs` block supports the following:

* `dirs` - (Required) Add the list of directory as their own folder into the archive

* `root_dirs` - (Required) Add the list of directory contents into the archive root level

## Attributes Reference

The following attributes are exported:
Expand Down