veyron/tools/mgmt/device/impl: refactor local servers setup in local-install

This cleans up the code a bit and prepares the way for another change that
will enable package override during install.

Change-Id: If0faab4e8db73141ff54cc7519d14c6511fa4467
diff --git a/tools/mgmt/device/impl/local_install.go b/tools/mgmt/device/impl/local_install.go
index b6a926d..43b6e19 100644
--- a/tools/mgmt/device/impl/local_install.go
+++ b/tools/mgmt/device/impl/local_install.go
@@ -61,26 +61,40 @@
 	return o, &openAuthorizer{}, nil
 }
 
-func createServer(ctx *context.T, stderr io.Writer, objects map[string]interface{}) (string, func(), error) {
+type mapServer struct {
+	name       string
+	dispatcher mapDispatcher
+}
+
+func (ms *mapServer) serve(name string, object interface{}) (string, error) {
+	if _, ok := ms.dispatcher[name]; ok {
+		return "", fmt.Errorf("can't have more than one object with name %v", name)
+	}
+	ms.dispatcher[name] = object
+	return naming.Join(ms.name, name), nil
+}
+
+func createServer(ctx *context.T, stderr io.Writer) (*mapServer, func(), error) {
 	server, err := veyron2.NewServer(ctx)
 	if err != nil {
-		return "", nil, err
+		return nil, nil, err
 	}
 	spec := veyron2.GetListenSpec(ctx)
 	endpoints, err := server.Listen(spec)
 	if err != nil {
-		return "", nil, err
+		return nil, nil, err
 	}
 	var name string
 	if spec.Proxy != "" {
 		id, err := uniqueid.Random()
 		if err != nil {
-			return "", nil, err
+			return nil, nil, err
 		}
 		name = id.String()
 	}
-	if err := server.ServeDispatcher(name, mapDispatcher(objects)); err != nil {
-		return "", nil, err
+	dispatcher := make(mapDispatcher)
+	if err := server.ServeDispatcher(name, dispatcher); err != nil {
+		return nil, nil, err
 	}
 	cleanup := func() {
 		if err := server.Stop(); err != nil {
@@ -98,12 +112,12 @@
 		if len(nsRoots) > 0 {
 			name = naming.Join(nsRoots[0], name)
 		}
-		return name, cleanup, nil
+	} else if len(endpoints) > 0 {
+		name = endpoints[0].Name()
+	} else {
+		return nil, nil, fmt.Errorf("no endpoints")
 	}
-	if len(endpoints) == 0 {
-		return "", nil, fmt.Errorf("no endpoints")
-	}
-	return endpoints[0].Name(), cleanup, nil
+	return &mapServer{name: name, dispatcher: dispatcher}, cleanup, nil
 }
 
 var errNotImplemented = fmt.Errorf("method not implemented")
@@ -184,6 +198,26 @@
 	return errNotImplemented
 }
 
+func servePackage(p string, ms *mapServer, tmpZipDir string) (string, string, error) {
+	info, err := os.Stat(p)
+	if os.IsNotExist(err) {
+		return "", "", fmt.Errorf("%v not found: %v", p, err)
+	} else if err != nil {
+		return "", "", fmt.Errorf("Stat(%v) failed: %v", p, err)
+	}
+	pkgName := naming.Join("packages", info.Name())
+	fileName := p
+	// Directory packages first get zip'ped.
+	if info.IsDir() {
+		fileName = filepath.Join(tmpZipDir, info.Name()+".zip")
+		if err := packages.CreateZip(fileName, p); err != nil {
+			return "", "", err
+		}
+	}
+	name, err := ms.serve(pkgName, repository.BinaryServer(binaryInvoker(fileName)))
+	return info.Name(), name, err
+}
+
 // runInstallLocal creates a new envelope on the fly from the provided
 // arguments, and then points the device manager back to itself for downloading
 // the app envelope and binary.
@@ -231,53 +265,38 @@
 	if _, err := os.Stat(binary); err != nil {
 		return fmt.Errorf("binary %v not found: %v", binary, err)
 	}
-	objects := map[string]interface{}{"binary": repository.BinaryServer(binaryInvoker(binary))}
-	name, cancel, err := createServer(gctx, cmd.Stderr(), objects)
+	server, cancel, err := createServer(gctx, cmd.Stderr())
 	if err != nil {
 		return fmt.Errorf("failed to create server: %v", err)
 	}
 	defer cancel()
-	envelope.Binary = naming.Join(name, "binary")
+	envelope.Binary, err = server.serve("binary", repository.BinaryServer(binaryInvoker(binary)))
+	if err != nil {
+		return err
+	}
 
 	// For each package dir/file specified in the arguments list, set up an
 	// object in the binary service to serve that package, and add the
 	// object name to the envelope's Packages map.
-	var tmpZipDir string
+	tmpZipDir, err := ioutil.TempDir("", "packages")
+	if err != nil {
+		return fmt.Errorf("failed to create a temp dir for zip packages: %v", err)
+	}
+	defer os.RemoveAll(tmpZipDir)
 	for _, p := range pkgs {
 		if envelope.Packages == nil {
 			envelope.Packages = make(map[string]string)
 		}
-		info, err := os.Stat(p)
-		if os.IsNotExist(err) {
-			return fmt.Errorf("%v not found: %v", p, err)
-		} else if err != nil {
-			return fmt.Errorf("Stat(%v) failed: %v", p, err)
+		pname, oname, err := servePackage(p, server, tmpZipDir)
+		if err != nil {
+			return err
 		}
-		pkgName := naming.Join("packages", info.Name())
-		if _, ok := objects[pkgName]; ok {
-			return fmt.Errorf("can't have more than one package with name %v", info.Name())
-		}
-		fileName := p
-		// Directory packages first get zip'ped.
-		if info.IsDir() {
-			if tmpZipDir == "" {
-				tmpZipDir, err = ioutil.TempDir("", "packages")
-				if err != nil {
-					return fmt.Errorf("failed to create a temp dir for zip packages: %v", err)
-				}
-				defer os.RemoveAll(tmpZipDir)
-			}
-			fileName = filepath.Join(tmpZipDir, info.Name()+".zip")
-			if err := packages.CreateZip(fileName, p); err != nil {
-				return err
-			}
-		}
-		objects[pkgName] = repository.BinaryServer(binaryInvoker(fileName))
-		envelope.Packages[info.Name()] = naming.Join(name, pkgName)
+		envelope.Packages[pname] = oname
 	}
-
-	objects["application"] = repository.ApplicationServer(envelopeInvoker(envelope))
-	appName := naming.Join(name, "application")
+	appName, err := server.serve("application", repository.ApplicationServer(envelopeInvoker(envelope)))
+	if err != nil {
+		return err
+	}
 	appID, err := device.ApplicationClient(deviceName).Install(gctx, appName, device.Config(configOverride))
 	// Reset the value for any future invocations of "install" or
 	// "install-local" (we run more than one command per process in unit