veyron/services/mgmt: Implement optional packages
This change implements optional packages in the mgmt services. The
binary repository now saves the media type and encoding of the objects,
e.g type=application/x-tar,encoding=gzip, or type=application/zip.
The node manager uses this information to download and install the
packages requested in the application envelopes.
TODO: Test the node manager implementation. This will require either using the
real binaryd, or significantly improving the mock binaryd.
Change-Id: Idc7d0ee7663ccd1816a271289de8f61403d8dcce
diff --git a/services/mgmt/node/impl/app_service.go b/services/mgmt/node/impl/app_service.go
index 794d9f0..0ad6e33 100644
--- a/services/mgmt/node/impl/app_service.go
+++ b/services/mgmt/node/impl/app_service.go
@@ -21,6 +21,10 @@
// bin - application binary
// previous - symbolic link to previous version directory
// envelope - application envelope (JSON-encoded)
+// pkg/ - the application packages
+// <pkg name>
+// <pkg name>.__info
+// ...
// <version 2 timestamp>
// ...
// current - symbolic link to the current version
@@ -29,6 +33,9 @@
// credentials/ - holds veyron credentials (unless running
// through security agent)
// root/ - workspace that the instance is run from
+// packages/ - the installed packages
+// <pkg name>/
+// ...
// logs/ - stderr/stdout and log files generated by instance
// info - metadata for the instance (such as app
// cycle manager name and process id)
@@ -136,6 +143,8 @@
vsecurity "veyron.io/veyron/veyron/security"
"veyron.io/veyron/veyron/security/agent"
"veyron.io/veyron/veyron/security/agent/keymgr"
+ libbinary "veyron.io/veyron/veyron/services/mgmt/lib/binary"
+ libpackages "veyron.io/veyron/veyron/services/mgmt/lib/packages"
iconfig "veyron.io/veyron/veyron/services/mgmt/node/config"
)
@@ -319,10 +328,25 @@
if err := mkdir(versionDir); err != nil {
return "", errOperationFailed
}
+ pkgDir := filepath.Join(versionDir, "pkg")
+ if err := mkdir(pkgDir); err != nil {
+ return "", errOperationFailed
+ }
// TODO(caprita): Share binaries if already existing locally.
if err := downloadBinary(versionDir, "bin", envelope.Binary); err != nil {
return versionDir, err
}
+ for localPkg, pkgName := range envelope.Packages {
+ if localPkg == "" || localPkg[0] == '.' || strings.Contains(localPkg, string(filepath.Separator)) {
+ vlog.Infof("invalid local package name: %q", localPkg)
+ return versionDir, errOperationFailed
+ }
+ path := filepath.Join(pkgDir, localPkg)
+ if err := libbinary.DownloadToFile(rt.R().NewContext(), pkgName, path); err != nil {
+ vlog.Infof("DownloadToFile(%q, %q) failed: %v", pkgName, path, err)
+ return versionDir, errOperationFailed
+ }
+ }
if err := saveEnvelope(versionDir, envelope); err != nil {
return versionDir, err
}
@@ -554,6 +578,31 @@
return installationDirCore(i.suffix, i.config.Root)
}
+// installPackages installs all the packages for a new instance.
+func installPackages(versionDir, instanceDir string) error {
+ envelope, err := loadEnvelope(versionDir)
+ if err != nil {
+ return err
+ }
+ packagesDir := filepath.Join(instanceDir, "root", "packages")
+ if err := os.MkdirAll(packagesDir, os.FileMode(0700)); err != nil {
+ return err
+ }
+ // TODO(rthellend): Consider making the packages read-only and sharing
+ // 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 {
+ return err
+ }
+ }
+ return nil
+}
+
func initializeInstanceACLs(instanceDir string, blessings []string, acl security.ACL) error {
if acl.In == nil {
// The acl.In will be empty for an unclaimed node manager. In this case,
@@ -599,6 +648,10 @@
vlog.Errorf("Symlink(%v, %v) failed: %v", versionDir, versionLink, err)
return instanceDir, instanceID, errOperationFailed
}
+ if err := installPackages(versionDir, instanceDir); err != nil {
+ vlog.Errorf("installPackages(%v, %v) failed: %v", versionDir, instanceDir, err)
+ return instanceDir, instanceID, errOperationFailed
+ }
instanceInfo := new(instanceInfo)
if err := setupPrincipal(instanceDir, versionDir, call, i.securityAgent, instanceInfo); err != nil {
return instanceDir, instanceID, err