Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.

Commit 5d39755

Browse files
author
Matthieu Nottale
committed
pack, unpack: New commands.
Signed-off-by: Matthieu Nottale <[email protected]>
1 parent 48f17c0 commit 5d39755

File tree

4 files changed

+151
-8
lines changed

4 files changed

+151
-8
lines changed

cmd/pack.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
"github.com/docker/lunchbox/packager"
8+
"github.com/spf13/cobra"
9+
)
10+
11+
var packCmd = &cobra.Command{
12+
Use: "pack <app-name> [-o output_file]",
13+
Short: "Pack this app as a single file",
14+
Args: cobra.ExactArgs(1),
15+
Run: func(cmd *cobra.Command, args []string) {
16+
err := packager.Pack(args[0], packOutputFile)
17+
if err != nil {
18+
fmt.Printf("%v\n", err)
19+
os.Exit(1)
20+
}
21+
},
22+
}
23+
24+
var packOutputFile string
25+
26+
func init() {
27+
rootCmd.AddCommand(packCmd)
28+
packCmd.Flags().StringVarP(&packOutputFile, "output", "o", "-", "Output file (- for stdout)")
29+
}

cmd/unpack.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
"github.com/docker/lunchbox/packager"
8+
"github.com/spf13/cobra"
9+
)
10+
11+
var unpackCmd = &cobra.Command{
12+
Use: "unpack <app-name> [-o output_dir]",
13+
Short: "Unpack the app to expose the content",
14+
Args: cobra.ExactArgs(1),
15+
Run: func(cmd *cobra.Command, args []string) {
16+
err := packager.Unpack(args[0], unpackOutputDir)
17+
if err != nil {
18+
fmt.Printf("%v\n", err)
19+
os.Exit(1)
20+
}
21+
},
22+
}
23+
24+
var unpackOutputDir string
25+
26+
func init() {
27+
rootCmd.AddCommand(unpackCmd)
28+
unpackCmd.Flags().StringVarP(&unpackOutputDir, "output", "o", ".", "Output directory (.)")
29+
}

packager/extract.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,35 +27,43 @@ func Extract(appname string) (string, func(), error) {
2727
return appname, func() {}, nil
2828
}
2929
// not a dir: probably a tarball package, extract that in a temp dir
30-
f, err := os.Open(appname)
30+
tempDir, err := ioutil.TempDir("", "dockerapp")
3131
if err != nil {
3232
return "", func() {}, err
3333
}
34-
tempDir, err := ioutil.TempDir("", "dockerapp")
34+
err = extract(appname, tempDir)
3535
if err != nil {
3636
return "", func() {}, err
3737
}
38+
return tempDir, func() { os.RemoveAll(tempDir) }, nil
39+
}
40+
41+
func extract(appname, outputDir string) error {
42+
f, err := os.Open(appname)
43+
if err != nil {
44+
return err
45+
}
3846
tarReader := tar.NewReader(f)
3947
for {
4048
header, err := tarReader.Next()
4149
if err == io.EOF {
4250
break
4351
}
4452
if err != nil {
45-
return "", func() {}, err
53+
return err
4654
}
47-
tempDir = tempDir + "/"
55+
outputDir = outputDir + "/"
4856
switch header.Typeflag {
4957
case tar.TypeDir: // = directory
50-
os.Mkdir(tempDir+header.Name, 0755)
58+
os.Mkdir(outputDir+header.Name, 0755)
5159
case tar.TypeReg: // = regular file
5260
data := make([]byte, header.Size)
5361
_, err := tarReader.Read(data)
5462
if err != nil {
55-
return "", func() {}, err
63+
return err
5664
}
57-
ioutil.WriteFile(tempDir+header.Name, data, 0755)
65+
ioutil.WriteFile(outputDir+header.Name, data, 0644)
5866
}
5967
}
60-
return tempDir, func() { os.RemoveAll(tempDir) }, nil
68+
return nil
6169
}

packager/packing.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package packager
2+
3+
import (
4+
"archive/tar"
5+
"fmt"
6+
"io"
7+
"io/ioutil"
8+
"os"
9+
"path"
10+
11+
"github.com/docker/lunchbox/utils"
12+
"golang.org/x/crypto/ssh/terminal"
13+
)
14+
15+
// Pack packs the app as a single file
16+
func Pack(appname, output string) error {
17+
if output == "-" && terminal.IsTerminal(int(os.Stdout.Fd())) {
18+
return fmt.Errorf("Refusing to output to a terminal, use a shell redirect or the '-o' option")
19+
}
20+
appname, cleanup, err := Extract(appname)
21+
if err != nil {
22+
return err
23+
}
24+
defer cleanup()
25+
var target io.Writer
26+
if output == "-" {
27+
target = os.Stdout
28+
} else {
29+
target, err = os.Create(output)
30+
if err != nil {
31+
return err
32+
}
33+
}
34+
tarout := tar.NewWriter(target)
35+
files := []string{"metadata.yml", "services.yml", "settings.yml"}
36+
for _, f := range files {
37+
payload, err := ioutil.ReadFile(path.Join(appname, f))
38+
if err != nil {
39+
return err
40+
}
41+
h := &tar.Header{
42+
Name: f,
43+
Size: int64(len(payload)),
44+
}
45+
err = tarout.WriteHeader(h)
46+
if err != nil {
47+
return err
48+
}
49+
_, err = tarout.Write(payload)
50+
if err != nil {
51+
return err
52+
}
53+
}
54+
return tarout.Close()
55+
}
56+
57+
// Unpack extracts a packed app
58+
func Unpack(appname, targetDir string) error {
59+
s, err := os.Stat(appname)
60+
if err != nil {
61+
// try appending our extension
62+
appname = utils.DirNameFromAppName(appname)
63+
s, err = os.Stat(appname)
64+
}
65+
if err != nil {
66+
return err
67+
}
68+
if s.IsDir() {
69+
return fmt.Errorf("app already extracted")
70+
}
71+
out := path.Join(targetDir, utils.AppNameFromDir(appname)+".docker-app")
72+
err = os.Mkdir(out, 0755)
73+
if err != nil {
74+
return err
75+
}
76+
return extract(appname, out)
77+
}

0 commit comments

Comments
 (0)