Merge "veyron.core: Fix tests that leave files in /tmp"
diff --git a/lib/testutil/integration/util.go b/lib/testutil/integration/util.go
index 9968938..d5e2cf4 100644
--- a/lib/testutil/integration/util.go
+++ b/lib/testutil/integration/util.go
@@ -91,23 +91,29 @@
if err != nil {
return nil, err
}
+ // TODO(jsimsa): Consider using the veyron exec library to
+ // facilitate coordination and communication between the
+ // parent and the child process.
if err := cmd.Start(); err != nil {
return nil, fmt.Errorf("%q failed: %v", strings.Join(cmd.Args, " "), err)
}
- // Wait for the server to mount itself.
+ // Wait for the server to mount both its tcp and ws endpoint.
ready := make(chan struct{}, 1)
go func() {
defer outPipe.Close()
scanner := bufio.NewScanner(outPipe)
+ nmounts := 0
for scanner.Scan() {
line := scanner.Text()
if strings.Index(line, "ipc pub: mount") != -1 {
- close(ready)
- return
+ nmounts++
+ if nmounts == 2 {
+ close(ready)
+ }
}
}
if err := scanner.Err(); err != nil {
- fmt.Fprintf(os.Stderr, "Scan() failOAed: %v\n", err)
+ fmt.Fprintf(os.Stderr, "Scan() failed: %v\n", err)
}
}()
select {
@@ -115,6 +121,6 @@
return cmd.Process, nil
case <-time.After(time.Second):
cmd.Process.Kill()
- return nil, fmt.Errorf("timed out waiting for %q to mount itself", bin)
+ return nil, fmt.Errorf("timed out waiting for %q to mount itself", strings.Join(cmd.Args, " "))
}
}
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index 1516527..af3a370 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -429,7 +429,7 @@
for {
flow, err := ln.Accept()
if err != nil {
- vlog.VI(10).Infof("ipc: Accept on %v failed: %v", ln, err)
+ vlog.VI(10).Infof("ipc: Accept on %v failed: %v", ep, err)
return
}
calls.Add(1)
@@ -437,13 +437,13 @@
defer calls.Done()
fs, err := newFlowServer(flow, s)
if err != nil {
- vlog.Errorf("newFlowServer on %v failed: %v", ln, err)
+ vlog.Errorf("newFlowServer on %v failed: %v", ep, err)
return
}
if err := fs.serve(); err != nil {
// TODO(caprita): Logging errors here is too spammy. For example, "not
// authorized" errors shouldn't be logged as server errors.
- vlog.Errorf("Flow serve on %v failed: %v", ln, err)
+ vlog.Errorf("Flow serve on %v failed: %v", ep, err)
}
}(flow)
}
diff --git a/runtimes/google/ipc/signature_test.go b/runtimes/google/ipc/signature_test.go
index 83d9c78..0f5c5ac 100644
--- a/runtimes/google/ipc/signature_test.go
+++ b/runtimes/google/ipc/signature_test.go
@@ -100,7 +100,7 @@
}},
}
for _, test := range tests {
- sig, err := reserved.MethodSignature(runtime.NewContext(), name, test.Method)
+ sig, err := reserved.MethodSignature(runtime.NewContext(), nil, name, test.Method)
if err != nil {
t.Errorf("call failed: %v", err)
}
@@ -155,7 +155,7 @@
},
}
- sig, err := reserved.Signature(runtime.NewContext(), name)
+ sig, err := reserved.Signature(runtime.NewContext(), nil, name)
if err != nil {
t.Errorf("call failed: %v", err)
}
diff --git a/runtimes/google/naming/namespace/all_test.go b/runtimes/google/naming/namespace/all_test.go
index 64c1cd2..2bb395c 100644
--- a/runtimes/google/naming/namespace/all_test.go
+++ b/runtimes/google/naming/namespace/all_test.go
@@ -623,13 +623,10 @@
}
func TestBadRoots(t *testing.T) {
- _, r, cleanup := createRuntimes(t)
- defer cleanup()
-
- if _, err := namespace.New(r); err != nil {
+ if _, err := namespace.New(); err != nil {
t.Errorf("namespace.New should not have failed with no roots")
}
- if _, err := namespace.New(r, "not a rooted name"); err == nil {
+ if _, err := namespace.New("not a rooted name"); err == nil {
t.Errorf("namespace.New should have failed with an unrooted name")
}
}
diff --git a/runtimes/google/naming/namespace/cache_test.go b/runtimes/google/naming/namespace/cache_test.go
index e12cd69..1b7894e 100644
--- a/runtimes/google/naming/namespace/cache_test.go
+++ b/runtimes/google/naming/namespace/cache_test.go
@@ -109,7 +109,7 @@
{"/h2//c", "/h3"},
{"/h3//d", "/h4:1234"},
}
- ns, _ := New(nil)
+ ns, _ := New()
c := ns.resolutionCache.(*ttlCache)
for _, p := range preload {
e := &naming.MountEntry{Servers: []naming.MountedServer{naming.MountedServer{Server: "p.server", Expires: future(3000)}}}
@@ -152,7 +152,7 @@
}
func TestCacheDisableEnable(t *testing.T) {
- ns, _ := New(nil)
+ ns, _ := New()
// Default should be working resolution cache.
name := "/h1//a"
diff --git a/runtimes/google/naming/namespace/glob.go b/runtimes/google/naming/namespace/glob.go
index 632c0b8..937b6b6 100644
--- a/runtimes/google/naming/namespace/glob.go
+++ b/runtimes/google/naming/namespace/glob.go
@@ -7,6 +7,7 @@
"veyron.io/veyron/veyron/lib/glob"
+ "veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/naming"
@@ -30,6 +31,7 @@
// recursive true to continue below the matched pattern
func (ns *namespace) globAtServer(ctx context.T, qe *queuedEntry, pattern *glob.Glob, l *list.List) error {
server := qe.me
+ client := veyron2.RuntimeFromContext(ctx).Client()
pstr := pattern.String()
vlog.VI(2).Infof("globAtServer(%v, %v)", *server, pstr)
@@ -54,7 +56,6 @@
// Don't further resolve s.Server.
callCtx, _ := ctx.WithTimeout(callTimeout)
- client := ns.rt.Client()
call, err := client.StartCall(callCtx, s.Server, ipc.GlobMethod, []interface{}{pstr}, options.NoResolve(true))
if err != nil {
lastErr = err
diff --git a/runtimes/google/naming/namespace/mount.go b/runtimes/google/naming/namespace/mount.go
index ac2e875..3d4e03a 100644
--- a/runtimes/google/naming/namespace/mount.go
+++ b/runtimes/google/naming/namespace/mount.go
@@ -5,6 +5,7 @@
inaming "veyron.io/veyron/veyron/runtimes/google/naming"
+ "veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/naming"
@@ -118,9 +119,12 @@
}
}
}
+
+ client := veyron2.RuntimeFromContext(ctx).Client()
+
// Mount the server in all the returned mount tables.
f := func(ctx context.T, mt, id string) status {
- return mountIntoMountTable(ctx, ns.rt.Client(), mt, server, ttl, flags, id)
+ return mountIntoMountTable(ctx, client, mt, server, ttl, flags, id)
}
err := ns.dispatch(ctx, name, f)
vlog.VI(1).Infof("Mount(%s, %s) -> %v", name, server, err)
@@ -130,8 +134,9 @@
func (ns *namespace) Unmount(ctx context.T, name, server string) error {
defer vlog.LogCall()()
// Unmount the server from all the mount tables.
- f := func(ctx context.T, mt, id string) status {
- return unmountFromMountTable(ctx, ns.rt.Client(), mt, server, id)
+ client := veyron2.RuntimeFromContext(ctx).Client()
+ f := func(context context.T, mt, id string) status {
+ return unmountFromMountTable(ctx, client, mt, server, id)
}
err := ns.dispatch(ctx, name, f)
vlog.VI(1).Infof("Unmount(%s, %s) -> %v", name, server, err)
diff --git a/runtimes/google/naming/namespace/namespace.go b/runtimes/google/naming/namespace/namespace.go
index 9d58742..16ec70e 100644
--- a/runtimes/google/naming/namespace/namespace.go
+++ b/runtimes/google/naming/namespace/namespace.go
@@ -6,7 +6,6 @@
inaming "veyron.io/veyron/veyron/runtimes/google/naming"
- "veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/verror"
"veyron.io/veyron/veyron2/vlog"
@@ -18,7 +17,6 @@
// namespace is an implementation of naming.Namespace.
type namespace struct {
sync.RWMutex
- rt veyron2.Runtime
// the default root servers for resolutions in this namespace.
roots []string
@@ -45,13 +43,12 @@
}
// Create a new namespace.
-func New(rt veyron2.Runtime, roots ...string) (*namespace, error) {
+func New(roots ...string) (*namespace, error) {
if !rooted(roots) {
return nil, badRoots(roots)
}
// A namespace with no roots can still be used for lookups of rooted names.
return &namespace{
- rt: rt,
roots: roots,
maxResolveDepth: defaultMaxResolveDepth,
maxRecursiveGlobDepth: defaultMaxRecursiveGlobDepth,
diff --git a/runtimes/google/naming/namespace/resolve.go b/runtimes/google/naming/namespace/resolve.go
index 8647590..d010811 100644
--- a/runtimes/google/naming/namespace/resolve.go
+++ b/runtimes/google/naming/namespace/resolve.go
@@ -5,6 +5,7 @@
"fmt"
"runtime"
+ "veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/naming"
@@ -80,6 +81,7 @@
return nil, verror.Make(naming.ErrNoSuchName, ctx, name)
}
pattern := getRootPattern(opts)
+ client := veyron2.RuntimeFromContext(ctx).Client()
// Iterate walking through mount table servers.
for remaining := ns.maxResolveDepth; remaining > 0; remaining-- {
vlog.VI(2).Infof("ResolveX(%s) loop %v", name, *e)
@@ -89,7 +91,7 @@
}
var err error
curr := e
- if e, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), curr, pattern); err != nil {
+ if e, err = ns.resolveAgainstMountTable(ctx, client, curr, pattern); err != nil {
// Lots of reasons why another error can happen. We are trying
// to single out "this isn't a mount table".
if notAnMT(err) {
@@ -130,6 +132,7 @@
return nil, verror.Make(naming.ErrNoMountTable, ctx)
}
pattern := getRootPattern(opts)
+ client := veyron2.RuntimeFromContext(ctx).Client()
last := e
for remaining := ns.maxResolveDepth; remaining > 0; remaining-- {
vlog.VI(2).Infof("ResolveToMountTableX(%s) loop %v", name, e)
@@ -140,7 +143,7 @@
vlog.VI(1).Infof("ResolveToMountTableX(%s) -> %v", name, last)
return last, nil
}
- if e, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), e, pattern); err != nil {
+ if e, err = ns.resolveAgainstMountTable(ctx, client, e, pattern); err != nil {
if verror.Is(err, naming.ErrNoSuchNameRoot.ID) {
vlog.VI(1).Infof("ResolveToMountTableX(%s) -> %v (NoSuchRoot: %v)", name, last, curr)
return last, nil
@@ -224,10 +227,11 @@
if err != nil {
return nil, err
}
+ client := veyron2.RuntimeFromContext(ctx).Client()
for remaining := ns.maxResolveDepth; remaining > 0; remaining-- {
vlog.VI(2).Infof("Unresolve loop %s", names)
curr := names
- if names, err = unresolveAgainstServer(ctx, ns.rt.Client(), names); err != nil {
+ if names, err = unresolveAgainstServer(ctx, client, names); err != nil {
return nil, err
}
if len(names) == 0 {
diff --git a/runtimes/google/rt/rt.go b/runtimes/google/rt/rt.go
index ab635b2..e91f6f8 100644
--- a/runtimes/google/rt/rt.go
+++ b/runtimes/google/rt/rt.go
@@ -97,7 +97,7 @@
vlog.VI(1).Infof("Using profile %q", rt.profile.Name())
}
- if ns, err := namespace.New(rt, rt.flags.NamespaceRoots...); err != nil {
+ if ns, err := namespace.New(rt.flags.NamespaceRoots...); err != nil {
return nil, fmt.Errorf("Couldn't create mount table: %v", err)
} else {
rt.ns = ns
diff --git a/services/mgmt/application/applicationd/test.sh b/services/mgmt/application/applicationd/test.sh
deleted file mode 100755
index 7f40da2..0000000
--- a/services/mgmt/application/applicationd/test.sh
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/bash
-
-# Test the application repository daemon.
-#
-# This test starts an application repository daemon and uses the
-# application repository client to verify that <application>.Put(),
-# <application>.Match(), and <application>.Remove() work as expected.
-
-source "${VEYRON_ROOT}/scripts/lib/shell_test.sh"
-
-readonly WORKDIR="${shell_test_WORK_DIR}"
-
-build() {
- APPLICATIOND_BIN="$(shell_test::build_go_binary 'veyron.io/veyron/veyron/services/mgmt/application/applicationd')"
- APPLICATION_BIN="$(shell_test::build_go_binary 'veyron.io/veyron/veyron/tools/application')"
-}
-
-main() {
- cd "${WORKDIR}"
- build
-
- shell_test::setup_server_test
-
- # Start the application repository daemon.
- local -r REPO="applicationd-test-repo"
- local -r STORE=$(shell::tmp_dir)
- shell_test::start_server "${APPLICATIOND_BIN}" --name="${REPO}" --store="${STORE}" --veyron.tcp.address=127.0.0.1:0 \
- || shell_test::fail "line ${LINENO} failed to start applicationd"
-
- # Create an application envelope.
- local -r APPLICATION="${REPO}/test-application/v1"
- local -r PROFILE="test-profile"
- local -r ENVELOPE_WANT=$(shell::tmp_file)
- cat > "${ENVELOPE_WANT}" <<EOF
-{"Title":"title", "Args":[], "Binary":"foo", "Env":[]}
-EOF
- "${APPLICATION_BIN}" put "${APPLICATION}" "${PROFILE}" "${ENVELOPE_WANT}" || shell_test::fail "line ${LINENO}: 'put' failed"
-
- # Match the application envelope.
- local -r ENVELOPE_GOT=$(shell::tmp_file)
- "${APPLICATION_BIN}" match "${APPLICATION}" "${PROFILE}" | tee "${ENVELOPE_GOT}" || shell_test::fail "line ${LINENO}: 'match' failed"
- if [[ $(cmp "${ENVELOPE_WANT}" "${ENVELOPE_GOT}" &> /dev/null) ]]; then
- shell_test::fail "mismatching application envelopes"
- fi
-
- # Remove the application envelope.
- "${APPLICATION_BIN}" remove "${APPLICATION}" "${PROFILE}" || shell_test::fail "line ${LINENO}: 'remove' failed"
-
- # Check the application envelope no longer exists.
- local -r RESULT=$(shell::check_result "${APPLICATION_BIN}" match "${APPLICATION}" "${PROFILE}")
- shell_test::assert_ne "${RESULT}" "0" "${LINENO}"
-
- shell_test::pass
-}
-
-main "$@"
diff --git a/services/mgmt/application/applicationd/testdata/integration_test.go b/services/mgmt/application/applicationd/testdata/integration_test.go
new file mode 100644
index 0000000..aa0f440
--- /dev/null
+++ b/services/mgmt/application/applicationd/testdata/integration_test.go
@@ -0,0 +1,134 @@
+package integration_test
+
+import (
+ "bytes"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "veyron.io/veyron/veyron/lib/modules"
+ "veyron.io/veyron/veyron/lib/testutil/integration"
+ "veyron.io/veyron/veyron/lib/testutil/security"
+ _ "veyron.io/veyron/veyron/profiles"
+ "veyron.io/veyron/veyron2/naming"
+)
+
+var binPkgs = []string{
+ "veyron.io/veyron/veyron/services/mgmt/application/applicationd",
+ "veyron.io/veyron/veyron/tools/application",
+}
+
+func helper(t *testing.T, expectError bool, binDir, credentials, mt, cmd string, args ...string) string {
+ var out bytes.Buffer
+ args = append([]string{"-veyron.credentials=" + credentials, "-veyron.namespace.root=" + mt, cmd}, args...)
+ command := exec.Command(filepath.Join(binDir, "application"), args...)
+ command.Stdout = &out
+ command.Stderr = &out
+ err := command.Run()
+ if err != nil && !expectError {
+ t.Fatalf("%q failed: %v\n%v", strings.Join(command.Args, " "), err, out.String())
+ }
+ if err == nil && expectError {
+ t.Fatalf("%q did not fail when it should", strings.Join(command.Args, " "))
+ }
+ return strings.TrimSpace(out.String())
+
+}
+
+func matchEnvelope(t *testing.T, expectError bool, binDir, credentials, mt, name, suffix string) string {
+ return helper(t, expectError, binDir, credentials, mt, "match", naming.Join(name, suffix), "test-profile")
+}
+
+func putEnvelope(t *testing.T, binDir, credentials, mt, name, suffix, envelope string) string {
+ return helper(t, false, binDir, credentials, mt, "put", naming.Join(name, suffix), "test-profile", envelope)
+}
+
+func removeEnvelope(t *testing.T, binDir, credentials, mt, name, suffix string) string {
+ return helper(t, false, binDir, credentials, mt, "remove", naming.Join(name, suffix), "test-profile")
+}
+
+func TestHelperProcess(t *testing.T) {
+ modules.DispatchInTest()
+}
+
+func TestApplicationRepository(t *testing.T) {
+ // Build the required binaries.
+ binDir, cleanup, err := integration.BuildPkgs(binPkgs)
+ if err != nil {
+ t.Fatalf("%v", err)
+ }
+ defer cleanup()
+
+ // Start a root mount table.
+ shell, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("NewShell() failed: %v", err)
+ }
+ defer shell.Cleanup(os.Stdin, os.Stderr)
+ handle, mt, err := integration.StartRootMT(shell)
+ if err != nil {
+ t.Fatalf("%v", err)
+ }
+ defer handle.CloseStdin()
+
+ // Generate credentials.
+ root := security.NewPrincipal("root")
+ credentials := security.NewVeyronCredentials(root, "test-credentials")
+ defer os.RemoveAll(credentials)
+
+ // Start the application repository.
+ appRepoBin := filepath.Join(binDir, "applicationd")
+ appRepoName := "test-app-repo"
+ appRepoStore, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatalf("TempDir() failed: %v", err)
+ }
+ defer os.RemoveAll(appRepoStore)
+ args := []string{
+ "-name=" + appRepoName,
+ "-store=" + appRepoStore,
+ "-veyron.tcp.address=127.0.0.1:0",
+ "-veyron.credentials=" + credentials,
+ "-veyron.namespace.root=" + mt,
+ }
+ serverProcess, err := integration.StartServer(appRepoBin, args)
+ if err != nil {
+ t.Fatalf("%v", err)
+ }
+ defer serverProcess.Kill()
+
+ // Create an application envelope.
+ appRepoSuffix := "test-application/v1"
+ appEnvelopeFile, err := ioutil.TempFile("", "")
+ if err != nil {
+ t.Fatalf("TempFile() failed: %v", err)
+ }
+ defer appEnvelopeFile.Close()
+ defer os.Remove(appEnvelopeFile.Name())
+ wantEnvelope := `{
+ "Title": "title",
+ "Args": null,
+ "Binary": "foo",
+ "Env": null,
+ "Packages": null
+}`
+ if _, err := appEnvelopeFile.Write([]byte(wantEnvelope)); err != nil {
+ t.Fatalf("Write() failed: %v", err)
+ }
+ putEnvelope(t, binDir, credentials, mt, appRepoName, appRepoSuffix, appEnvelopeFile.Name())
+
+ // Match the application envelope.
+ gotEnvelope := matchEnvelope(t, false, binDir, credentials, mt, appRepoName, appRepoSuffix)
+ if gotEnvelope != wantEnvelope {
+ t.Fatalf("unexpected output: got %v, want %v", gotEnvelope, wantEnvelope)
+ }
+
+ // Remove the application envelope.
+ removeEnvelope(t, binDir, credentials, mt, appRepoName, appRepoSuffix)
+
+ // Check that the application envelope no longer exists.
+ matchEnvelope(t, true, binDir, credentials, mt, appRepoName, appRepoSuffix)
+}
diff --git a/services/mgmt/binary/binaryd/main.go b/services/mgmt/binary/binaryd/main.go
index 16ac319..3ac83da 100644
--- a/services/mgmt/binary/binaryd/main.go
+++ b/services/mgmt/binary/binaryd/main.go
@@ -20,9 +20,9 @@
const defaultDepth = 3
var (
- name = flag.String("name", "", "name to mount the binary repository as")
- rootFlag = flag.String("root", "", "root directory for the binary repository")
- httpAddr = flag.String("http", ":0", "TCP address on which the HTTP server runs")
+ name = flag.String("name", "", "name to mount the binary repository as")
+ rootDirFlag = flag.String("root_dir", "", "root directory for the binary repository")
+ httpAddr = flag.String("http", ":0", "TCP address on which the HTTP server runs")
)
// toIPPort tries to swap in the 'best' accessible IP for the host part of the
@@ -53,32 +53,31 @@
}
defer runtime.Cleanup()
- root, err := impl.SetupRoot(*rootFlag)
+ rootDir, err := impl.SetupRootDir(*rootDirFlag)
if err != nil {
- vlog.Errorf("SetupRoot(%q) failed: %v", *rootFlag, err)
+ vlog.Errorf("SetupRootDir(%q) failed: %v", *rootDirFlag, err)
return
}
- vlog.Infof("Binary repository rooted at %v", root)
-
- state, err := impl.NewState(root, defaultDepth)
- if err != nil {
- vlog.Errorf("NewState(%v, %v) failed: %v", root, defaultDepth, err)
- return
- }
+ vlog.Infof("Binary repository rooted at %v", rootDir)
listener, err := net.Listen("tcp", *httpAddr)
if err != nil {
vlog.Errorf("Listen(%s) failed: %v", *httpAddr, err)
os.Exit(1)
}
- vlog.Infof("Binary repository HTTP server at: %q", toIPPort(listener.Addr().String()))
+ rootURL := toIPPort(listener.Addr().String())
+ state, err := impl.NewState(rootDir, rootURL, defaultDepth)
+ if err != nil {
+ vlog.Errorf("NewState(%v, %v, %v) failed: %v", rootDir, rootURL, defaultDepth, err)
+ return
+ }
+ vlog.Infof("Binary repository HTTP server at: %q", rootURL)
go func() {
if err := http.Serve(listener, http.FileServer(impl.NewHTTPRoot(state))); err != nil {
vlog.Errorf("Serve() failed: %v", err)
os.Exit(1)
}
}()
-
server, err := runtime.NewServer()
if err != nil {
vlog.Errorf("NewServer() failed: %v", err)
diff --git a/services/mgmt/binary/binaryd/test.sh b/services/mgmt/binary/binaryd/test.sh
deleted file mode 100755
index 17347fa..0000000
--- a/services/mgmt/binary/binaryd/test.sh
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/bin/bash
-
-# Test the binary repository daemon.
-#
-# This test starts a binary repository daemon and uses the binary
-# repository client to verify that <binary>.Upload(),
-# <binary>.Download(), and <binary>.Delete() work as expected.
-
-source "${VEYRON_ROOT}/scripts/lib/shell_test.sh"
-
-readonly WORKDIR="${shell_test_WORK_DIR}"
-
-build() {
- BINARYD_BIN="$(shell_test::build_go_binary 'veyron.io/veyron/veyron/services/mgmt/binary/binaryd')"
- BINARY_BIN="$(shell_test::build_go_binary 'veyron.io/veyron/veyron/tools/binary')"
-}
-
-main() {
- cd "${WORKDIR}"
- build
-
- shell_test::setup_server_test
-
- # Start the binary repository daemon.
- local -r REPO="binaryd-test-repo"
- shell_test::start_server "${BINARYD_BIN}" --name="${REPO}" --veyron.tcp.address=127.0.0.1:0 --http=127.0.0.1:0 \
- || shell_test::fail "line ${LINENO} failed to start binaryd"
- local -r HTTP_ADDR=$(grep 'HTTP server at: "' "${START_SERVER_LOG_FILE}" | sed -e 's/^.*HTTP server at: "//' | sed -e 's/"$//')
-
- # Create a binary file.
- local -r BINARY_SUFFIX="test-binary"
- local -r BINARY="${REPO}/${BINARY_SUFFIX}"
- local -r BINARY_FILE="${WORKDIR}/bin1"
- dd if=/dev/urandom of="${BINARY_FILE}" bs=1000000 count=16 \
- || shell_test::fail "line ${LINENO}: faile to create a random binary file"
- "${BINARY_BIN}" upload "${BINARY}" "${BINARY_FILE}" || shell_test::fail "line ${LINENO}: 'upload' failed"
-
- # Create TAR file.
- local -r TAR="${REPO}/tarobj"
- local -r TAR_FILE="${WORKDIR}/bin1.tar.gz"
- tar zcvf "${TAR_FILE}" "${BINARY_FILE}"
- "${BINARY_BIN}" upload "${TAR}" "${TAR_FILE}" || shell_test::fail "line ${LINENO}: 'upload' failed"
-
- # Download the binary file.
- local -r BINARY_FILE2="${WORKDIR}/bin2"
- "${BINARY_BIN}" download "${BINARY}" "${BINARY_FILE2}" || shell_test::fail "line ${LINENO}: 'RPC download' failed"
- if [[ $(cmp "${BINARY_FILE}" "${BINARY_FILE2}" &> /dev/null) ]]; then
- shell_test::fail "mismatching binary file downloaded via RPC"
- fi
- local -r BINARY_FILE2_INFO=$(cat "${BINARY_FILE2}.__info")
- shell_test::assert_eq "${BINARY_FILE2_INFO}" '{"Type":"application/octet-stream","Encoding":""}' "${LINENO}"
-
- # Download the tar file.
- local -r TAR_FILE2="${WORKDIR}/downloadedtar"
- "${BINARY_BIN}" download "${TAR}" "${TAR_FILE2}" || shell_test::fail "line ${LINENO}: 'RPC download' failed"
- if [[ $(cmp "${TAR_FILE}" "${TAR_FILE2}" &> /dev/null) ]]; then
- shell_test::fail "mismatching tar file downloaded via RPC"
- fi
- local -r TAR_FILE2_INFO=$(cat "${TAR_FILE2}.__info")
- shell_test::assert_eq "${TAR_FILE2_INFO}" '{"Type":"application/x-tar","Encoding":"gzip"}' "${LINENO}"
-
- local -r BINARY_FILE3="${WORKDIR}/bin3"
- curl -f -o "${BINARY_FILE3}" "http://${HTTP_ADDR}/${BINARY_SUFFIX}" || shell_test::fail "line ${LINENO}: 'HTTP download' failed"
- if [[ $(cmp "${BINARY_FILE}" "${BINARY_FILE3}" &> /dev/null) ]]; then
- shell_test::fail "mismatching binary file downloaded via HTTP"
- fi
-
- # Remove the files.
- "${BINARY_BIN}" delete "${BINARY}" || shell_test::fail "line ${LINENO}: 'delete' failed"
- "${BINARY_BIN}" delete "${TAR}" || shell_test::fail "line ${LINENO}: 'delete' failed"
-
- # Check the files no longer exist.
- local RESULT=$(shell::check_result "${BINARY_BIN}" download "${BINARY}" "${BINARY_FILE2}")
- shell_test::assert_ne "${RESULT}" "0" "${LINENO}"
- RESULT=$(shell::check_result "${BINARY_BIN}" download "${TAR}" "${TAR_FILE2}")
- shell_test::assert_ne "${RESULT}" "0" "${LINENO}"
-
- shell_test::pass
-}
-
-main "$@"
diff --git a/services/mgmt/binary/binaryd/testdata/integration_test.go b/services/mgmt/binary/binaryd/testdata/integration_test.go
new file mode 100644
index 0000000..7bd82f6
--- /dev/null
+++ b/services/mgmt/binary/binaryd/testdata/integration_test.go
@@ -0,0 +1,242 @@
+package integration_test
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "veyron.io/veyron/veyron/lib/modules"
+ "veyron.io/veyron/veyron/lib/testutil"
+ "veyron.io/veyron/veyron/lib/testutil/integration"
+ "veyron.io/veyron/veyron/lib/testutil/security"
+ _ "veyron.io/veyron/veyron/profiles"
+ "veyron.io/veyron/veyron2/naming"
+)
+
+func init() {
+ testutil.Init()
+}
+
+var binPkgs = []string{
+ "veyron.io/veyron/veyron/services/mgmt/binary/binaryd",
+ "veyron.io/veyron/veyron/tools/binary",
+}
+
+func checkFileType(t *testing.T, file, typeString string) {
+ var catOut bytes.Buffer
+ catCmd := exec.Command("cat", file+".__info")
+ catCmd.Stdout = &catOut
+ catCmd.Stderr = &catOut
+ if err := catCmd.Run(); err != nil {
+ t.Fatalf("%q failed: %v\n%v", strings.Join(catCmd.Args, " "), err, catOut.String())
+ }
+ if got, want := strings.TrimSpace(catOut.String()), typeString; got != want {
+ t.Fatalf("unexpect file type: got %v, want %v", got, want)
+ }
+}
+
+func compareFiles(t *testing.T, f1, f2 string) {
+ var cmpOut bytes.Buffer
+ cmpCmd := exec.Command("cmp", f1, f2)
+ cmpCmd.Stdout = &cmpOut
+ cmpCmd.Stderr = &cmpOut
+ if err := cmpCmd.Run(); err != nil {
+ t.Fatalf("%q failed: %v\n%v", strings.Join(cmpCmd.Args, " "), err, cmpOut.String())
+ }
+}
+
+func deleteFile(t *testing.T, binDir, credentials, mt, name, suffix string) {
+ var deleteOut bytes.Buffer
+ deleteArgs := []string{
+ "-veyron.credentials=" + credentials,
+ "-veyron.namespace.root=" + mt,
+ "delete", naming.Join(name, suffix),
+ }
+ deleteCmd := exec.Command(filepath.Join(binDir, "binary"), deleteArgs...)
+ deleteCmd.Stdout = &deleteOut
+ deleteCmd.Stderr = &deleteOut
+ if err := deleteCmd.Run(); err != nil {
+ t.Fatalf("%q failed: %v\n%v", strings.Join(deleteCmd.Args, " "), err, deleteOut.String())
+ }
+}
+
+func downloadFile(t *testing.T, expectError bool, binDir, credentials, mt, name, path, suffix string) {
+ var downloadOut bytes.Buffer
+ downloadArgs := []string{
+ "-veyron.credentials=" + credentials,
+ "-veyron.namespace.root=" + mt,
+ "download", naming.Join(name, suffix), path,
+ }
+ downloadCmd := exec.Command(filepath.Join(binDir, "binary"), downloadArgs...)
+ downloadCmd.Stdout = &downloadOut
+ downloadCmd.Stderr = &downloadOut
+ err := downloadCmd.Run()
+ if err != nil && !expectError {
+ t.Fatalf("%q failed: %v\n%v", strings.Join(downloadCmd.Args, " "), err, downloadOut.String())
+ }
+ if err == nil && expectError {
+ t.Fatalf("%q did not fail when it should", strings.Join(downloadCmd.Args, " "))
+ }
+}
+
+func downloadURL(t *testing.T, path, rootURL, suffix string) {
+ var curlOut bytes.Buffer
+ curlCmd := exec.Command("curl", "-f", "-o", path, fmt.Sprintf("%v/%v", rootURL, suffix))
+ curlCmd.Stdout = &curlOut
+ curlCmd.Stderr = &curlOut
+ if err := curlCmd.Run(); err != nil {
+ t.Fatalf("%q failed: %v\n%v", strings.Join(curlCmd.Args, " "), err, curlOut.String())
+ }
+}
+
+func rootURL(t *testing.T, binDir, credentials, mt, name string) string {
+ var rootOut bytes.Buffer
+ rootArgs := []string{
+ "-veyron.credentials=" + credentials,
+ "-veyron.namespace.root=" + mt,
+ "url", name,
+ }
+ rootCmd := exec.Command(filepath.Join(binDir, "binary"), rootArgs...)
+ rootCmd.Stdout = &rootOut
+ rootCmd.Stderr = &rootOut
+ if err := rootCmd.Run(); err != nil {
+ t.Fatalf("%q failed: %v\n%v", strings.Join(rootCmd.Args, " "), err, rootOut.String())
+ }
+ return strings.TrimSpace(rootOut.String())
+}
+
+func uploadFile(t *testing.T, binDir, credentials, mt, name, path, suffix string) {
+ var uploadOut bytes.Buffer
+ uploadArgs := []string{
+ "-veyron.credentials=" + credentials,
+ "-veyron.namespace.root=" + mt,
+ "upload", naming.Join(name, suffix), path,
+ }
+ uploadCmd := exec.Command(filepath.Join(binDir, "binary"), uploadArgs...)
+ uploadCmd.Stdout = &uploadOut
+ uploadCmd.Stderr = &uploadOut
+ if err := uploadCmd.Run(); err != nil {
+ t.Fatalf("%q failed: %v\n%v", strings.Join(uploadCmd.Args, " "), err, uploadOut.String())
+ }
+}
+
+func TestHelperProcess(t *testing.T) {
+ modules.DispatchInTest()
+}
+
+func TestBinaryRepositoryIntegration(t *testing.T) {
+ // Build the required binaries.
+ binDir, cleanup, err := integration.BuildPkgs(binPkgs)
+ if err != nil {
+ t.Fatalf("%v", err)
+ }
+ defer cleanup()
+
+ // Start a root mount table.
+ shell, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("NewShell() failed: %v", err)
+ }
+ defer shell.Cleanup(os.Stdin, os.Stderr)
+ handle, mt, err := integration.StartRootMT(shell)
+ if err != nil {
+ t.Fatalf("%v", err)
+ }
+ defer handle.CloseStdin()
+
+ // Generate credentials.
+ principal := security.NewPrincipal("root")
+ credentials := security.NewVeyronCredentials(principal, "test-credentials")
+ defer os.RemoveAll(credentials)
+
+ // Start the build server.
+ binaryRepoBin := filepath.Join(binDir, "binaryd")
+ binaryRepoName := "test-binary-repository"
+ args := []string{
+ "-name=" + binaryRepoName,
+ "-http=127.0.0.1:0",
+ "-veyron.tcp.address=127.0.0.1:0",
+ "-veyron.credentials=" + credentials,
+ "-veyron.namespace.root=" + mt,
+ }
+ serverProcess, err := integration.StartServer(binaryRepoBin, args)
+ if err != nil {
+ t.Fatalf("%v", err)
+ }
+ defer serverProcess.Kill()
+
+ // Upload a random binary file.
+ binFile, err := ioutil.TempFile("", "")
+ if err != nil {
+ t.Fatalf("TempFile() failed: %v", err)
+ }
+ defer binFile.Close()
+ defer os.Remove(binFile.Name())
+ if _, err := binFile.Write(testutil.RandomBytes(16 * 1000 * 1000)); err != nil {
+ t.Fatalf("Write() failed: %v", err)
+ }
+ binSuffix := "test-binary"
+ uploadFile(t, binDir, credentials, mt, binaryRepoName, binFile.Name(), binSuffix)
+
+ // Upload a compressed version of the binary file.
+ tarFile := binFile.Name() + ".tar.gz"
+ var tarOut bytes.Buffer
+ tarCmd := exec.Command("tar", "zcvf", tarFile, binFile.Name())
+ tarCmd.Stdout = &tarOut
+ tarCmd.Stderr = &tarOut
+ if err := tarCmd.Run(); err != nil {
+ t.Fatalf("%q failed: %v\n%v", strings.Join(tarCmd.Args, " "), err, tarOut.String())
+ }
+ defer os.Remove(tarFile)
+ tarSuffix := "test-compressed-file"
+ uploadFile(t, binDir, credentials, mt, binaryRepoName, tarFile, tarSuffix)
+
+ // Download the binary file and check that it matches the
+ // original one and that it has the right file type.
+ downloadedBinFile := binFile.Name() + "-downloaded"
+ defer os.Remove(downloadedBinFile)
+ downloadFile(t, false, binDir, credentials, mt, binaryRepoName, downloadedBinFile, binSuffix)
+ compareFiles(t, binFile.Name(), downloadedBinFile)
+ checkFileType(t, downloadedBinFile, `{"Type":"application/octet-stream","Encoding":""}`)
+
+ // Download the compressed version of the binary file and
+ // check that it matches the original one and that it has the
+ // right file type.
+ downloadedTarFile := binFile.Name() + "-downloaded.tar.gz"
+ defer os.Remove(downloadedTarFile)
+ downloadFile(t, false, binDir, credentials, mt, binaryRepoName, downloadedTarFile, tarSuffix)
+ compareFiles(t, tarFile, downloadedTarFile)
+ checkFileType(t, downloadedTarFile, `{"Type":"application/x-tar","Encoding":"gzip"}`)
+
+ // Fetch the root URL of the HTTP server used by the binary
+ // repository to serve URLs.
+ root := rootURL(t, binDir, credentials, mt, binaryRepoName)
+
+ // Download the binary file using the HTTP protocol and check
+ // that it matches the original one.
+ downloadedBinFileURL := binFile.Name() + "-downloaded-url"
+ defer os.Remove(downloadedBinFileURL)
+ downloadURL(t, downloadedBinFileURL, root, binSuffix)
+ compareFiles(t, downloadedBinFile, downloadedBinFileURL)
+
+ // Download the compressed version of the binary file using
+ // the HTTP protocol and check that it matches the original
+ // one.
+ downloadedTarFileURL := binFile.Name() + "-downloaded-url.tar.gz"
+ defer os.Remove(downloadedTarFileURL)
+ downloadURL(t, downloadedTarFileURL, root, tarSuffix)
+ compareFiles(t, downloadedTarFile, downloadedTarFileURL)
+
+ // Delete the files.
+ deleteFile(t, binDir, credentials, mt, binaryRepoName, binSuffix)
+ deleteFile(t, binDir, credentials, mt, binaryRepoName, tarSuffix)
+
+ // Check the files no longer exist.
+ downloadFile(t, true, binDir, credentials, mt, binaryRepoName, downloadedBinFile, binSuffix)
+ downloadFile(t, true, binDir, credentials, mt, binaryRepoName, downloadedTarFile, tarSuffix)
+}
diff --git a/services/mgmt/binary/impl/fs_utils.go b/services/mgmt/binary/impl/fs_utils.go
index 62eaab8..3240d31 100644
--- a/services/mgmt/binary/impl/fs_utils.go
+++ b/services/mgmt/binary/impl/fs_utils.go
@@ -92,7 +92,7 @@
// createObjectNameTree returns a tree of all the valid object names in the
// repository.
func (i *binaryService) createObjectNameTree() *treeNode {
- pattern := i.state.root
+ pattern := i.state.rootDir
for d := 0; d < i.state.depth; d++ {
pattern = filepath.Join(pattern, "*")
}
diff --git a/services/mgmt/binary/impl/impl_test.go b/services/mgmt/binary/impl/impl_test.go
index 6f603e0..b336cd1 100644
--- a/services/mgmt/binary/impl/impl_test.go
+++ b/services/mgmt/binary/impl/impl_test.go
@@ -105,11 +105,11 @@
// startServer starts the binary repository server.
func startServer(t *testing.T, depth int) (repository.BinaryClientMethods, string, string, func()) {
// Setup the root of the binary repository.
- root, err := ioutil.TempDir("", veyronPrefix)
+ rootDir, err := ioutil.TempDir("", veyronPrefix)
if err != nil {
t.Fatalf("TempDir() failed: %v", err)
}
- path, perm := filepath.Join(root, VersionFile), os.FileMode(0600)
+ path, perm := filepath.Join(rootDir, VersionFile), os.FileMode(0600)
if err := ioutil.WriteFile(path, []byte(Version), perm); err != nil {
vlog.Fatalf("WriteFile(%v, %v, %v) failed: %v", path, Version, perm, err)
}
@@ -118,14 +118,14 @@
if err != nil {
t.Fatalf("NewServer() failed: %v", err)
}
- state, err := NewState(root, depth)
- if err != nil {
- t.Fatalf("NewState(%v, %v) failed: %v", root, depth, err)
- }
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
+ state, err := NewState(rootDir, listener.Addr().String(), depth)
+ if err != nil {
+ t.Fatalf("NewState(%v, %v) failed: %v", rootDir, listener.Addr().String(), depth, err)
+ }
go func() {
if err := http.Serve(listener, http.FileServer(NewHTTPRoot(state))); err != nil {
vlog.Fatalf("Serve() failed: %v", err)
@@ -148,12 +148,10 @@
t.Fatalf("Stop() failed: %v", err)
}
if err := os.RemoveAll(path); err != nil {
- t.Fatalf("Remove(%v) failed: %v", path, err)
+ t.Fatalf("RemoveAll(%v) failed: %v", path, err)
}
- // Check that any directories and files that were created to
- // represent the binary objects have been garbage collected.
- if err := os.RemoveAll(root); err != nil {
- t.Fatalf("Remove(%v) failed: %v", root, err)
+ if err := os.RemoveAll(rootDir); err != nil {
+ t.Fatalf("RemoveAll(%v) failed: %v", rootDir, err)
}
}
}
diff --git a/services/mgmt/binary/impl/service.go b/services/mgmt/binary/impl/service.go
index e88028d..6249891 100644
--- a/services/mgmt/binary/impl/service.go
+++ b/services/mgmt/binary/impl/service.go
@@ -2,10 +2,11 @@
// objects identified by object name suffixes using the local file
// system. Given an object name suffix, the implementation computes an
// MD5 hash of the suffix and generates the following path in the
-// local filesystem: /<root>/<dir_1>/.../<dir_n>/<hash>. The root and
-// the directory depth are parameters of the implementation. The
-// contents of the directory include the checksum and data for each of
-// the individual parts of the binary, and the name of the object:
+// local filesystem: /<root_dir>/<dir_1>/.../<dir_n>/<hash>. The root
+// directory and the directory depth are parameters of the
+// implementation. The contents of the directory include the checksum
+// and data for each of the individual parts of the binary, and the
+// name of the object:
//
// name
// <part_1>/checksum
@@ -156,9 +157,9 @@
}
for {
// Remove the binary and all directories on the path back to the
- // root that are left empty after the binary is removed.
+ // root directory that are left empty after the binary is removed.
path = filepath.Dir(path)
- if i.state.root == path {
+ if i.state.rootDir == path {
break
}
if err := os.Remove(path); err != nil {
@@ -204,10 +205,11 @@
return nil
}
+// TODO(jsimsa): Design and implement an access control mechanism for
+// the URL-based downloads.
func (i *binaryService) DownloadURL(ipc.ServerContext) (string, int64, error) {
vlog.Infof("%v.DownloadURL()", i.suffix)
- // TODO(jsimsa): Implement.
- return "", 0, nil
+ return i.state.rootURL + "/" + i.suffix, 0, nil
}
func (i *binaryService) Stat(ipc.ServerContext) ([]binary.PartInfo, repository.MediaInfo, error) {
diff --git a/services/mgmt/binary/impl/setup.go b/services/mgmt/binary/impl/setup.go
index 12e43fa..2f96bf5 100644
--- a/services/mgmt/binary/impl/setup.go
+++ b/services/mgmt/binary/impl/setup.go
@@ -10,9 +10,9 @@
const defaultRootPrefix = "veyron_binary_repository"
-// SetupRoot sets up the root directory if it doesn't already exist. If an
+// SetupRootDir sets up the root directory if it doesn't already exist. If an
// empty string is used as root, create a new temporary directory.
-func SetupRoot(root string) (string, error) {
+func SetupRootDir(root string) (string, error) {
if root == "" {
var err error
if root, err = ioutil.TempDir("", defaultRootPrefix); err != nil {
diff --git a/services/mgmt/binary/impl/state.go b/services/mgmt/binary/impl/state.go
index cf6c3b1..757d3a6 100644
--- a/services/mgmt/binary/impl/state.go
+++ b/services/mgmt/binary/impl/state.go
@@ -29,21 +29,24 @@
// before its performance degrades allows the binary repository to
// store 16B objects.
depth int
- // root identifies the local filesystem directory in which the
+ // rootDir identifies the local filesystem directory in which the
// binary repository stores its objects.
- root string
+ rootDir string
+ // rootURL identifies the root URL of the HTTP server serving
+ // the download URLs.
+ rootURL string
}
// NewState creates a new state object for the binary service. This
// should be passed into both NewDispatcher and NewHTTPRoot.
-func NewState(root string, depth int) (*state, error) {
+func NewState(rootDir, rootURL string, depth int) (*state, error) {
if min, max := 0, md5.Size-1; min > depth || depth > max {
return nil, fmt.Errorf("Unexpected depth, expected a value between %v and %v, got %v", min, max, depth)
}
- if _, err := os.Stat(root); err != nil {
- return nil, fmt.Errorf("Stat(%v) failed: %v", root, err)
+ if _, err := os.Stat(rootDir); err != nil {
+ return nil, fmt.Errorf("Stat(%v) failed: %v", rootDir, err)
}
- path := filepath.Join(root, VersionFile)
+ path := filepath.Join(rootDir, VersionFile)
output, err := ioutil.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("ReadFile(%v) failed: %v", path, err)
@@ -52,8 +55,9 @@
return nil, fmt.Errorf("Unexpected version: expected %v, got %v", expected, got)
}
return &state{
- depth: depth,
- root: root,
+ depth: depth,
+ rootDir: rootDir,
+ rootURL: rootURL,
}, nil
}
@@ -66,5 +70,5 @@
for j := 0; j < s.depth; j++ {
dir = filepath.Join(dir, hash[j*2:(j+1)*2])
}
- return filepath.Join(s.root, dir, hash)
+ return filepath.Join(s.rootDir, dir, hash)
}
diff --git a/services/mgmt/build/buildd/test.sh b/services/mgmt/build/buildd/test.sh
deleted file mode 100755
index 4df2905..0000000
--- a/services/mgmt/build/buildd/test.sh
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/bin/bash
-
-# Test the build server daemon.
-#
-# This test starts a build server daemon and uses the build client to
-# verify that <build>.Build() works as expected.
-
-source "${VEYRON_ROOT}/scripts/lib/shell_test.sh"
-
-readonly WORKDIR="${shell_test_WORK_DIR}"
-
-build() {
- BUILDD_BIN="$(shell_test::build_go_binary 'veyron.io/veyron/veyron/services/mgmt/build/buildd')"
- BUILD_BIN="$(shell_test::build_go_binary 'veyron.io/veyron/veyron/tools/build')"
-}
-
-main() {
- cd "${WORKDIR}"
- build
-
- shell_test::setup_server_test
-
- # Start the binary repository daemon.
- local -r SERVER="buildd-test-server"
- local GO_BIN=$(which go)
- local -r GO_ROOT=$("${GO_BIN}" env GOROOT)
- shell_test::start_server "${BUILDD_BIN}" --name="${SERVER}" --gobin="${GO_BIN}" --goroot="${GO_ROOT}" --veyron.tcp.address=127.0.0.1:0 \
- || shell_test::fail "line ${LINENO} failed to start server"
-
- # Create and build a test source file.
- local -r GO_PATH=$(shell::tmp_dir)
- local -r BIN_DIR="${GO_PATH}/bin"
- mkdir -p "${BIN_DIR}"
- local -r SRC_DIR="${GO_PATH}/src/test"
- mkdir -p "${SRC_DIR}"
- local -r SRC_FILE="${SRC_DIR}/test.go"
- cat > "${SRC_FILE}" <<EOF
-package main
-
-import "fmt"
-
-func main() {
- fmt.Printf("Hello World!\n")
-}
-EOF
- GOPATH="${GO_PATH}" GOROOT="${GO_ROOT}" TMPDIR="${BIN_DIR}" "${BUILD_BIN}" build "${SERVER}" "test" || shell_test::fail "line ${LINENO}: 'build' failed"
- if [[ ! -e "${BIN_DIR}/test" ]]; then
- shell_test::fail "test binary not found"
- fi
- local -r GOT=$("${BIN_DIR}/test")
- local -r WANT="Hello World!"
- shell_test::assert_eq "${GOT}" "${WANT}" "${LINENO}"
-
- shell_test::pass
-}
-
-main "$@"
diff --git a/services/mgmt/build/buildd/testdata/integration_test.go b/services/mgmt/build/buildd/testdata/integration_test.go
index 630a22f..cbcb948 100644
--- a/services/mgmt/build/buildd/testdata/integration_test.go
+++ b/services/mgmt/build/buildd/testdata/integration_test.go
@@ -47,7 +47,7 @@
modules.DispatchInTest()
}
-func TestBuild(t *testing.T) {
+func TestBuildServerIntegration(t *testing.T) {
// Build the required binaries.
binDir, cleanup, err := integration.BuildPkgs(binPkgs)
if err != nil {
diff --git a/services/mgmt/lib/binary/impl.go b/services/mgmt/lib/binary/impl.go
index b68fe7f..2688fbd 100644
--- a/services/mgmt/lib/binary/impl.go
+++ b/services/mgmt/lib/binary/impl.go
@@ -176,6 +176,17 @@
return nil
}
+func DownloadURL(ctx context.T, von string) (string, int64, error) {
+ ctx, cancel := ctx.WithTimeout(time.Minute)
+ defer cancel()
+ url, ttl, err := repository.BinaryClient(von).DownloadURL(ctx)
+ if err != nil {
+ vlog.Errorf("DownloadURL() failed: %v", err)
+ return "", 0, err
+ }
+ return url, ttl, nil
+}
+
func upload(ctx context.T, r io.ReadSeeker, mediaInfo repository.MediaInfo, von string) error {
client := repository.BinaryClient(von)
offset, whence := int64(0), 2
diff --git a/services/mgmt/lib/binary/impl_test.go b/services/mgmt/lib/binary/impl_test.go
index ca0a76f..216cfd1 100644
--- a/services/mgmt/lib/binary/impl_test.go
+++ b/services/mgmt/lib/binary/impl_test.go
@@ -37,11 +37,11 @@
func setupRepository(t *testing.T) (string, func()) {
// Setup the root of the binary repository.
- root, err := ioutil.TempDir("", veyronPrefix)
+ rootDir, err := ioutil.TempDir("", veyronPrefix)
if err != nil {
t.Fatalf("TempDir() failed: %v", err)
}
- path, perm := filepath.Join(root, impl.VersionFile), os.FileMode(0600)
+ path, perm := filepath.Join(rootDir, impl.VersionFile), os.FileMode(0600)
if err := ioutil.WriteFile(path, []byte(impl.Version), perm); err != nil {
vlog.Fatalf("WriteFile(%v, %v, %v) failed: %v", path, impl.Version, perm, err)
}
@@ -51,9 +51,9 @@
t.Fatalf("NewServer() failed: %v", err)
}
depth := 2
- state, err := impl.NewState(root, depth)
+ state, err := impl.NewState(rootDir, "http://test-root-url", depth)
if err != nil {
- t.Fatalf("NewState(%v, %v) failed: %v", root, depth, err)
+ t.Fatalf("NewState(%v, %v) failed: %v", rootDir, depth, err)
}
dispatcher := impl.NewDispatcher(state, nil)
endpoint, err := server.Listen(profiles.LocalListenSpec)
@@ -71,8 +71,8 @@
}
// Check that any directories and files that were created to
// represent the binary objects have been garbage collected.
- if err := os.Remove(root); err != nil {
- t.Fatalf("Remove(%v) failed: %v", root, err)
+ if err := os.RemoveAll(rootDir); err != nil {
+ t.Fatalf("Remove(%v) failed: %v", rootDir, err)
}
// Shutdown the binary repository server.
if err := server.Stop(); err != nil {
@@ -160,3 +160,17 @@
t.Errorf("Delete(%v) failed: %v", von, err)
}
}
+
+// TestDownloadURL tests the binary repository client-side library
+// DownloadURL method.
+func TestDownloadURL(t *testing.T) {
+ von, cleanup := setupRepository(t)
+ defer cleanup()
+ url, _, err := DownloadURL(runtime.NewContext(), von)
+ if err != nil {
+ t.Fatalf("DownloadURL(%v) failed: %v", von, err)
+ }
+ if got, want := url, "http://test-root-url/test"; got != want {
+ t.Fatalf("unexpect output: got %v, want %v", got, want)
+ }
+}
diff --git a/services/mgmt/node/impl/impl_test.go b/services/mgmt/node/impl/impl_test.go
index f5dd9e2..eefe69e 100644
--- a/services/mgmt/node/impl/impl_test.go
+++ b/services/mgmt/node/impl/impl_test.go
@@ -745,11 +745,11 @@
}
func startRealBinaryRepository(t *testing.T) func() {
- root, err := binaryimpl.SetupRoot("")
+ rootDir, err := binaryimpl.SetupRootDir("")
if err != nil {
- t.Fatalf("binaryimpl.SetupRoot failed: %v", err)
+ t.Fatalf("binaryimpl.SetupRootDir failed: %v", err)
}
- state, err := binaryimpl.NewState(root, 3)
+ state, err := binaryimpl.NewState(rootDir, "", 3)
if err != nil {
t.Fatalf("binaryimpl.NewState failed: %v", err)
}
@@ -774,8 +774,8 @@
if err := server.Stop(); err != nil {
t.Fatalf("server.Stop failed: %v", err)
}
- if err := os.RemoveAll(root); err != nil {
- t.Fatalf("os.RemoveAll(%q) failed: %v", root, err)
+ if err := os.RemoveAll(rootDir); err != nil {
+ t.Fatalf("os.RemoveAll(%q) failed: %v", rootDir, err)
}
}
}
diff --git a/services/mgmt/profile/profiled/test.sh b/services/mgmt/profile/profiled/test.sh
deleted file mode 100755
index 7022e92..0000000
--- a/services/mgmt/profile/profiled/test.sh
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/bin/bash
-
-# Test the profile repository daemon.
-#
-# This test starts an profile repository daemon and uses the profile
-# repository client to verify that <profile>.Put(), <profile>.Label(),
-# <profile>.Description(), <profile>.Speficiation(), and
-# <profile>.Remove() work as expected.
-
-source "${VEYRON_ROOT}/scripts/lib/shell_test.sh"
-
-readonly WORKDIR="${shell_test_WORK_DIR}"
-
-build() {
- PROFILED_BIN="$(shell_test::build_go_binary 'veyron.io/veyron/veyron/services/mgmt/profile/profiled')"
- PROFILE_BIN="$(shell_test::build_go_binary 'veyron.io/veyron/veyron/tools/profile')"
-}
-
-main() {
- local GOT OUTPUT RESULT WANT
-
- cd "${WORKDIR}"
- build
-
- shell_test::setup_server_test
-
- # Start the profile repository daemon.
- local -r REPO="profiled-test-repo"
- local -r STORE=$(shell::tmp_dir)
- shell_test::start_server "${PROFILED_BIN}" --name="${REPO}" --veyron.tcp.address=127.0.0.1:0 --store="${STORE}" \
- || shell_test::fail "line ${LINENO} failed to start server"
-
- # Create a profile.
- local -r PROFILE="${REPO}/test-profile"
- "${PROFILE_BIN}" put "${PROFILE}" || shell_test::fail "line ${LINENO}: 'put' failed"
-
- # Retrieve the profile label.
- OUTPUT=$(shell::tmp_file)
- "${PROFILE_BIN}" label "${PROFILE}" | tee "${OUTPUT}" || shell_test::fail "line ${LINENO}: 'label' failed"
- GOT=$(cat "${OUTPUT}")
- WANT="example"
- shell_test::assert_eq "${GOT}" "${WANT}" "${LINENO}"
-
- # Retrieve the profile description.
- OUTPUT=$(shell::tmp_file)
- "${PROFILE_BIN}" description "${PROFILE}" | tee "${OUTPUT}" || shell_test::fail "line ${LINENO}: 'description' failed"
- GOT=$(cat "${OUTPUT}")
- WANT="Example profile to test the profile manager implementation."
- shell_test::assert_eq "${GOT}" "${WANT}" "${LINENO}"
-
- # Retrieve the profile specification.
- OUTPUT=$(shell::tmp_file)
- "${PROFILE_BIN}" spec "${PROFILE}" | tee "${OUTPUT}" || shell_test::fail "line ${LINENO}: 'spec' failed"
- GOT=$(cat "${OUTPUT}")
- WANT='profile.Specification{Arch:"amd64", Description:"Example profile to test the profile manager implementation.", Format:"ELF", Libraries:map[profile.Library]struct {}{profile.Library{Name:"foo", MajorVersion:"1", MinorVersion:"0"}:struct {}{}}, Label:"example", OS:"linux"}'
- shell_test::assert_eq "${GOT}" "${WANT}" "${LINENO}"
-
- # Remove the profile.
- "${PROFILE_BIN}" remove "${PROFILE}" || shell_test::fail "line ${LINENO}: 'remove' failed"
-
- # Check the profile no longer exists.
- RESULT=$(shell::check_result "${PROFILE_BIN}" label "${PROFILE}")
- shell_test::assert_ne "${RESULT}" "0" "${LINENO}"
- RESULT=$(shell::check_result "${PROFILE_BIN}" description "${PROFILE}")
- shell_test::assert_ne "${RESULT}" "0" "${LINENO}"
- RESULT=$(shell::check_result "${PROFILE_BIN}" spec "${PROFILE}")
- shell_test::assert_ne "${RESULT}" "0" "${LINENO}"
-
- shell_test::pass
-}
-
-main "$@"
diff --git a/services/mgmt/profile/profiled/testdata/integration_test.go b/services/mgmt/profile/profiled/testdata/integration_test.go
new file mode 100644
index 0000000..673f07b
--- /dev/null
+++ b/services/mgmt/profile/profiled/testdata/integration_test.go
@@ -0,0 +1,155 @@
+package integration_test
+
+import (
+ "bytes"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "veyron.io/veyron/veyron/lib/modules"
+ "veyron.io/veyron/veyron/lib/testutil/integration"
+ "veyron.io/veyron/veyron/lib/testutil/security"
+ _ "veyron.io/veyron/veyron/profiles"
+ "veyron.io/veyron/veyron2/naming"
+)
+
+var binPkgs = []string{
+ "veyron.io/veyron/veyron/services/mgmt/profile/profiled",
+ "veyron.io/veyron/veyron/tools/profile",
+}
+
+func profileCommandOutput(t *testing.T, expectError bool, command, binDir, credentials, mt, name, suffix string) string {
+ var labelOut bytes.Buffer
+ labelArgs := []string{
+ "-veyron.credentials=" + credentials,
+ "-veyron.namespace.root=" + mt,
+ command, naming.Join(name, suffix),
+ }
+ labelCmd := exec.Command(filepath.Join(binDir, "profile"), labelArgs...)
+ labelCmd.Stdout = &labelOut
+ labelCmd.Stderr = &labelOut
+ err := labelCmd.Run()
+ if err != nil && !expectError {
+ t.Fatalf("%q failed: %v\n%v", strings.Join(labelCmd.Args, " "), err, labelOut.String())
+ }
+ if err == nil && expectError {
+ t.Fatalf("%q did not fail when it should", strings.Join(labelCmd.Args, " "))
+ }
+ return strings.TrimSpace(labelOut.String())
+}
+
+func putProfile(t *testing.T, binDir, credentials, mt, name, suffix string) {
+ var putOut bytes.Buffer
+ putArgs := []string{
+ "-veyron.credentials=" + credentials,
+ "-veyron.namespace.root=" + mt,
+ "put", naming.Join(name, suffix),
+ }
+ putCmd := exec.Command(filepath.Join(binDir, "profile"), putArgs...)
+ putCmd.Stdout = &putOut
+ putCmd.Stderr = &putOut
+ if err := putCmd.Run(); err != nil {
+ t.Fatalf("%q failed: %v\n%v", strings.Join(putCmd.Args, " "), err, putOut.String())
+ }
+}
+
+func removeProfile(t *testing.T, binDir, credentials, mt, name, suffix string) {
+ var removeOut bytes.Buffer
+ removeArgs := []string{
+ "-veyron.credentials=" + credentials,
+ "-veyron.namespace.root=" + mt,
+ "remove", naming.Join(name, suffix),
+ }
+ removeCmd := exec.Command(filepath.Join(binDir, "profile"), removeArgs...)
+ removeCmd.Stdout = &removeOut
+ removeCmd.Stderr = &removeOut
+ if err := removeCmd.Run(); err != nil {
+ t.Fatalf("%q failed: %v\n%v", strings.Join(removeCmd.Args, " "), err, removeOut.String())
+ }
+}
+
+func TestHelperProcess(t *testing.T) {
+ modules.DispatchInTest()
+}
+
+func TestProfileRepository(t *testing.T) {
+ // Build the required binaries.
+ binDir, cleanup, err := integration.BuildPkgs(binPkgs)
+ if err != nil {
+ t.Fatalf("%v", err)
+ }
+ defer cleanup()
+
+ // Start a root mount table.
+ shell, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("NewShell() failed: %v", err)
+ }
+ defer shell.Cleanup(os.Stdin, os.Stderr)
+ handle, mt, err := integration.StartRootMT(shell)
+ if err != nil {
+ t.Fatalf("%v", err)
+ }
+ defer handle.CloseStdin()
+
+ // Generate credentials.
+ root := security.NewPrincipal("root")
+ credentials := security.NewVeyronCredentials(root, "test-credentials")
+ defer os.RemoveAll(credentials)
+
+ // Start the profile repository.
+ profileRepoBin := filepath.Join(binDir, "profiled")
+ profileRepoName := "test-profile-repo"
+ profileRepoStore, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatalf("TempDir() failed: %v", err)
+ }
+ defer os.RemoveAll(profileRepoStore)
+ args := []string{
+ "-name=" + profileRepoName, "-store=" + profileRepoStore,
+ "-veyron.tcp.address=127.0.0.1:0",
+ "-veyron.credentials=" + credentials,
+ "-veyron.namespace.root=" + mt,
+ }
+ serverProcess, err := integration.StartServer(profileRepoBin, args)
+ if err != nil {
+ t.Fatalf("%v", err)
+ }
+ defer serverProcess.Kill()
+
+ // Create a profile.
+ const profile = "test-profile"
+ putProfile(t, binDir, credentials, mt, profileRepoName, profile)
+
+ // Retrieve the profile label and check it matches the
+ // expected label.
+ profileLabel := profileCommandOutput(t, false, "label", binDir, credentials, mt, profileRepoName, profile)
+ if got, want := profileLabel, "example"; got != want {
+ t.Fatalf("unexpected output: got %v, want %v", got, want)
+ }
+
+ // Retrieve the profile description and check it matches the
+ // expected description.
+ profileDesc := profileCommandOutput(t, false, "description", binDir, credentials, mt, profileRepoName, profile)
+ if got, want := profileDesc, "Example profile to test the profile manager implementation."; got != want {
+ t.Fatalf("unexpected output: got %v, want %v", got, want)
+ }
+
+ // Retrieve the profile specification and check it matches the
+ // expected specification.
+ profileSpec := profileCommandOutput(t, false, "specification", binDir, credentials, mt, profileRepoName, profile)
+ if got, want := profileSpec, `profile.Specification{Arch:"amd64", Description:"Example profile to test the profile manager implementation.", Format:"ELF", Libraries:map[profile.Library]struct {}{profile.Library{Name:"foo", MajorVersion:"1", MinorVersion:"0"}:struct {}{}}, Label:"example", OS:"linux"}`; got != want {
+ t.Fatalf("unexpected output: got %v, want %v", got, want)
+ }
+
+ // Remove the profile.
+ removeProfile(t, binDir, credentials, mt, profileRepoName, profile)
+
+ // Check that the profile no longer exists.
+ profileCommandOutput(t, true, "label", binDir, credentials, mt, profileRepoName, profile)
+ profileCommandOutput(t, true, "description", binDir, credentials, mt, profileRepoName, profile)
+ profileCommandOutput(t, true, "specification", binDir, credentials, mt, profileRepoName, profile)
+}
diff --git a/tools/binary/impl.go b/tools/binary/impl.go
index df34d6c..54d6d25 100644
--- a/tools/binary/impl.go
+++ b/tools/binary/impl.go
@@ -10,7 +10,7 @@
var cmdDelete = &cmdline.Command{
Run: runDelete,
Name: "delete",
- Short: "Delete binary",
+ Short: "Delete a binary",
Long: "Delete connects to the binary repository and deletes the specified binary",
ArgsName: "<von>",
ArgsLong: "<von> is the veyron object name of the binary to delete",
@@ -31,7 +31,7 @@
var cmdDownload = &cmdline.Command{
Run: runDownload,
Name: "download",
- Short: "Download binary",
+ Short: "Download a binary",
Long: `
Download connects to the binary repository, downloads the specified binary, and
writes it to a file.
@@ -58,7 +58,7 @@
var cmdUpload = &cmdline.Command{
Run: runUpload,
Name: "upload",
- Short: "Upload binary",
+ Short: "Upload a binary",
Long: `
Upload connects to the binary repository and uploads the binary of the specified
file. When successful, it writes the name of the new binary to stdout.
@@ -83,6 +83,28 @@
return nil
}
+var cmdURL = &cmdline.Command{
+ Run: runURL,
+ Name: "url",
+ Short: "Fetch a download URL",
+ Long: "Connect to the binary repository and fetch the download URL for the given veyron object name.",
+ ArgsName: "<von>",
+ ArgsLong: "<von> is the veyron object name of the binary repository",
+}
+
+func runURL(cmd *cmdline.Command, args []string) error {
+ if expected, got := 1, len(args); expected != got {
+ return cmd.UsageErrorf("rooturl: incorrect number of arguments, expected %d, got %d", expected, got)
+ }
+ von := args[0]
+ url, _, err := binary.DownloadURL(runtime.NewContext(), von)
+ if err != nil {
+ return err
+ }
+ fmt.Fprintf(cmd.Stdout(), "%v\n", url)
+ return nil
+}
+
func root() *cmdline.Command {
return &cmdline.Command{
Name: "binary",
@@ -90,6 +112,6 @@
Long: `
The binary tool facilitates interaction with the veyron binary repository.
`,
- Children: []*cmdline.Command{cmdDelete, cmdDownload, cmdUpload},
+ Children: []*cmdline.Command{cmdDelete, cmdDownload, cmdUpload, cmdURL},
}
}
diff --git a/tools/binary/impl_test.go b/tools/binary/impl_test.go
index 180b53c..004a235 100644
--- a/tools/binary/impl_test.go
+++ b/tools/binary/impl_test.go
@@ -50,7 +50,10 @@
func (s *server) DownloadURL(ipc.ServerContext) (string, int64, error) {
vlog.Infof("DownloadURL() was called. suffix=%v", s.suffix)
- return "", 0, nil
+ if s.suffix != "" {
+ return "", 0, fmt.Errorf("non-empty suffix: %v", s.suffix)
+ }
+ return "test-download-url", 0, nil
}
func (s *server) Stat(ipc.ServerContext) ([]binary.PartInfo, repository.MediaInfo, error) {
@@ -119,19 +122,20 @@
return
}
defer stopServer(t, server)
+
// Setup the command-line.
cmd := root()
- var stdout, stderr bytes.Buffer
- cmd.Init(nil, &stdout, &stderr)
+ var out bytes.Buffer
+ cmd.Init(nil, &out, &out)
// Test the 'delete' command.
if err := cmd.Execute([]string{"delete", naming.JoinAddressName(endpoint.String(), "exists")}); err != nil {
- t.Fatalf("%v", err)
+ t.Fatalf("%v failed: %v\n%v", "delete", err, out.String())
}
- if expected, got := "Binary deleted successfully", strings.TrimSpace(stdout.String()); got != expected {
+ if expected, got := "Binary deleted successfully", strings.TrimSpace(out.String()); got != expected {
t.Errorf("Got %q, expected %q", got, expected)
}
- stdout.Reset()
+ out.Reset()
// Test the 'download' command.
dir, err := ioutil.TempDir("", "binaryimpltest")
@@ -142,9 +146,9 @@
file := path.Join(dir, "testfile")
defer os.Remove(file)
if err := cmd.Execute([]string{"download", naming.JoinAddressName(endpoint.String(), "exists"), file}); err != nil {
- t.Fatalf("%v", err)
+ t.Fatalf("%v failed: %v\n%v", "download", err, out.String())
}
- if expected, got := "Binary downloaded to file "+file, strings.TrimSpace(stdout.String()); got != expected {
+ if expected, got := "Binary downloaded to file "+file, strings.TrimSpace(out.String()); got != expected {
t.Errorf("Got %q, expected %q", got, expected)
}
buf, err := ioutil.ReadFile(file)
@@ -154,10 +158,19 @@
if expected := "HelloWorld"; string(buf) != expected {
t.Errorf("Got %q, expected %q", string(buf), expected)
}
- stdout.Reset()
+ out.Reset()
// Test the 'upload' command.
if err := cmd.Execute([]string{"upload", naming.JoinAddressName(endpoint.String(), "exists"), file}); err != nil {
- t.Fatalf("%v", err)
+ t.Fatalf("%v failed: %v\n%v", "upload", err, out.String())
+ }
+ out.Reset()
+
+ // Test the 'url' command.
+ if err := cmd.Execute([]string{"url", naming.JoinAddressName(endpoint.String(), "")}); err != nil {
+ t.Fatalf("%v failed: %v\n%v", "url", err, out.String())
+ }
+ if expected, got := "test-download-url", strings.TrimSpace(out.String()); got != expected {
+ t.Errorf("Got %q, expected %q", got, expected)
}
}
diff --git a/tools/build/impl_test.go b/tools/build/impl_test.go
index 4f4d3f7..59f832d 100644
--- a/tools/build/impl_test.go
+++ b/tools/build/impl_test.go
@@ -11,14 +11,12 @@
"veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/services/mgmt/binary"
"veyron.io/veyron/veyron2/services/mgmt/build"
- "veyron.io/veyron/veyron2/verror"
+ verror "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron2/vlog"
"veyron.io/veyron/veyron/profiles"
)
-var errInternalError = verror.Internalf("internal error")
-
type mock struct{}
func (mock) Build(ctx build.BuilderBuildContext, arch build.Architecture, opsys build.OperatingSystem) ([]byte, error) {
@@ -28,7 +26,7 @@
}
if err := iterator.Err(); err != nil {
vlog.Errorf("Advance() failed: %v", err)
- return nil, errInternalError
+ return nil, verror.Make(verror.Internal, ctx)
}
return nil, nil
}
diff --git a/tools/mgmt/test.sh b/tools/mgmt/test.sh
index bd4c59a..f253152 100755
--- a/tools/mgmt/test.sh
+++ b/tools/mgmt/test.sh
@@ -89,7 +89,7 @@
# Start a binary server.
local -r BINARYD_NAME="binaryd"
shell_test::start_server "${BINARYD_BIN}" --name="${BINARYD_NAME}" \
- --root="$(shell::tmp_dir)/binstore" --veyron.tcp.address=127.0.0.1:0 --http=127.0.0.1:0 \
+ --root_dir="$(shell::tmp_dir)/binstore" --veyron.tcp.address=127.0.0.1:0 --http=127.0.0.1:0 \
|| shell_test::fail "line ${LINENO} failed to start binaryd"
# Upload a binary to the binary server. The binary we upload is binaryd
@@ -110,7 +110,7 @@
# Upload an envelope for our test app.
local -r SAMPLE_APP_NAME="${APPLICATIOND_NAME}/testapp/v0"
local -r APP_PUBLISH_NAME="testbinaryd"
- echo "{\"Title\":\"BINARYD\", \"Args\":[\"--name=${APP_PUBLISH_NAME}\", \"--root=./binstore\", \"--veyron.tcp.address=127.0.0.1:0\"], \"Binary\":\"${SAMPLE_APP_BIN_NAME}\", \"Env\":[]}" > ./app.envelope && \
+ echo "{\"Title\":\"BINARYD\", \"Args\":[\"--name=${APP_PUBLISH_NAME}\", \"--root_dir=./binstore\", \"--veyron.tcp.address=127.0.0.1:0\"], \"Binary\":\"${SAMPLE_APP_BIN_NAME}\", \"Env\":[]}" > ./app.envelope && \
"${APPLICATION_BIN}" put "${SAMPLE_APP_NAME}" test ./app.envelope && rm ./app.envelope
# Verify that the envelope we uploaded shows up with glob.
diff --git a/tools/profile/impl.go b/tools/profile/impl.go
index 8869787..ce7924d 100644
--- a/tools/profile/impl.go
+++ b/tools/profile/impl.go
@@ -62,7 +62,7 @@
var cmdSpecification = &cmdline.Command{
Run: runSpecification,
- Name: "spec",
+ Name: "specification",
Short: "Shows the specification of the profile.",
Long: "Shows the specification of the profile.",
ArgsName: "<profile>",
@@ -71,7 +71,7 @@
func runSpecification(cmd *cmdline.Command, args []string) error {
if expected, got := 1, len(args); expected != got {
- return cmd.UsageErrorf("spec: incorrect number of arguments, expected %d, got %d", expected, got)
+ return cmd.UsageErrorf("specification: incorrect number of arguments, expected %d, got %d", expected, got)
}
name := args[0]
p := repository.ProfileClient(name)
diff --git a/tools/profile/impl_test.go b/tools/profile/impl_test.go
index b442271..9569760 100644
--- a/tools/profile/impl_test.go
+++ b/tools/profile/impl_test.go
@@ -145,7 +145,7 @@
stdout.Reset()
// Test the 'spec' command.
- if err := cmd.Execute([]string{"spec", exists}); err != nil {
+ if err := cmd.Execute([]string{"specification", exists}); err != nil {
t.Fatalf("%v", err)
}
if expected, got := fmt.Sprintf("%#v", spec), strings.TrimSpace(stdout.String()); got != expected {