[ARVADOS] created: 1.1.3-264-g7633b94
Git user
git at public.curoverse.com
Tue Mar 20 16:58:04 EDT 2018
at 7633b9438d0c75693eb167c146b26764dd7161ed (commit)
commit 7633b9438d0c75693eb167c146b26764dd7161ed
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Tue Mar 20 16:56:16 2018 -0400
13111: Add /users virtual dir to siteFS.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/lib/mount/fs.go b/lib/mount/fs.go
index d269779..1633296 100644
--- a/lib/mount/fs.go
+++ b/lib/mount/fs.go
@@ -33,7 +33,7 @@ type keepFS struct {
Uid int
Gid int
- root arvados.FileSystem
+ root arvados.CustomFileSystem
open map[uint64]*sharedFile
lastFH uint64
sync.Mutex
@@ -70,6 +70,7 @@ func (fs *keepFS) lookupFH(fh uint64) *sharedFile {
func (fs *keepFS) Init() {
defer fs.debugPanics()
fs.root = fs.Client.SiteFileSystem(fs.KeepClient)
+ fs.root.MountProject("home", "")
if fs.ready != nil {
close(fs.ready)
}
diff --git a/sdk/go/arvados/fs_project.go b/sdk/go/arvados/fs_project.go
index 776c8a3..7f34a42 100644
--- a/sdk/go/arvados/fs_project.go
+++ b/sdk/go/arvados/fs_project.go
@@ -10,25 +10,31 @@ import (
"time"
)
+type staleChecker struct {
+ mtx sync.Mutex
+ last time.Time
+}
+
+func (sc *staleChecker) DoIfStale(fn func(), staleFunc func(time.Time) bool) {
+ sc.mtx.Lock()
+ defer sc.mtx.Unlock()
+ if !staleFunc(sc.last) {
+ return
+ }
+ sc.last = time.Now()
+ fn()
+}
+
// projectnode exposes an Arvados project as a filesystem directory.
type projectnode struct {
inode
+ staleChecker
uuid string
err error
-
- loadLock sync.Mutex
- loadStart time.Time
}
func (pn *projectnode) load() {
- fs := pn.FS().(*siteFileSystem)
-
- pn.loadLock.Lock()
- defer pn.loadLock.Unlock()
- if !fs.Stale(pn.loadStart) {
- return
- }
- pn.loadStart = time.Now()
+ fs := pn.FS().(*customFileSystem)
if pn.uuid == "" {
var resp User
@@ -89,7 +95,7 @@ func (pn *projectnode) load() {
}
func (pn *projectnode) Readdir() ([]os.FileInfo, error) {
- pn.load()
+ pn.staleChecker.DoIfStale(pn.load, pn.FS().(*customFileSystem).Stale)
if pn.err != nil {
return nil, pn.err
}
@@ -97,7 +103,7 @@ func (pn *projectnode) Readdir() ([]os.FileInfo, error) {
}
func (pn *projectnode) Child(name string, replace func(inode) (inode, error)) (inode, error) {
- pn.load()
+ pn.staleChecker.DoIfStale(pn.load, pn.FS().(*customFileSystem).Stale)
if pn.err != nil {
return nil, pn.err
}
diff --git a/sdk/go/arvados/fs_site.go b/sdk/go/arvados/fs_site.go
index cdcf40e..b52a83e 100644
--- a/sdk/go/arvados/fs_site.go
+++ b/sdk/go/arvados/fs_site.go
@@ -10,22 +10,23 @@ import (
"time"
)
-type siteFileSystem struct {
+type CustomFileSystem interface {
+ FileSystem
+ MountByID(mount string)
+ MountProject(mount, uuid string)
+ MountUsers(mount string)
+}
+
+type customFileSystem struct {
fileSystem
staleThreshold time.Time
staleLock sync.Mutex
}
-// SiteFileSystem returns a FileSystem that maps collections and other
-// Arvados objects onto a filesystem layout.
-//
-// This is experimental: the filesystem layout is not stable, and
-// there are significant known bugs and shortcomings. For example,
-// writes are not persisted until Sync() is called.
-func (c *Client) SiteFileSystem(kc keepClient) FileSystem {
+func (c *Client) CustomFileSystem(kc keepClient) CustomFileSystem {
root := &vdirnode{}
- fs := &siteFileSystem{
+ fs := &customFileSystem{
fileSystem: fileSystem{
fsBackend: keepBackend{apiClient: c, keepClient: kc},
root: root,
@@ -41,14 +42,18 @@ func (c *Client) SiteFileSystem(kc keepClient) FileSystem {
},
inodes: make(map[string]inode),
}
- root.inode.Child("by_id", func(inode) (inode, error) {
+ return fs
+}
+
+func (fs *customFileSystem) MountByID(mount string) {
+ fs.root.Child(mount, func(inode) (inode, error) {
return &vdirnode{
inode: &treenode{
fs: fs,
parent: fs.root,
inodes: make(map[string]inode),
fileinfo: fileinfo{
- name: "by_id",
+ name: mount,
modTime: time.Now(),
mode: 0755 | os.ModeDir,
},
@@ -56,13 +61,45 @@ func (c *Client) SiteFileSystem(kc keepClient) FileSystem {
create: fs.mountCollection,
}, nil
})
- root.inode.Child("home", func(inode) (inode, error) {
- return fs.newProjectNode(fs.root, "home", ""), nil
+}
+
+func (fs *customFileSystem) MountProject(mount, uuid string) {
+ fs.root.Child(mount, func(inode) (inode, error) {
+ return fs.newProjectNode(fs.root, mount, uuid), nil
})
+}
+
+func (fs *customFileSystem) MountUsers(mount string) {
+ fs.root.Child(mount, func(inode) (inode, error) {
+ return &usersnode{
+ inode: &treenode{
+ fs: fs,
+ parent: fs.root,
+ inodes: make(map[string]inode),
+ fileinfo: fileinfo{
+ name: mount,
+ modTime: time.Now(),
+ mode: 0755 | os.ModeDir,
+ },
+ },
+ }, nil
+ })
+}
+
+// SiteFileSystem returns a FileSystem that maps collections and other
+// Arvados objects onto a filesystem layout.
+//
+// This is experimental: the filesystem layout is not stable, and
+// there are significant known bugs and shortcomings. For example,
+// writes are not persisted until Sync() is called.
+func (c *Client) SiteFileSystem(kc keepClient) CustomFileSystem {
+ fs := c.CustomFileSystem(kc)
+ fs.MountByID("by_id")
+ fs.MountUsers("users")
return fs
}
-func (fs *siteFileSystem) Sync() error {
+func (fs *customFileSystem) Sync() error {
fs.staleLock.Lock()
defer fs.staleLock.Unlock()
fs.staleThreshold = time.Now()
@@ -71,17 +108,17 @@ func (fs *siteFileSystem) Sync() error {
// Stale returns true if information obtained at time t should be
// considered stale.
-func (fs *siteFileSystem) Stale(t time.Time) bool {
+func (fs *customFileSystem) Stale(t time.Time) bool {
fs.staleLock.Lock()
defer fs.staleLock.Unlock()
return !fs.staleThreshold.Before(t)
}
-func (fs *siteFileSystem) newNode(name string, perm os.FileMode, modTime time.Time) (node inode, err error) {
+func (fs *customFileSystem) newNode(name string, perm os.FileMode, modTime time.Time) (node inode, err error) {
return nil, ErrInvalidOperation
}
-func (fs *siteFileSystem) mountCollection(parent inode, id string) inode {
+func (fs *customFileSystem) mountCollection(parent inode, id string) inode {
var coll Collection
err := fs.RequestAndDecode(&coll, "GET", "arvados/v1/collections/"+id, nil, nil)
if err != nil {
@@ -96,7 +133,23 @@ func (fs *siteFileSystem) mountCollection(parent inode, id string) inode {
return root
}
-func (fs *siteFileSystem) newProjectNode(root inode, name, uuid string) inode {
+func (fs *customFileSystem) newProjectNode(root inode, name, uuid string) inode {
+ return &projectnode{
+ uuid: uuid,
+ inode: &treenode{
+ fs: fs,
+ parent: root,
+ inodes: make(map[string]inode),
+ fileinfo: fileinfo{
+ name: name,
+ modTime: time.Now(),
+ mode: 0755 | os.ModeDir,
+ },
+ },
+ }
+}
+
+func (fs *customFileSystem) newUserNode(root inode, name, uuid string) inode {
return &projectnode{
uuid: uuid,
inode: &treenode{
diff --git a/sdk/go/arvados/fs_users.go b/sdk/go/arvados/fs_users.go
new file mode 100644
index 0000000..ccfe2c5
--- /dev/null
+++ b/sdk/go/arvados/fs_users.go
@@ -0,0 +1,62 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package arvados
+
+import (
+ "os"
+)
+
+// usersnode is a virtual directory with an entry for each visible
+// Arvados username, each showing the respective user's "home
+// projects".
+type usersnode struct {
+ inode
+ staleChecker
+ err error
+}
+
+func (un *usersnode) load() {
+ fs := un.FS().(*customFileSystem)
+
+ params := ResourceListParams{
+ Order: "uuid",
+ }
+ for {
+ var resp UserList
+ un.err = fs.RequestAndDecode(&resp, "GET", "arvados/v1/users", nil, params)
+ if un.err != nil {
+ return
+ }
+ if len(resp.Items) == 0 {
+ break
+ }
+ for _, user := range resp.Items {
+ if user.Username == "" {
+ continue
+ }
+ un.inode.Child(user.Username, func(inode) (inode, error) {
+ return fs.newProjectNode(un, user.Username, user.UUID), nil
+ })
+ }
+ params.Filters = []Filter{{"uuid", ">", resp.Items[len(resp.Items)-1].UUID}}
+ }
+ un.err = nil
+}
+
+func (un *usersnode) Readdir() ([]os.FileInfo, error) {
+ un.staleChecker.DoIfStale(un.load, un.FS().(*customFileSystem).Stale)
+ if un.err != nil {
+ return nil, un.err
+ }
+ return un.inode.Readdir()
+}
+
+func (un *usersnode) Child(name string, _ func(inode) (inode, error)) (inode, error) {
+ un.staleChecker.DoIfStale(un.load, un.FS().(*customFileSystem).Stale)
+ if un.err != nil {
+ return nil, un.err
+ }
+ return un.inode.Child(name, nil)
+}
diff --git a/services/keep-web/handler.go b/services/keep-web/handler.go
index 19a2040..5ab4f70 100644
--- a/services/keep-web/handler.go
+++ b/services/keep-web/handler.go
@@ -236,7 +236,7 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
pathParts := strings.Split(r.URL.Path[1:], "/")
var stripParts int
- var targetID string
+ var collectionID string
var tokens []string
var reqTokens []string
var pathToken bool
@@ -250,7 +250,7 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
attachment = true
}
- if targetID = parseCollectionIDFromDNSName(r.Host); targetID != "" {
+ if collectionID = parseCollectionIDFromDNSName(r.Host); collectionID != "" {
// http://ID.collections.example/PATH...
credentialsOK = true
} else if r.URL.Path == "/status.json" {
@@ -258,28 +258,23 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
return
} else if len(pathParts) >= 1 && strings.HasPrefix(pathParts[0], "c=") {
// /c=ID[/PATH...]
- targetID = parseCollectionIDFromURL(pathParts[0][2:])
+ collectionID = parseCollectionIDFromURL(pathParts[0][2:])
stripParts = 1
} else if len(pathParts) >= 2 && pathParts[0] == "collections" {
if len(pathParts) >= 4 && pathParts[1] == "download" {
// /collections/download/ID/TOKEN/PATH...
- targetID = parseCollectionIDFromURL(pathParts[2])
+ collectionID = parseCollectionIDFromURL(pathParts[2])
tokens = []string{pathParts[3]}
stripParts = 4
pathToken = true
} else {
// /collections/ID/PATH...
- targetID = parseCollectionIDFromURL(pathParts[1])
+ collectionID = parseCollectionIDFromURL(pathParts[1])
tokens = h.Config.AnonymousTokens
stripParts = 2
}
}
- if targetID == "" {
- statusCode = http.StatusNotFound
- return
- }
-
formToken := r.FormValue("api_token")
if formToken != "" && r.Header.Get("Origin") != "" && attachment && r.URL.Query().Get("api_token") == "" {
// The client provided an explicit token in the POST
@@ -347,7 +342,7 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
tokenResult := make(map[string]int)
for _, arv.ApiToken = range tokens {
var err error
- collection, err = h.Config.Cache.Get(arv, targetID, forceReload)
+ collection, err = h.Config.Cache.Get(arv, collectionID, forceReload)
if err == nil {
// Success
break
@@ -414,14 +409,21 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
AuthToken: arv.ApiToken,
Insecure: arv.ApiInsecure,
}
- fs, err := collection.FileSystem(client, kc)
+
+ var fs arvados.FileSystem
+ if collectionID == "" {
+ fs = client.SiteFileSystem(kc)
+ } else {
+ fs, err = collection.FileSystem(client, kc)
+ }
if err != nil {
statusCode, statusText = http.StatusInternalServerError, err.Error()
return
}
- targetIsPDH := arvadosclient.PDHMatch(targetID)
- if targetIsPDH && writeMethod[r.Method] {
+ writefs, writeOK := fs.(arvados.CollectionFileSystem)
+ targetIsPDH := arvadosclient.PDHMatch(collectionID)
+ if (targetIsPDH || !writeOK) && writeMethod[r.Method] {
statusCode, statusText = http.StatusMethodNotAllowed, errReadOnly.Error()
return
}
@@ -435,7 +437,7 @@ func (h *handler) ServeHTTP(wOrig http.ResponseWriter, r *http.Request) {
w = &updateOnSuccess{
ResponseWriter: w,
update: func() error {
- return h.Config.Cache.Update(client, *collection, fs)
+ return h.Config.Cache.Update(client, *collection, writefs)
}}
}
h := webdav.Handler{
diff --git a/services/keep-web/webdav.go b/services/keep-web/webdav.go
index af83681..941090a 100644
--- a/services/keep-web/webdav.go
+++ b/services/keep-web/webdav.go
@@ -36,7 +36,7 @@ var (
// existence automatically so sequences like "mkcol foo; put foo/bar"
// work as expected.
type webdavFS struct {
- collfs arvados.CollectionFileSystem
+ collfs arvados.FileSystem
writing bool
// webdav PROPFIND reads the first few bytes of each file
// whose filename extension isn't recognized, which is
commit 161de9f7fe71e328837df5c053f3663bc59245f3
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Tue Mar 20 10:52:09 2018 -0400
13111: Add tests for /users/ paths.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/services/keep-web/cadaver_test.go b/services/keep-web/cadaver_test.go
index b7ec1de..5f5f69a 100644
--- a/services/keep-web/cadaver_test.go
+++ b/services/keep-web/cadaver_test.go
@@ -22,24 +22,33 @@ import (
)
func (s *IntegrationSuite) TestCadaverHTTPAuth(c *check.C) {
- s.testCadaver(c, arvadostest.ActiveToken, func(newUUID string) (string, string, string) {
+ s.testCadaver(c, arvadostest.ActiveToken, func(newCollection arvados.Collection) (string, string, string) {
r := "/c=" + arvadostest.FooAndBarFilesInDirUUID + "/"
- w := "/c=" + newUUID + "/"
+ w := "/c=" + newCollection.UUID + "/"
pdh := "/c=" + strings.Replace(arvadostest.FooAndBarFilesInDirPDH, "+", "-", -1) + "/"
return r, w, pdh
})
}
func (s *IntegrationSuite) TestCadaverPathAuth(c *check.C) {
- s.testCadaver(c, "", func(newUUID string) (string, string, string) {
+ s.testCadaver(c, "", func(newCollection arvados.Collection) (string, string, string) {
r := "/c=" + arvadostest.FooAndBarFilesInDirUUID + "/t=" + arvadostest.ActiveToken + "/"
- w := "/c=" + newUUID + "/t=" + arvadostest.ActiveToken + "/"
+ w := "/c=" + newCollection.UUID + "/t=" + arvadostest.ActiveToken + "/"
pdh := "/c=" + strings.Replace(arvadostest.FooAndBarFilesInDirPDH, "+", "-", -1) + "/t=" + arvadostest.ActiveToken + "/"
return r, w, pdh
})
}
-func (s *IntegrationSuite) testCadaver(c *check.C, password string, pathFunc func(string) (string, string, string)) {
+func (s *IntegrationSuite) TestCadaverUserProject(c *check.C) {
+ s.testCadaver(c, arvadostest.ActiveToken, func(newCollection arvados.Collection) (string, string, string) {
+ r := "/users/active/foo_file_in_dir/"
+ w := "/users/active/" + newCollection.Name
+ pdh := "/c=" + strings.Replace(arvadostest.FooAndBarFilesInDirPDH, "+", "-", -1) + "/"
+ return r, w, pdh
+ })
+}
+
+func (s *IntegrationSuite) testCadaver(c *check.C, password string, pathFunc func(arvados.Collection) (string, string, string)) {
testdata := []byte("the human tragedy consists in the necessity of living with the consequences of actions performed under the pressure of compulsions we do not understand")
tempdir, err := ioutil.TempDir("", "keep-web-test-")
@@ -62,7 +71,7 @@ func (s *IntegrationSuite) testCadaver(c *check.C, password string, pathFunc fun
err = arv.RequestAndDecode(&newCollection, "POST", "/arvados/v1/collections", bytes.NewBufferString(url.Values{"collection": {"{}"}}.Encode()), nil)
c.Assert(err, check.IsNil)
- readPath, writePath, pdhPath := pathFunc(newCollection.UUID)
+ readPath, writePath, pdhPath := pathFunc(newCollection)
matchToday := time.Now().Format("Jan +2")
diff --git a/services/keep-web/handler_test.go b/services/keep-web/handler_test.go
index 21e47c8..a8f8c83 100644
--- a/services/keep-web/handler_test.go
+++ b/services/keep-web/handler_test.go
@@ -62,6 +62,7 @@ func (s *UnitSuite) TestInvalidUUID(c *check.C) {
for _, trial := range []string{
"http://keep-web/c=" + bogusID + "/foo",
"http://keep-web/c=" + bogusID + "/t=" + token + "/foo",
+ "http://keep-web/users/active/" + bogusID + "/foo",
"http://keep-web/collections/download/" + bogusID + "/" + token + "/foo",
"http://keep-web/collections/" + bogusID + "/foo",
"http://" + bogusID + ".keep-web/" + bogusID + "/foo",
@@ -517,6 +518,12 @@ func (s *IntegrationSuite) TestDirectoryListing(c *check.C) {
cutDirs: 2,
},
{
+ uri: "download.example.com/users/active/" + arvadostest.FooAndBarFilesInDirUUID + "/",
+ header: authHeader,
+ expect: []string{"dir1/foo", "dir1/bar"},
+ cutDirs: 3,
+ },
+ {
uri: "collections.example.com/collections/download/" + arvadostest.FooAndBarFilesInDirUUID + "/" + arvadostest.ActiveToken + "/",
header: nil,
expect: []string{"dir1/foo", "dir1/bar"},
commit 0d5f2f7c474724871f05bad1308c66e68c9d7473
Author: Tom Clegg <tclegg at veritasgenetics.com>
Date: Tue Mar 20 10:00:36 2018 -0400
13111: Test webdav with http basic authentication.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tclegg at veritasgenetics.com>
diff --git a/services/keep-web/cadaver_test.go b/services/keep-web/cadaver_test.go
index eb32367..b7ec1de 100644
--- a/services/keep-web/cadaver_test.go
+++ b/services/keep-web/cadaver_test.go
@@ -6,11 +6,13 @@ package main
import (
"bytes"
+ "fmt"
"io"
"io/ioutil"
"net/url"
"os"
"os/exec"
+ "path/filepath"
"strings"
"time"
@@ -19,34 +21,51 @@ import (
check "gopkg.in/check.v1"
)
-func (s *IntegrationSuite) TestWebdavWithCadaver(c *check.C) {
+func (s *IntegrationSuite) TestCadaverHTTPAuth(c *check.C) {
+ s.testCadaver(c, arvadostest.ActiveToken, func(newUUID string) (string, string, string) {
+ r := "/c=" + arvadostest.FooAndBarFilesInDirUUID + "/"
+ w := "/c=" + newUUID + "/"
+ pdh := "/c=" + strings.Replace(arvadostest.FooAndBarFilesInDirPDH, "+", "-", -1) + "/"
+ return r, w, pdh
+ })
+}
+
+func (s *IntegrationSuite) TestCadaverPathAuth(c *check.C) {
+ s.testCadaver(c, "", func(newUUID string) (string, string, string) {
+ r := "/c=" + arvadostest.FooAndBarFilesInDirUUID + "/t=" + arvadostest.ActiveToken + "/"
+ w := "/c=" + newUUID + "/t=" + arvadostest.ActiveToken + "/"
+ pdh := "/c=" + strings.Replace(arvadostest.FooAndBarFilesInDirPDH, "+", "-", -1) + "/t=" + arvadostest.ActiveToken + "/"
+ return r, w, pdh
+ })
+}
+
+func (s *IntegrationSuite) testCadaver(c *check.C, password string, pathFunc func(string) (string, string, string)) {
testdata := []byte("the human tragedy consists in the necessity of living with the consequences of actions performed under the pressure of compulsions we do not understand")
- localfile, err := ioutil.TempFile("", "localfile")
+ tempdir, err := ioutil.TempDir("", "keep-web-test-")
+ c.Assert(err, check.IsNil)
+ defer os.RemoveAll(tempdir)
+
+ localfile, err := ioutil.TempFile(tempdir, "localfile")
c.Assert(err, check.IsNil)
- defer os.Remove(localfile.Name())
localfile.Write(testdata)
- emptyfile, err := ioutil.TempFile("", "emptyfile")
+ emptyfile, err := ioutil.TempFile(tempdir, "emptyfile")
c.Assert(err, check.IsNil)
- defer os.Remove(emptyfile.Name())
- checkfile, err := ioutil.TempFile("", "checkfile")
+ checkfile, err := ioutil.TempFile(tempdir, "checkfile")
c.Assert(err, check.IsNil)
- defer os.Remove(checkfile.Name())
var newCollection arvados.Collection
arv := arvados.NewClientFromEnv()
arv.AuthToken = arvadostest.ActiveToken
err = arv.RequestAndDecode(&newCollection, "POST", "/arvados/v1/collections", bytes.NewBufferString(url.Values{"collection": {"{}"}}.Encode()), nil)
c.Assert(err, check.IsNil)
- writePath := "/c=" + newCollection.UUID + "/t=" + arv.AuthToken + "/"
- pdhPath := "/c=" + strings.Replace(arvadostest.FooAndBarFilesInDirPDH, "+", "-", -1) + "/t=" + arv.AuthToken + "/"
+ readPath, writePath, pdhPath := pathFunc(newCollection.UUID)
matchToday := time.Now().Format("Jan +2")
- readPath := "/c=" + arvadostest.FooAndBarFilesInDirUUID + "/t=" + arvadostest.ActiveToken + "/"
type testcase struct {
path string
cmd string
@@ -215,6 +234,22 @@ func (s *IntegrationSuite) TestWebdavWithCadaver(c *check.C) {
os.Remove(checkfile.Name())
cmd := exec.Command("cadaver", "http://"+s.testServer.Addr+trial.path)
+ if password != "" {
+ // cadaver won't try username/password
+ // authentication unless the server responds
+ // 401 to an unauthenticated request, which it
+ // only does in AttachmentOnlyHost,
+ // TrustAllContent, and per-collection vhost
+ // cases.
+ s.testServer.Config.AttachmentOnlyHost = s.testServer.Addr
+
+ cmd.Env = append(os.Environ(), "HOME="+tempdir)
+ f, err := os.OpenFile(filepath.Join(tempdir, ".netrc"), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
+ c.Assert(err, check.IsNil)
+ _, err = fmt.Fprintf(f, "default login none password %s\n", password)
+ c.Assert(err, check.IsNil)
+ c.Assert(f.Close(), check.IsNil)
+ }
cmd.Stdin = bytes.NewBufferString(trial.cmd)
stdout, err := cmd.StdoutPipe()
c.Assert(err, check.Equals, nil)
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list