Merge "veyron2/services/store: Add a Dir interface."
diff --git a/examples/rockpaperscissors/rpsbot/main.go b/examples/rockpaperscissors/rpsbot/main.go
index 2e0d662..67af3f2 100644
--- a/examples/rockpaperscissors/rpsbot/main.go
+++ b/examples/rockpaperscissors/rpsbot/main.go
@@ -16,9 +16,13 @@
"veyron/examples/rockpaperscissors/impl"
"veyron/lib/signals"
sflag "veyron/security/flag"
+ "veyron/services/mgmt/debug"
+
+ "veyron2"
"veyron2/context"
"veyron2/ipc"
"veyron2/rt"
+ "veyron2/security"
"veyron2/vlog"
)
@@ -29,6 +33,29 @@
address = flag.String("address", ":0", "address to listen on")
)
+// The dispatcher returns the RPS invoker unless a suffix is used, or the method
+// called is Glob. This is intended to exercise the DebugServer code.
+type dispatcher struct {
+ rpsInvoker ipc.Invoker
+ auth security.Authorizer
+ debug ipc.Dispatcher
+}
+
+func (d *dispatcher) Lookup(suffix, method string) (ipc.Invoker, security.Authorizer, error) {
+ if len(suffix) == 0 && method != "Glob" {
+ return d.rpsInvoker, d.auth, nil
+ }
+ return d.debug.Lookup(suffix, method)
+}
+
+func newDispatcher(runtime veyron2.Runtime, service *impl.RPS, auth security.Authorizer) *dispatcher {
+ return &dispatcher{
+ rpsInvoker: ipc.ReflectInvoker(rps.NewServerRockPaperScissors(service)),
+ auth: auth,
+ debug: debug.NewDispatcher(runtime, vlog.Log.LogDir(), auth),
+ }
+}
+
func main() {
r := rt.Init()
defer r.Cleanup()
@@ -38,10 +65,10 @@
}
defer server.Stop()
- rand.Seed(time.Now().UTC().UnixNano())
+ rand.Seed(time.Now().UnixNano())
rpsService := impl.NewRPS()
- dispatcher := ipc.LeafDispatcher(rps.NewServerRockPaperScissors(rpsService), sflag.NewAuthorizerOrDie())
+ dispatcher := newDispatcher(r, rpsService, sflag.NewAuthorizerOrDie())
ep, err := server.Listen(*protocol, *address)
if err != nil {
diff --git a/services/mgmt/node/impl/app_invoker.go b/services/mgmt/node/impl/app_invoker.go
index efbfe8e..c81e079 100644
--- a/services/mgmt/node/impl/app_invoker.go
+++ b/services/mgmt/node/impl/app_invoker.go
@@ -9,6 +9,7 @@
// <config.Root>/
// app-<hash 1>/ - the application dir is named using a hash of the application title
// installation-<id 1>/ - installations are labelled with ids
+// <status> - one of the values for installationState enum
// <version 1 timestamp>/ - timestamp of when the version was downloaded
// bin - application binary
// previous - symbolic link to previous version directory (TODO)
@@ -105,6 +106,43 @@
"veyron2/vlog"
)
+// installationState describes the states that an installation can be in at any
+// time.
+type installationState int
+
+const (
+ active installationState = iota
+ uninstalled
+)
+
+// String returns the name that will be used to encode the state as a file name
+// in the installation's dir.
+func (s installationState) String() string {
+ switch s {
+ case active:
+ return "active"
+ case uninstalled:
+ return "uninstalled"
+ default:
+ return "unknown"
+ }
+}
+
+func installationStateIs(installationDir string, state installationState) bool {
+ if _, err := os.Stat(filepath.Join(installationDir, state.String())); err != nil {
+ return false
+ }
+ return true
+}
+
+func transitionInstallation(installationDir string, initial, target installationState) error {
+ return transitionState(installationDir, initial, target)
+}
+
+func initializeInstallation(installationDir string, initial installationState) error {
+ return initializeState(installationDir, initial)
+}
+
// instanceState describes the states that an instance can be in at any time.
type instanceState int
@@ -138,9 +176,17 @@
}
}
-func transition(instanceDir string, initial, target instanceState) error {
- initialState := filepath.Join(instanceDir, initial.String())
- targetState := filepath.Join(instanceDir, target.String())
+func transitionInstance(instanceDir string, initial, target instanceState) error {
+ return transitionState(instanceDir, initial, target)
+}
+
+func initializeInstance(instanceDir string, initial instanceState) error {
+ return initializeState(instanceDir, initial)
+}
+
+func transitionState(dir string, initial, target fmt.Stringer) error {
+ initialState := filepath.Join(dir, initial.String())
+ targetState := filepath.Join(dir, target.String())
if err := os.Rename(initialState, targetState); err != nil {
if os.IsNotExist(err) {
return errInvalidOperation
@@ -151,8 +197,8 @@
return nil
}
-func initializeState(instanceDir string, initial instanceState) error {
- initialStatus := filepath.Join(instanceDir, initial.String())
+func initializeState(dir string, initial fmt.Stringer) error {
+ initialStatus := filepath.Join(dir, initial.String())
if err := ioutil.WriteFile(initialStatus, []byte("status"), 0600); err != nil {
vlog.Errorf("WriteFile(%v) failed: %v", initialStatus, err)
return errOperationFailed
@@ -303,7 +349,7 @@
return "", errOperationFailed
}
deferrer := func() {
- if err := os.RemoveAll(versionDir); err != nil {
+ if err := os.RemoveAll(installationDir); err != nil {
vlog.Errorf("RemoveAll(%v) failed: %v", versionDir, err)
}
}
@@ -327,6 +373,9 @@
vlog.Errorf("Symlink(%v, %v) failed: %v", versionDir, link, err)
return "", errOperationFailed
}
+ if err := initializeInstallation(installationDir, active); err != nil {
+ return "", err
+ }
deferrer = nil
return naming.Join(envelope.Title, installationID), nil
}
@@ -383,6 +432,9 @@
if err != nil {
return "", "", err
}
+ if !installationStateIs(installationDir, active) {
+ return "", "", errInvalidOperation
+ }
instanceID := generateID()
instanceDir := filepath.Join(installationDir, "instances", instanceDirName(instanceID))
if mkdir(instanceDir) != nil {
@@ -399,7 +451,7 @@
vlog.Errorf("Symlink(%v, %v) failed: %v", versionDir, versionLink, err)
return instanceDir, instanceID, errOperationFailed
}
- if err := initializeState(instanceDir, suspended); err != nil {
+ if err := initializeInstance(instanceDir, suspended); err != nil {
return instanceDir, instanceID, err
}
return instanceDir, instanceID, nil
@@ -494,7 +546,7 @@
}
func (i *appInvoker) run(instanceDir string) error {
- if err := transition(instanceDir, suspended, starting); err != nil {
+ if err := transitionInstance(instanceDir, suspended, starting); err != nil {
return err
}
cmd, err := genCmd(instanceDir)
@@ -502,10 +554,10 @@
err = i.startCmd(instanceDir, cmd)
}
if err != nil {
- transition(instanceDir, starting, suspended)
+ transitionInstance(instanceDir, starting, suspended)
return err
}
- return transition(instanceDir, starting, started)
+ return transitionInstance(instanceDir, starting, started)
}
func (i *appInvoker) Start(ipc.ServerContext) ([]string, error) {
@@ -589,17 +641,17 @@
if err != nil {
return err
}
- if err := transition(instanceDir, suspended, stopped); err == errOperationFailed || err == nil {
+ if err := transitionInstance(instanceDir, suspended, stopped); err == errOperationFailed || err == nil {
return err
}
- if err := transition(instanceDir, started, stopping); err != nil {
+ if err := transitionInstance(instanceDir, started, stopping); err != nil {
return err
}
if err := stop(instanceDir); err != nil {
- transition(instanceDir, stopping, started)
+ transitionInstance(instanceDir, stopping, started)
return err
}
- return transition(instanceDir, stopping, stopped)
+ return transitionInstance(instanceDir, stopping, stopped)
}
func (i *appInvoker) Suspend(ipc.ServerContext) error {
@@ -607,27 +659,30 @@
if err != nil {
return err
}
- if err := transition(instanceDir, started, suspending); err != nil {
+ if err := transitionInstance(instanceDir, started, suspending); err != nil {
return err
}
if err := stop(instanceDir); err != nil {
- transition(instanceDir, suspending, started)
+ transitionInstance(instanceDir, suspending, started)
return err
}
- return transition(instanceDir, suspending, suspended)
+ return transitionInstance(instanceDir, suspending, suspended)
}
-func (*appInvoker) Uninstall(ipc.ServerContext) error {
+func (i *appInvoker) Uninstall(ipc.ServerContext) error {
+ installationDir, err := i.installationDir()
+ if err != nil {
+ return err
+ }
+ return transitionInstallation(installationDir, active, uninstalled)
+}
+
+func (*appInvoker) Update(ipc.ServerContext) error {
// TODO(jsimsa): Implement.
return nil
}
-func (i *appInvoker) Update(ipc.ServerContext) error {
- // TODO(jsimsa): Implement.
- return nil
-}
-
-func (i *appInvoker) UpdateTo(_ ipc.ServerContext, von string) error {
+func (*appInvoker) UpdateTo(_ ipc.ServerContext, von string) error {
// TODO(jsimsa): Implement.
return nil
}
diff --git a/services/mgmt/node/impl/impl_test.go b/services/mgmt/node/impl/impl_test.go
index 2dd443f..d6856f2 100644
--- a/services/mgmt/node/impl/impl_test.go
+++ b/services/mgmt/node/impl/impl_test.go
@@ -452,25 +452,37 @@
return appID
}
-func startApp(t *testing.T, appID string) string {
+func startAppImpl(t *testing.T, appID string) (string, error) {
appsName := "nm//apps"
appName := naming.Join(appsName, appID)
stub, err := node.BindApplication(appName)
if err != nil {
t.Fatalf("BindApplication(%v) failed: %v", appName, err)
}
- var instanceID string
if instanceIDs, err := stub.Start(rt.R().NewContext()); err != nil {
- t.Fatalf("Start failed: %v", err)
+ return "", err
} else {
if want, got := 1, len(instanceIDs); want != got {
t.Fatalf("Expected %v instance ids, got %v instead", want, got)
}
- instanceID = instanceIDs[0]
+ return instanceIDs[0], nil
+ }
+}
+
+func startApp(t *testing.T, appID string) string {
+ instanceID, err := startAppImpl(t, appID)
+ if err != nil {
+ t.Fatalf("Start failed: %v", err)
}
return instanceID
}
+func startAppShouldFail(t *testing.T, appID string, expectedError verror.ID) {
+ if _, err := startAppImpl(t, appID); err == nil || !verror.Is(err, expectedError) {
+ t.Fatalf("Start(%v) expected to fail with %v, got %v instead", appID, expectedError, err)
+ }
+}
+
func stopApp(t *testing.T, appID, instanceID string) {
appsName := "nm//apps"
appName := naming.Join(appsName, appID)
@@ -510,6 +522,18 @@
}
}
+func uninstallApp(t *testing.T, appID string) {
+ appsName := "nm//apps"
+ appName := naming.Join(appsName, appID)
+ stub, err := node.BindApplication(appName)
+ if err != nil {
+ t.Fatalf("BindApplication(%v) failed: %v", appName, err)
+ }
+ if err := stub.Uninstall(rt.R().NewContext()); err != nil {
+ t.Fatalf("Uninstall failed: %v", err)
+ }
+}
+
func verifyAppWorkspace(t *testing.T, root, appID, instanceID string) {
// HACK ALERT: for now, we peek inside the node manager's directory
// structure (which ought to be opaque) to check for what the app has
@@ -596,6 +620,12 @@
verifyAppWorkspace(t, root, appID, instanceID)
+ // Uninstall the app.
+ uninstallApp(t, appID)
+
+ // Starting new instances should no longer be allowed.
+ startAppShouldFail(t, appID, verror.BadArg)
+
// Cleanly shut down the node manager.
syscall.Kill(nm.Cmd.Process.Pid, syscall.SIGINT)
nm.Expect("nm terminating")
diff --git a/tools/playground/README.md b/tools/playground/README.md
index 30d1d50..aa0c6af 100644
--- a/tools/playground/README.md
+++ b/tools/playground/README.md
@@ -1,18 +1,12 @@
# Running the playground compile server locally
-Install Docker for Linux:
+Install Docker on Goobuntu:
-TODO(sadovsky): These instructions don't seem to work, and furthermore, adding
-docker.list to sources.list.d may pose a security risk.
+ * http://go/installdocker
- $ sudo sh -c "echo deb https://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list"
- $ sudo apt-get update
- $ sudo apt-get install lxc-docker
- $ aptitude versions lxc-docker
+Install Docker on OS X:
-Or for OS X, from this site:
-
- https://github.com/boot2docker/osx-installer/releases
+ * https://github.com/boot2docker/osx-installer/releases
Build the playground Docker image (this will take a while...):
@@ -20,6 +14,9 @@
$ sudo docker build -t playground $VEYRON_ROOT/veyron/go/src/veyron/tools/playground/builder/.
+Note: If you want to ensure an up-to-date version of veyron is installed in the
+Docker image, run the above command with the "--no-cache" flag.
+
Install the playground binaries:
$ go install veyron/tools/playground/...
diff --git a/tools/playground/builder/Dockerfile b/tools/playground/builder/Dockerfile
index f846ab1..b34ad18 100644
--- a/tools/playground/builder/Dockerfile
+++ b/tools/playground/builder/Dockerfile
@@ -8,10 +8,14 @@
RUN /usr/local/veyron/bin/veyron profile setup core
RUN rm /root/.netrc
+# Uncomment the following line to use the local copy of vbuild.go. This is
+# useful when developing and testing local changes to vbuild.go.
+# ADD vbuild.go $VEYRON_ROOT/veyron/go/src/tools/playground/builder
+
WORKDIR /usr/local/veyron/veyron
-ENV PATH /usr/local/veyron/veyron/go/bin:/usr/local/veyron/veyron/scripts/build:/usr/local/bin:/usr/bin:/bin
-RUN go install veyron/tools/identity veyron/services/mounttable/mounttabled veyron2/vdl/vdl
-RUN go install veyron/tools/playground/builder veyron/tools/playground/compilerd
+ENV PATH /usr/local/veyron/veyron/go/bin:/usr/local/bin:/usr/bin:/bin
+RUN $VEYRON_ROOT/veyron/scripts/build/go install veyron/tools/identity veyron/services/mounttable/mounttabled veyron2/vdl/vdl
+RUN $VEYRON_ROOT/veyron/scripts/build/go install veyron/tools/playground/builder veyron/tools/playground/compilerd
RUN /usr/sbin/useradd -d /home/playground -m playground