veyron/services/mgmt/lib/packages,device/impl: enable installing regular files
Currently, only archive packages are supported for installation. With this
change, archives will continue to be extracted into the directory specified
by the local package name, but in addition, non-archive package files will
be copied directly as the local package name.
Change-Id: Ic37891a5d9bf1c88945d8daca23df3ded4b8c70b
diff --git a/services/mgmt/device/impl/app_service.go b/services/mgmt/device/impl/app_service.go
index f88abfa..5d5b6c7 100644
--- a/services/mgmt/device/impl/app_service.go
+++ b/services/mgmt/device/impl/app_service.go
@@ -630,11 +630,8 @@
// them between apps or instances.
for pkg, _ := range envelope.Packages {
pkgFile := filepath.Join(versionDir, "pkg", pkg)
- dstDir := filepath.Join(packagesDir, pkg)
- if err := os.MkdirAll(dstDir, os.FileMode(0700)); err != nil {
- return err
- }
- if err := libpackages.Install(pkgFile, dstDir); err != nil {
+ dst := filepath.Join(packagesDir, pkg)
+ if err := libpackages.Install(pkgFile, dst); err != nil {
return err
}
}
diff --git a/services/mgmt/device/impl/impl_test.go b/services/mgmt/device/impl/impl_test.go
index abd5f84..0d171c5 100644
--- a/services/mgmt/device/impl/impl_test.go
+++ b/services/mgmt/device/impl/impl_test.go
@@ -1235,6 +1235,12 @@
if _, err := libbinary.UploadFromDir(ctx, naming.Join(binaryVON, "testpkg"), tmpdir); err != nil {
t.Fatalf("libbinary.UploadFromDir failed: %v", err)
}
+ if err := ioutil.WriteFile(filepath.Join(tmpdir, "goodbye.txt"), []byte("Goodbye World!"), 0600); err != nil {
+ t.Fatalf("ioutil.WriteFile failed: %v", err)
+ }
+ if _, err := libbinary.UploadFromFile(ctx, naming.Join(binaryVON, "testfile"), filepath.Join(tmpdir, "goodbye.txt")); err != nil {
+ t.Fatalf("libbinary.UploadFromFile failed: %v", err)
+ }
root, cleanup := mgmttest.SetupRootDir(t, "devicemanager")
defer cleanup()
@@ -1255,7 +1261,8 @@
// Create the envelope for the first version of the app.
*envelope = envelopeFromShell(sh, nil, appCmd, "google naps", "appV1")
(*envelope).Packages = map[string]string{
- "test": "realbin/testpkg",
+ "test": "realbin/testpkg",
+ "test2": "realbin/testfile",
}
// Install the app.
@@ -1271,15 +1278,28 @@
t.Fatalf("failed to get ping")
}
- // Ask the app to cat a file from the package.
- file := filepath.Join("packages", "test", "hello.txt")
- name := "appV1"
- content, err := cat(ctx, name, file)
- if err != nil {
- t.Errorf("cat(%q, %q) failed: %v", name, file, err)
- }
- if expected := "Hello World!"; content != expected {
- t.Errorf("unexpected content: expected %q, got %q", expected, content)
+ for _, c := range []struct {
+ path, content string
+ }{
+ {
+ filepath.Join("test", "hello.txt"),
+ "Hello World!",
+ },
+ {
+ "test2",
+ "Goodbye World!",
+ },
+ } {
+ // Ask the app to cat the file.
+ file := filepath.Join("packages", c.path)
+ name := "appV1"
+ content, err := cat(ctx, name, file)
+ if err != nil {
+ t.Errorf("cat(%q, %q) failed: %v", name, file, err)
+ }
+ if expected := c.content; content != expected {
+ t.Errorf("unexpected content: expected %q, got %q", expected, content)
+ }
}
}
diff --git a/services/mgmt/lib/packages/packages.go b/services/mgmt/lib/packages/packages.go
index 330b61d..a1abc45 100644
--- a/services/mgmt/lib/packages/packages.go
+++ b/services/mgmt/lib/packages/packages.go
@@ -41,19 +41,39 @@
return repository.MediaInfo{Type: defaultType}
}
-// Install installs a package in the given directory. If the package is a TAR or
-// ZIP archive, its content is extracted in the destination directory.
-// Otherwise, the package file itself is copied to the destination directory.
-func Install(pkgFile, dir string) error {
+func copyFile(src, dst string) error {
+ s, err := os.Open(src)
+ if err != nil {
+ return err
+ }
+ defer s.Close()
+ d, err := os.Create(dst)
+ if err != nil {
+ return err
+ }
+ defer d.Close()
+ if _, err = io.Copy(d, s); err != nil {
+ return err
+ }
+ return d.Sync()
+}
+
+// Install installs a package in the given destination. If the package is a TAR
+// or ZIP archive, the destination becomes a directory where the archive content
+// is extracted. Otherwise, the package file itself is copied to the
+// destination.
+func Install(pkgFile, destination string) error {
mediaInfo, err := LoadMediaInfo(pkgFile)
if err != nil {
return err
}
switch mediaInfo.Type {
case "application/x-tar":
- return extractTar(pkgFile, mediaInfo, dir)
+ return extractTar(pkgFile, mediaInfo.Encoding, destination)
case "application/zip":
- return extractZip(pkgFile, dir)
+ return extractZip(pkgFile, destination)
+ case defaultType:
+ return copyFile(pkgFile, destination)
default:
return fmt.Errorf("unsupported media type: %v", mediaInfo.Type)
}
@@ -133,6 +153,9 @@
}
func extractZip(zipFile, installDir string) error {
+ if err := os.Mkdir(installDir, os.FileMode(0700)); err != nil {
+ return fmt.Errorf("os.Mkdir(%q) failed: %v", installDir, err)
+ }
zr, err := zip.OpenReader(zipFile)
if err != nil {
return err
@@ -175,7 +198,10 @@
return nil
}
-func extractTar(pkgFile string, mediaInfo repository.MediaInfo, installDir string) error {
+func extractTar(pkgFile string, encoding string, installDir string) error {
+ if err := os.Mkdir(installDir, os.FileMode(0700)); err != nil {
+ return fmt.Errorf("os.Mkdir(%q) failed: %v", installDir, err)
+ }
f, err := os.Open(pkgFile)
if err != nil {
return err
@@ -183,7 +209,7 @@
defer f.Close()
var reader io.Reader
- switch enc := mediaInfo.Encoding; enc {
+ switch encoding {
case "":
reader = f
case "gzip":
@@ -194,7 +220,7 @@
case "bzip2":
reader = bzip2.NewReader(f)
default:
- return fmt.Errorf("unsupported encoding: %q", enc)
+ return fmt.Errorf("unsupported encoding: %q", encoding)
}
tr := tar.NewReader(reader)
diff --git a/services/mgmt/lib/packages/packages_test.go b/services/mgmt/lib/packages/packages_test.go
index 16074ad..6e409cf 100644
--- a/services/mgmt/lib/packages/packages_test.go
+++ b/services/mgmt/lib/packages/packages_test.go
@@ -47,19 +47,27 @@
"a/foo.txt perm:600",
}
for _, file := range []string{zipfile, tarfile, tgzfile} {
- setupDstDir(t, dstdir)
if err := packages.Install(file, dstdir); err != nil {
t.Errorf("packages.Install failed for %q: %v", file, err)
}
files := scanDir(dstdir)
if !reflect.DeepEqual(files, expected) {
- t.Errorf("unexpected result for %q: Got %q, want %q", file, files, expected)
+ t.Errorf("unexpected result for %q: got %q, want %q", file, files, expected)
+ }
+ if err := os.RemoveAll(dstdir); err != nil {
+ t.Fatalf("os.RemoveAll(%q) failed: %v", dstdir, err)
}
}
-
- setupDstDir(t, dstdir)
- if err := packages.Install(binfile, dstdir); err == nil {
- t.Errorf("expected packages.Install to fail %q", binfile)
+ dstfile := filepath.Join(workdir, "dstfile")
+ if err := packages.Install(binfile, dstfile); err != nil {
+ t.Errorf("packages.Install failed for %q: %v", binfile, err)
+ }
+ contents, err := ioutil.ReadFile(dstfile)
+ if err != nil {
+ t.Errorf("ReadFile(%q) failed: %v", dstfile, err)
+ }
+ if want, got := "This is a binary file", string(contents); want != got {
+ t.Errorf("unexpected result for %q: got %q, want %q", binfile, got, want)
}
}
@@ -80,7 +88,7 @@
}
for _, tc := range testcases {
if got := packages.MediaInfoForFileName(tc.filename); !reflect.DeepEqual(got, tc.expected) {
- t.Errorf("unexpected result for %q: Got %v, want %v", tc.filename, got, tc.expected)
+ t.Errorf("unexpected result for %q: got %v, want %v", tc.filename, got, tc.expected)
}
}
}
@@ -194,12 +202,3 @@
sort.Strings(files)
return files
}
-
-func setupDstDir(t *testing.T, dst string) {
- if err := os.RemoveAll(dst); err != nil {
- t.Fatalf("os.RemoveAll(%q) failed: %v", dst, err)
- }
- if err := os.Mkdir(dst, os.FileMode(0755)); err != nil {
- t.Fatalf("os.Mkdir(%q) failed: %v", dst, err)
- }
-}