Skip to content

Commit

Permalink
Add unpack option to InRequest params
Browse files Browse the repository at this point in the history
If the InRequest is configured with `Params{Unpack: true}`, unpack the archive
after downloading it. The original archive is not stored.

In other words, if you have an archive, `some-archive.tgz`, with a file
`some-file` in it, the destination directory after downloading and unpacking
will contain just `some-file`.
  • Loading branch information
Kris Hicks authored and krishicks committed Jul 12, 2017
1 parent fd92752 commit 11f7a7f
Show file tree
Hide file tree
Showing 5 changed files with 424 additions and 6 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Places the following files in the destination:

#### Parameters

*None.*
* `unpack`: *Optional.* If true and the file is an archive (tar, gzipped tar, other gzipped file, or zip), unpack the file. Gzipped tarballs will be both ungzipped and untarred.


### `out`: Upload an object to the bucket.
Expand Down
76 changes: 76 additions & 0 deletions in/archive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package in

import (
"bufio"
"errors"
"fmt"
"io"
"os"
"os/exec"
"strings"

"bitbucket.org/taruti/mimemagic"
)

var archiveMimetypes = []string{
"application/x-gzip",
"application/gzip",
"application/x-tar",
"application/zip",
}

func mimetype(r *bufio.Reader) (string, error) {
bs, err := r.Peek(512)
if err != nil && err != io.EOF {
return "", err
}

if len(bs) == 0 {
return "", errors.New("cannot determine mimetype from empty bytes")
}

return mimemagic.Match("", bs), nil
}

func archiveMimetype(filename string) string {
f, err := os.Open(filename)
if err != nil {
return ""
}
defer f.Close()

mime, err := mimetype(bufio.NewReader(f))
if err != nil {
return ""
}

for i := range archiveMimetypes {
if strings.HasPrefix(mime, archiveMimetypes[i]) {
return archiveMimetypes[i]
}
}

return ""
}

func inflate(mime, path, destination string) error {
var cmd *exec.Cmd

switch mime {
case "application/zip":
cmd = exec.Command("unzip", "-P", "", "-d", destination, path)
defer os.Remove(path)

case "application/x-tar":
cmd = exec.Command("tar", "xf", path, "-C", destination)
defer os.Remove(path)

case "application/gzip", "application/x-gzip":
cmd = exec.Command("gunzip", path)

default:
return fmt.Errorf("don't know how to extract %s", mime)
}

return cmd.Run()
}
54 changes: 49 additions & 5 deletions in/in_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,6 @@ func (command *InCommand) Run(destinationDir string, request InRequest) (InRespo
versionID = request.Version.VersionID
}

err = command.writeVersionFile(versionNumber, destinationDir)
if err != nil {
return InResponse{}, err
}

err = command.downloadFile(
request.Source.Bucket,
remotePath,
Expand All @@ -90,11 +85,29 @@ func (command *InCommand) Run(destinationDir string, request InRequest) (InRespo
return InResponse{}, err
}

if request.Params.Unpack {
destinationPath := filepath.Join(destinationDir, path.Base(remotePath))
mime := archiveMimetype(destinationPath)
if mime == "" {
return InResponse{}, fmt.Errorf("not an archive: %s", destinationPath)
}

err = extractArchive(mime, destinationPath)
if err != nil {
return InResponse{}, err
}
}

url := command.urlProvider.GetURL(request, remotePath)
if err = command.writeURLFile(destinationDir, url); err != nil {
return InResponse{}, err
}

err = command.writeVersionFile(versionNumber, destinationDir)
if err != nil {
return InResponse{}, err
}

metadata := command.metadata(remotePath, request.Source.Private, url)

if versionID == "" {
Expand Down Expand Up @@ -152,3 +165,34 @@ func (command *InCommand) metadata(remotePath string, private bool, url string)

return metadata
}

func extractArchive(mime, filename string) error {
destDir := filepath.Dir(filename)

err := inflate(mime, filename, destDir)
if err != nil {
return fmt.Errorf("failed to extract archive: %s", err)
}

if mime == "application/gzip" || mime == "application/x-gzip" {
fileInfos, err := ioutil.ReadDir(destDir)
if err != nil {
return fmt.Errorf("failed to read dir: %s", err)
}

if len(fileInfos) != 1 {
return fmt.Errorf("%d files found after gunzip; expected 1", len(fileInfos))
}

filename = filepath.Join(destDir, fileInfos[0].Name())
mime = archiveMimetype(filename)
if mime == "application/x-tar" {
err = inflate(mime, filename, destDir)
if err != nil {
return fmt.Errorf("failed to extract archive: %s", err)
}
}
}

return nil
}
Loading

0 comments on commit 11f7a7f

Please sign in to comment.