Remove obsolete manifestfs
Change-Id: Ifa460f8773b5d55080e00dca337a5d4e93bf63e9
diff --git a/cmd/slothfs-deref-manifest/main.go b/cmd/slothfs-deref-manifest/main.go
deleted file mode 100644
index 00965f4..0000000
--- a/cmd/slothfs-deref-manifest/main.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2016 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
- "flag"
- "log"
- "os"
-
- "github.com/google/slothfs/gitiles"
- "github.com/google/slothfs/populate"
-)
-
-func main() {
- gitilesOptions := gitiles.DefineFlags()
- branch := flag.String("branch", "master", "Specify branch of the manifest repository to use.")
- repo := flag.String("repo", "platform/manifest", "Set repository name holding manifest file.")
- flag.Parse()
-
- service, err := gitiles.NewService(*gitilesOptions)
- if err != nil {
- log.Fatalf("NewService: %v", err)
- }
-
- mf, err := populate.FetchManifest(service, *repo, *branch)
- if err != nil {
- log.Fatalf("FetchManifest: %v", err)
- }
-
- mf.Filter()
-
- if err := populate.DerefManifest(service, mf); err != nil {
- log.Fatalf("DerefManifest: %v", err)
- }
-
- xml, err := mf.MarshalXML()
- if err != nil {
- log.Fatalf("MarshalXML: %v", err)
- }
-
- os.Stdout.Write(xml)
-}
diff --git a/cmd/slothfs-deref-repo/main.go b/cmd/slothfs-deref-repo/main.go
deleted file mode 100644
index 74140d2..0000000
--- a/cmd/slothfs-deref-repo/main.go
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2016 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// This program creates a manifest file with revisions filled in from
-// a normal repo checkout. This can be used for comparing slothfs and
-// actual repo checkouts.
-package main
-
-import (
- "flag"
- "log"
- "os"
- "path/filepath"
-
- "github.com/google/slothfs/manifest"
- git "github.com/libgit2/git2go"
-)
-
-func main() {
- flag.Parse()
- if len(flag.Args()) == 0 {
- log.Fatal("must state the repo toplevel directory.")
- }
-
- top := flag.Arg(0)
- mf, err := manifest.ParseFile(filepath.Join(top, ".repo", "manifest.xml"))
- if err != nil {
- log.Fatal(err)
- }
-
- mf.Filter()
- for i, p := range mf.Project {
- repoPath := filepath.Join(top, p.GetPath(), ".git")
- repo, err := git.OpenRepository(repoPath)
- if err != nil {
- continue
- }
-
- h, err := repo.Head()
- if err != nil {
- log.Println("head", p.Name, err)
- }
-
- obj, err := h.Peel(git.ObjectCommit)
- if err != nil {
- log.Println("peel", p.Name, err)
- }
-
- mf.Project[i].Revision = obj.Id().String()
- }
-
- xml, err := mf.MarshalXML()
- if err != nil {
- log.Fatal("MarshalXML", err)
- }
-
- os.Stdout.Write(xml)
-}
diff --git a/cmd/slothfs-manifestfs/main.go b/cmd/slothfs-manifestfs/main.go
deleted file mode 100644
index 549dfd7..0000000
--- a/cmd/slothfs-manifestfs/main.go
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2016 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
- "flag"
- "io/ioutil"
- "log"
- "os"
- "path/filepath"
- "time"
-
- "github.com/google/slothfs/cache"
- "github.com/google/slothfs/fs"
- "github.com/google/slothfs/gitiles"
- "github.com/google/slothfs/manifest"
- "github.com/hanwen/go-fuse/fuse/nodefs"
-)
-
-func main() {
- manifestPath := flag.String("manifest", "", "Set path to the expanded manifest file.")
- cacheDir := flag.String("cache", filepath.Join(os.Getenv("HOME"), ".cache", "slothfs"),
- "Set the directory holding the file system cache.")
- debug := flag.Bool("debug", false, "Print FUSE debug info.")
- config := flag.String("config", "", "Set path to clone configuration JSON file.")
- gitilesOptions := gitiles.DefineFlags()
- flag.Parse()
-
- if *manifestPath == "" {
- log.Fatal("must set --manifest")
- }
- if *cacheDir == "" {
- log.Fatal("must set --cache")
- }
-
- if len(flag.Args()) < 1 {
- log.Fatal("mountpoint argument missing.")
- }
- mntDir := flag.Arg(0)
-
- cache, err := cache.NewCache(*cacheDir, cache.Options{})
- if err != nil {
- log.Printf("NewCache: %v", err)
- }
-
- service, err := gitiles.NewService(*gitilesOptions)
- if err != nil {
- log.Printf("NewService: %v", err)
- }
-
- mf, err := manifest.ParseFile(*manifestPath)
- if err != nil {
- log.Fatal(err)
- }
-
- opts := fs.ManifestOptions{
- Manifest: mf,
- }
-
- if *config != "" {
- configContents, err := ioutil.ReadFile(*config)
- if err != nil {
- log.Fatal(err)
- }
- opts.RepoCloneOption, opts.FileCloneOption, err = fs.ReadConfig(configContents)
- if err != nil {
- log.Fatal(err)
- }
- }
-
- root, err := fs.NewManifestFS(service, cache, opts)
- if err != nil {
- log.Fatalf("NewManifestFS: %v", err)
- }
-
- server, _, err := nodefs.MountRoot(mntDir, root, &nodefs.Options{
- EntryTimeout: time.Hour,
- NegativeTimeout: time.Hour,
- AttrTimeout: time.Hour,
- Debug: *debug,
- })
- if err != nil {
- log.Fatalf("MountFileSystem: %v", err)
- }
- log.Printf("Started gitiles fs FUSE on %s", mntDir)
- server.Serve()
-}
diff --git a/fs/fixture_test.go b/fs/fixture_test.go
new file mode 100644
index 0000000..92d41f7
--- /dev/null
+++ b/fs/fixture_test.go
@@ -0,0 +1,103 @@
+// Copyright 2016 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fs
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "time"
+
+ "github.com/google/slothfs/cache"
+ "github.com/google/slothfs/gitiles"
+ "github.com/hanwen/go-fuse/fuse"
+ "github.com/hanwen/go-fuse/fuse/nodefs"
+)
+
+type testFixture struct {
+ dir string
+ mntDir string
+ server *fuse.Server
+ cache *cache.Cache
+ testServer *testServer
+ service *gitiles.Service
+ root nodefs.Node
+}
+
+func (f *testFixture) cleanup() {
+ if f.testServer != nil {
+ f.testServer.listener.Close()
+ }
+ if f.server != nil {
+ f.server.Unmount()
+ }
+ os.RemoveAll(f.dir)
+}
+
+func newTestFixture() (*testFixture, error) {
+ d, err := ioutil.TempDir("", "slothfs")
+ if err != nil {
+ return nil, err
+ }
+
+ fixture := &testFixture{dir: d}
+
+ fixture.cache, err = cache.NewCache(filepath.Join(d, "/cache"), cache.Options{})
+ if err != nil {
+ return nil, err
+ }
+
+ fixture.testServer, err = newTestServer()
+ if err != nil {
+ return nil, err
+ }
+
+ fixture.service, err = gitiles.NewService(gitiles.Options{
+ Address: fmt.Sprintf("http://%s", fixture.testServer.addr),
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return fixture, nil
+}
+
+func (f *testFixture) mount(root nodefs.Node) error {
+ f.mntDir = filepath.Join(f.dir, "mnt")
+ if err := os.Mkdir(f.mntDir, 0755); err != nil {
+ return err
+ }
+
+ fuseOpts := &nodefs.Options{
+ EntryTimeout: time.Hour,
+ NegativeTimeout: time.Hour,
+ AttrTimeout: time.Hour,
+ }
+
+ var err error
+ f.server, _, err = nodefs.MountRoot(f.mntDir, root, fuseOpts)
+ if err != nil {
+ return err
+ }
+
+ if fuseDebug {
+ f.server.SetDebug(true)
+ }
+ go f.server.Serve()
+
+ f.root = root
+ return nil
+}
diff --git a/fs/manifestfs.go b/fs/manifestfs.go
deleted file mode 100644
index 018efcf..0000000
--- a/fs/manifestfs.go
+++ /dev/null
@@ -1,274 +0,0 @@
-// Copyright 2016 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package fs
-
-import (
- "encoding/json"
- "fmt"
- "log"
- "path/filepath"
- "strings"
-
- "github.com/google/slothfs/cache"
- "github.com/google/slothfs/gitiles"
- "github.com/google/slothfs/manifest"
- "github.com/hanwen/go-fuse/fuse"
- "github.com/hanwen/go-fuse/fuse/nodefs"
-)
-
-type manifestFSRoot struct {
- nodefs.Node
-
- service *gitiles.Service
-
- cache *cache.Cache
- nodeCache *nodeCache
-
- // trees is Path => Tree map.
- trees map[string]*gitiles.Tree
-
- options ManifestOptions
-
- // XML data for the manifest.
- manifestXML []byte
-}
-
-func (r *manifestFSRoot) Deletable() bool { return false }
-
-func (r *manifestFSRoot) GetXAttr(attribute string, context *fuse.Context) (data []byte, code fuse.Status) {
- return nil, fuse.ENODATA
-}
-
-// NewManifestFS creates a Manifest FS root node.
-func NewManifestFS(service *gitiles.Service, cache *cache.Cache, opts ManifestOptions) (nodefs.Node, error) {
- xml, err := opts.Manifest.MarshalXML()
- if err != nil {
- return nil, err
- }
- root := &manifestFSRoot{
- Node: newDirNode(),
- nodeCache: newNodeCache(),
- cache: cache,
- service: service,
- options: opts,
- manifestXML: xml,
- }
-
- for _, p := range opts.Manifest.Project {
- if _, err := parseID(p.Revision); err != nil {
- return nil, fmt.Errorf("project %s revision %q does not parse: %v", p.Name, p.Revision, err)
- }
- }
-
- root.trees, err = fetchTreeMap(cache, service, opts.Manifest)
- if err != nil {
- return nil, err
- }
- return root, nil
-}
-
-func (r *manifestFSRoot) OnMount(fsConn *nodefs.FileSystemConnector) {
- if err := r.onMount(fsConn); err != nil {
- log.Printf("onMount: %v", err)
- for k := range r.Inode().Children() {
- r.Inode().RmChild(k)
- }
-
- r.Inode().NewChild("ERROR", false, newDataNode([]byte(err.Error())))
- }
-
- // Don't need the trees anymore.
- r.trees = nil
-}
-
-func (r *manifestFSRoot) onMount(fsConn *nodefs.FileSystemConnector) error {
- var byDepth [][]string
- for p := range r.trees {
- d := len(strings.Split(p, "/"))
- for len(byDepth) <= d {
- byDepth = append(byDepth, nil)
- }
-
- byDepth[d] = append(byDepth[d], p)
- }
-
- clonablePaths := map[string]bool{}
- revmap := map[string]*manifest.Project{}
- for i, p := range r.options.Manifest.Project {
- revmap[p.GetPath()] = &r.options.Manifest.Project[i]
-
- if p.CloneDepth == "" {
- clonablePaths[p.GetPath()] = true
- }
- }
-
- // TODO(hanwen): use parallelism here.
-
- for _, ps := range byDepth {
- for _, p := range ps {
- dir, base := filepath.Split(p)
- parent, left := fsConn.Node(r.Inode(), dir)
- for _, c := range left {
- ch := parent.NewChild(c, true, newDirNode())
- parent = ch
- }
-
- clone, ok := clonablePaths[p]
- if !ok {
- for _, e := range r.options.RepoCloneOption {
- if e.RE.FindString(p) != "" {
- clone = e.Clone
- break
- }
- }
- }
-
- cloneURL := revmap[p].CloneURL
- if !clone {
- cloneURL = ""
- }
-
- repoService := r.service.NewRepoService(revmap[p].Name)
-
- opts := GitilesRevisionOptions{
- Revision: revmap[p].Revision,
- GitilesOptions: GitilesOptions{
- CloneURL: cloneURL,
- CloneOption: r.options.FileCloneOption,
- },
- }
-
- subRoot := NewGitilesRoot(r.cache, r.trees[p], repoService, opts)
- subRoot.(*gitilesRoot).nodeCache = r.nodeCache
- parent.NewChild(base, true, subRoot)
- if err := subRoot.(*gitilesRoot).onMount(fsConn); err != nil {
- return fmt.Errorf("onMount(%s): %v", p, err)
- }
- }
- }
-
- // Do Linkfile, Copyfile after setting up the repos, so we
- // have directories to attach the copy/link nodes to.
- for _, p := range r.options.Manifest.Project {
- for _, cp := range p.Copyfile {
- srcNode, left := fsConn.Node(r.Inode(), filepath.Join(p.GetPath(), cp.Src))
- if len(left) > 0 {
- return fmt.Errorf("Copyfile(%s): source %s does not exist", p.Name, cp.Src)
- }
-
- dir, left := fsConn.Node(r.Inode(), cp.Dest)
- switch len(left) {
- case 0:
- return fmt.Errorf("Copyfile(%s): dest %s already exists.", p.Name, cp.Dest)
- case 1:
- default:
- return fmt.Errorf("Copyfile(%s): directory for dest %s does not exist.", p.Name, cp.Dest)
- }
-
- dir.AddChild(left[0], srcNode)
- }
-
- for _, lf := range p.Linkfile {
- dir, left := fsConn.Node(r.Inode(), lf.Dest)
- switch len(left) {
- case 0:
- return fmt.Errorf("Linkfile(%s): dest %s already exists.", p.Name, lf.Dest)
- case 1:
- default:
- return fmt.Errorf("Linkfile(%s): directory for dest %s does not exist.", p.Name, lf.Dest)
- }
-
- src := filepath.Join(p.GetPath(), lf.Src)
- rel, err := filepath.Rel(filepath.Dir(lf.Dest), src)
- if err != nil {
- return err
- }
-
- node := newLinkNode(filepath.Join(rel))
- dir.NewChild(left[0], false, node)
- }
- }
-
- metaNode := r.Inode().NewChild(".slothfs", true, newDirNode())
- metaNode.NewChild("manifest.xml", false, newDataNode(r.manifestXML))
-
- var tree gitiles.Tree
- treeContent, err := json.Marshal(tree)
- if err != nil {
- log.Panicf("json.Marshal: %v", err)
- }
- metaNode.NewChild("tree.json", false, newDataNode(treeContent))
-
- return nil
-}
-
-func fetchTreeMap(c *cache.Cache, service *gitiles.Service, mf *manifest.Manifest) (map[string]*gitiles.Tree, error) {
- type resultT struct {
- path string
- resp *gitiles.Tree
- err error
- }
-
- // Fetch all the trees in parallel.
- out := make(chan resultT, len(mf.Project))
- for _, p := range mf.Project {
- go func(p manifest.Project) {
- revID, err := parseID(p.Revision)
- if err != nil {
- out <- resultT{p.GetPath(), nil, err}
- return
- }
-
- tree, err := c.Tree.Get(revID)
- cached := (err == nil && tree != nil)
- if err != nil {
- if repo := c.Git.OpenLocal(p.CloneURL); repo != nil {
- tree, err = cache.GetTree(repo, revID)
- }
- }
-
- if err != nil {
- repoService := service.NewRepoService(p.Name)
-
- tree, err = repoService.GetTree(p.Revision, "", true)
- }
-
- if !cached && tree != nil && err == nil {
- if err := c.Tree.Add(revID, tree); err != nil {
- log.Printf("treeCache.Add: %v", err)
- }
- }
-
- out <- resultT{p.GetPath(), tree, err}
- }(p)
- }
-
- // drain goroutines
- var result []resultT
- for range mf.Project {
- r := <-out
- result = append(result, r)
- }
-
- resmap := map[string]*gitiles.Tree{}
- for _, r := range result {
- if r.err != nil {
- return nil, fmt.Errorf("Tree(%s): %v", r.path, r.err)
- }
-
- resmap[r.path] = r.resp
- }
- return resmap, nil
-}
diff --git a/fs/manifestfs_test.go b/fs/manifestfs_test.go
deleted file mode 100644
index b17ddf9..0000000
--- a/fs/manifestfs_test.go
+++ /dev/null
@@ -1,468 +0,0 @@
-// Copyright 2016 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package fs
-
-import (
- "fmt"
- "io/ioutil"
- "os"
- "os/exec"
- "path/filepath"
- "reflect"
- "sort"
- "strings"
- "syscall"
- "testing"
- "time"
-
- "github.com/google/slothfs/cache"
- "github.com/google/slothfs/gitiles"
- "github.com/google/slothfs/manifest"
- "github.com/hanwen/go-fuse/fuse"
- "github.com/hanwen/go-fuse/fuse/nodefs"
-)
-
-func newManifestTestFixture(mf *manifest.Manifest) (*testFixture, error) {
- fix, err := newTestFixture()
- if err != nil {
- return nil, err
- }
-
- opts := ManifestOptions{
- Manifest: mf,
- }
-
- fs, err := NewManifestFS(fix.service, fix.cache, opts)
- if err != nil {
- return nil, err
- }
- if err := fix.mount(fs); err != nil {
- return nil, err
- }
-
- return fix, nil
-}
-
-func TestManifestFSGitRepoSeedsTreeCache(t *testing.T) {
- fix, err := newTestFixture()
- if err != nil {
- t.Fatal("newTestFixture", err)
- }
- defer fix.cleanup()
-
- // Add a git repo.
- cmd := exec.Command("/bin/sh", "-c",
- strings.Join([]string{
- "mkdir -p localhost/platform/build/kati.git",
- "cd localhost/platform/build/kati.git",
- "git init",
- "touch file",
- "git add file",
- "git commit -m msg -a",
- "git show --no-patch --pretty=format:'%H' HEAD | head",
- }, " && "))
- cmd.Dir = filepath.Join(fix.dir, "cache", "git")
-
- headSHA1 := ""
- if out, err := cmd.CombinedOutput(); err != nil {
- t.Fatalf("create repo: %v, out: %s", err, string(out))
- } else {
- lines := strings.Split(string(out), "\n")
- headSHA1 = lines[len(lines)-1]
- }
-
- p := testManifest.Project[0]
- p.CloneURL = "file:///localhost/platform/build/kati"
- p.Revision = headSHA1
-
- mf := *testManifest
- mf.Project = nil
- mf.Project = append(mf.Project, p)
-
- opts := ManifestOptions{
- Manifest: &mf,
- }
-
- fs, err := NewManifestFS(fix.service, fix.cache, opts)
- if err != nil {
- t.Fatal("NewManifestFS", err)
- }
- if err := fix.mount(fs); err != nil {
- t.Fatal("mount", err)
- }
-
- headID, err := parseID(headSHA1)
- if err != nil {
- t.Fatalf("parseID(%s): %v", headSHA1, err)
- }
-
- tree, err := fix.cache.Tree.Get(headID)
- if err != nil {
- t.Fatalf("treeCache.Get(%s): %v", headSHA1, err)
- }
-
- newInt := func(n int) *int { return &n }
- tree.ID = ""
- want := &gitiles.Tree{
- Entries: []gitiles.TreeEntry{
- {
- Name: "file",
- Mode: 0100644,
- Type: "blob",
- ID: "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
- Size: newInt(0),
- },
- },
- }
- if !reflect.DeepEqual(tree, want) {
- t.Errorf("got cached tree %v, want %v", tree, want)
- }
-}
-
-func TestManifestFSCloneOption(t *testing.T) {
- mf := *testManifest
- for i := range mf.Project {
- mf.Project[i].CloneDepth = "1"
- }
-
- fix, err := newManifestTestFixture(&mf)
- if err != nil {
- t.Fatalf("newManifestTestFixture: %v", err)
- }
- defer fix.cleanup()
-
- fs := fix.root.(*manifestFSRoot)
- ch := fs.Inode()
- for _, n := range []string{"build", "kati", "AUTHORS"} {
- newCh := ch.GetChild(n)
- if ch == nil {
- t.Fatalf("node for %q not found. Have %s", n, ch.Children())
- }
- ch = newCh
- }
-
- giNode, ok := ch.Node().(*gitilesNode)
- if !ok {
- t.Fatalf("got node type %T, want *gitilesNode", ch.Node())
- }
-
- if giNode.clone {
- t.Errorf("file had clone set.")
- }
-}
-
-func TestManifestFSTimestamps(t *testing.T) {
- fix, err := newManifestTestFixture(testManifest)
- if err != nil {
- t.Fatal("newTestFixture", err)
- }
- defer fix.cleanup()
-
- var zeroFiles []string
- if err := filepath.Walk(fix.mntDir, func(n string, fi os.FileInfo, err error) error {
- if fi != nil && fi.ModTime().UnixNano() == 0 {
- r, _ := filepath.Rel(fix.mntDir, n)
- zeroFiles = append(zeroFiles, r)
- }
- return nil
- }); err != nil {
- t.Fatalf("Walk: %v", err)
- }
- if len(zeroFiles) > 0 {
- sort.Strings(zeroFiles)
- t.Errorf("found files with zero timestamps: %v", zeroFiles)
- }
-}
-
-func TestManifestFSBasic(t *testing.T) {
- fix, err := newManifestTestFixture(testManifest)
- if err != nil {
- t.Fatal("newTestFixture", err)
- }
- defer fix.cleanup()
-
- fn := filepath.Join(fix.mntDir, "build", "kati", "AUTHORS")
- fi, err := os.Lstat(fn)
- if err != nil {
- t.Fatalf("Lstat(%s): %v", fn, err)
- }
- if fi.Size() != 373 {
- t.Errorf("got size %d want %d", fi.Size(), 373)
- }
-
- contents, err := ioutil.ReadFile(fn)
- if err != nil {
- t.Fatalf("ReadFile(%s): %v", fn, err)
- }
-
- want := testGitiles["/platform/build/kati/+show/ce34badf691d36e8048b63f89d1a86ee5fa4325c/AUTHORS?format=TEXT"]
- if string(contents) != want {
- t.Fatalf("got %q, want %q", contents, want)
- }
-
- copyPath := filepath.Join(fix.mntDir, "build", "copydest")
- if copyFI, err := os.Lstat(copyPath); err != nil {
- t.Errorf("Lstat(%s): %v", copyPath, err)
- } else {
- copyStat := copyFI.Sys().(*syscall.Stat_t)
- origStat := fi.Sys().(*syscall.Stat_t)
-
- if !reflect.DeepEqual(copyStat, origStat) {
- t.Errorf("got stat %v, want %v", copyStat, origStat)
- }
- }
-
- linkPath := filepath.Join(fix.mntDir, "build", "linkdest")
- if got, err := os.Readlink(linkPath); err != nil {
- t.Errorf("Readlink(%s): %v", linkPath, err)
- } else if want := "kati/AUTHORS"; got != want {
- t.Errorf("Readlink(%s) = %q, want %q", linkPath, got, want)
- }
-}
-
-func TestManifestFSXMLFile(t *testing.T) {
- fix, err := newManifestTestFixture(testManifest)
- if err != nil {
- t.Fatal("newTestFixture", err)
- }
- defer fix.cleanup()
-
- xmlPath := filepath.Join(fix.mntDir, ".slothfs", "manifest.xml")
- fuseMF, err := manifest.ParseFile(xmlPath)
- if err != nil {
- t.Fatalf("ParseFile(%s): %v", xmlPath, err)
- }
-
- if !reflect.DeepEqual(fuseMF, testManifest) {
- t.Errorf("read back manifest %v, want %v", fuseMF, testManifest)
- }
-}
-
-type testFixture struct {
- dir string
- mntDir string
- server *fuse.Server
- cache *cache.Cache
- testServer *testServer
- service *gitiles.Service
- root nodefs.Node
-}
-
-func (f *testFixture) cleanup() {
- if f.testServer != nil {
- f.testServer.listener.Close()
- }
- if f.server != nil {
- f.server.Unmount()
- }
- os.RemoveAll(f.dir)
-}
-
-func newTestFixture() (*testFixture, error) {
- d, err := ioutil.TempDir("", "slothfs")
- if err != nil {
- return nil, err
- }
-
- fixture := &testFixture{dir: d}
-
- fixture.cache, err = cache.NewCache(filepath.Join(d, "/cache"), cache.Options{})
- if err != nil {
- return nil, err
- }
-
- fixture.testServer, err = newTestServer()
- if err != nil {
- return nil, err
- }
-
- fixture.service, err = gitiles.NewService(gitiles.Options{
- Address: fmt.Sprintf("http://%s", fixture.testServer.addr),
- })
- if err != nil {
- return nil, err
- }
-
- return fixture, nil
-}
-
-func (f *testFixture) mount(root nodefs.Node) error {
- f.mntDir = filepath.Join(f.dir, "mnt")
- if err := os.Mkdir(f.mntDir, 0755); err != nil {
- return err
- }
-
- fuseOpts := &nodefs.Options{
- EntryTimeout: time.Hour,
- NegativeTimeout: time.Hour,
- AttrTimeout: time.Hour,
- }
-
- var err error
- f.server, _, err = nodefs.MountRoot(f.mntDir, root, fuseOpts)
- if err != nil {
- return err
- }
-
- if fuseDebug {
- f.server.SetDebug(true)
- }
- go f.server.Serve()
-
- f.root = root
- return nil
-}
-
-func TestMultiManifestFSBrokenXML(t *testing.T) {
- fix, err := newTestFixture()
- if err != nil {
- t.Fatalf("newTestFixture: %v", err)
- }
- defer fix.cleanup()
-
- brokenXMLFile := filepath.Join(fix.dir, "broken.xml")
- if err := ioutil.WriteFile(brokenXMLFile, []byte("I'm not XML."), 0644); err != nil {
- t.Errorf("WriteFile(%s): %v", brokenXMLFile, err)
- }
-
- opts := MultiManifestFSOptions{}
- fs := NewMultiManifestFS(fix.service, fix.cache, opts)
-
- if err := fix.mount(fs); err != nil {
- t.Fatalf("mount: %v", err)
- }
-
- if err := os.Symlink(brokenXMLFile, filepath.Join(fix.mntDir, "config", "ws")); err == nil {
- t.Fatalf("want error for broken XML file")
- }
-}
-
-func TestMultiManifestFSBasic(t *testing.T) {
- fix, err := newTestFixture()
- if err != nil {
- t.Fatalf("newTestFixture: %v", err)
- }
- defer fix.cleanup()
-
- xmlFile := filepath.Join(fix.dir, "manifest.xml")
- if err := ioutil.WriteFile(xmlFile, []byte(testManifestXML), 0644); err != nil {
- t.Errorf("WriteFile(%s): %v", xmlFile, err)
- }
-
- opts := MultiManifestFSOptions{}
- fs := NewMultiManifestFS(fix.service, fix.cache, opts)
-
- if err := fix.mount(fs); err != nil {
- t.Fatalf("mount: %v", err)
- }
-
- wsDir := filepath.Join(fix.mntDir, "ws")
- if fi, err := os.Lstat(wsDir); err == nil {
- t.Fatalf("got %v, want non-existent workspace dir", fi)
- }
-
- configName := filepath.Join(fix.mntDir, "config", "ws")
- if err := os.Symlink(xmlFile, configName); err != nil {
- t.Fatalf("Symlink(%s): %v", xmlFile, err)
- }
-
- if _, err := os.Lstat(wsDir); err != nil {
- t.Fatalf("Lstat(%s): %v", wsDir, err)
- }
-
- if got, err := os.Readlink(configName); err != nil {
- t.Fatalf("Readlink(%s): %v", configName, err)
- } else if want := "../ws/.slothfs/manifest.xml"; got != want {
- t.Errorf("got link %s, want %s", got, want)
- }
-
- if _, err := manifest.ParseFile(configName); err != nil {
- t.Fatalf("ParseFile(%s): %v", configName, err)
- }
-
- fn := filepath.Join(wsDir, "build", "kati", "AUTHORS")
- if fi, err := os.Lstat(fn); err != nil {
- t.Fatalf("Lstat(%s): %v", fn, err)
- } else if fi.Size() != 373 {
- t.Errorf("got %d, want size 373", fi.Size())
- }
-
- if err := os.Remove(configName); err != nil {
- t.Fatalf("Delete(%s): %v", configName, err)
- }
-
- if fi, err := os.Lstat(wsDir); err == nil {
- t.Errorf("Lstat(%s): got %v, want error", wsDir, fi)
- }
-}
-
-func TestMultiManifestFSManifestDir(t *testing.T) {
- fix, err := newTestFixture()
- if err != nil {
- t.Fatalf("newTestFixture: %v", err)
- }
- defer fix.cleanup()
-
- mfDir := filepath.Join(fix.dir, "manifests")
- if err := os.MkdirAll(mfDir, 0755); err != nil {
- t.Fatalf("MkdirAll: %v", err)
- }
-
- xmlFile := filepath.Join(mfDir, "ws")
- if err := ioutil.WriteFile(xmlFile, []byte(testManifestXML), 0644); err != nil {
- t.Errorf("WriteFile(%s): %v", xmlFile, err)
- }
-
- opts := MultiManifestFSOptions{
- ManifestDir: mfDir,
- }
- fs := NewMultiManifestFS(fix.service, fix.cache, opts)
-
- if err := fix.mount(fs); err != nil {
- t.Fatalf("mount: %v", err)
- }
-
- wsDir := filepath.Join(fix.mntDir, "ws")
- if _, err := os.Lstat(wsDir); err != nil {
- t.Fatalf("Lstat(%s): %v", wsDir, err)
- }
-
- if err := os.Remove(filepath.Join(fix.mntDir, "config", "ws")); err != nil {
- t.Fatalf("Remove(config link): %v", err)
- }
-
- if fi, err := os.Lstat(filepath.Join(mfDir, "ws")); err == nil {
- t.Errorf("'ws' still in manifest dir: %v", fi)
- }
-
- f, err := ioutil.TempFile("", "")
- if err != nil {
- t.Fatalf("TempFile: %v", err)
- }
- if err := ioutil.WriteFile(f.Name(), []byte(testManifestXML), 0644); err != nil {
- t.Errorf("WriteFile(%s): %v", xmlFile, err)
- }
-
- configName := filepath.Join(fix.mntDir, "config", "ws2")
- if err := os.Symlink(f.Name(), configName); err != nil {
- t.Fatalf("Symlink(%s): %v", xmlFile, err)
- }
-
- // XML file appears again.
- xmlFile = filepath.Join(mfDir, "ws2")
- if _, err := os.Stat(xmlFile); err != nil {
- t.Errorf("Stat(%s): %v", xmlFile, err)
- }
-}
diff --git a/fs/multimanifestfs.go b/fs/multimanifestfs.go
deleted file mode 100644
index f9fa084..0000000
--- a/fs/multimanifestfs.go
+++ /dev/null
@@ -1,222 +0,0 @@
-// Copyright 2016 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package fs
-
-import (
- "io/ioutil"
- "log"
- "os"
- "path/filepath"
- "sync"
- "syscall"
-
- "github.com/google/slothfs/cache"
- "github.com/google/slothfs/gitiles"
- "github.com/google/slothfs/manifest"
- "github.com/hanwen/go-fuse/fuse"
- "github.com/hanwen/go-fuse/fuse/nodefs"
-)
-
-type multiManifestFSRoot struct {
- nodefs.Node
- nodeCache *nodeCache
- cache *cache.Cache
- fsConn *nodefs.FileSystemConnector
- options MultiManifestFSOptions
- gitiles *gitiles.Service
-}
-
-func (r *multiManifestFSRoot) StatFs() *fuse.StatfsOut {
- var s syscall.Statfs_t
- err := syscall.Statfs(r.cache.Root(), &s)
- if err == nil {
- out := &fuse.StatfsOut{}
- out.FromStatfsT(&s)
- return out
- }
- return nil
-}
-
-func (c *configNode) configureWorkspaces() error {
- if c.root.options.ManifestDir == "" {
- return nil
- }
- fs, err := filepath.Glob(filepath.Join(c.root.options.ManifestDir, "*"))
- if err != nil || len(fs) == 0 {
- return err
- }
-
- log.Println("configuring workspaces...")
- var wg sync.WaitGroup
- wg.Add(len(fs))
- for _, f := range fs {
- go func(n string) {
- _, code := c.Symlink(filepath.Base(n), n, nil)
- log.Printf("manifest %s: %v", n, code)
- wg.Done()
- }(f)
- }
- wg.Wait()
-
- return nil
-}
-
-func (r *multiManifestFSRoot) OnMount(fsConn *nodefs.FileSystemConnector) {
- r.fsConn = fsConn
-
- cfg := &configNode{
- Node: nodefs.NewDefaultNode(),
- root: r,
- }
- r.Inode().NewChild("config", true, cfg)
-
- if err := cfg.configureWorkspaces(); err != nil {
- log.Printf("configureWorkspaces: %v", err)
- }
-}
-
-func (c *configNode) Deletable() bool { return false }
-
-func NewMultiManifestFS(service *gitiles.Service, c *cache.Cache, options MultiManifestFSOptions) *multiManifestFSRoot {
- r := &multiManifestFSRoot{
- Node: nodefs.NewDefaultNode(),
- nodeCache: newNodeCache(),
- cache: c,
- options: options,
- gitiles: service,
- }
- return r
-}
-
-func (r *multiManifestFSRoot) Deletable() bool { return false }
-
-func (r *multiManifestFSRoot) GetXAttr(attribute string, context *fuse.Context) (data []byte, code fuse.Status) {
- return nil, fuse.ENODATA
-}
-
-type configNode struct {
- nodefs.Node
- root *multiManifestFSRoot
-}
-
-type configEntryNode struct {
- nodefs.Node
- link []byte
-}
-
-func (c *configEntryNode) GetAttr(out *fuse.Attr, f nodefs.File, ctx *fuse.Context) fuse.Status {
- out.Mode = fuse.S_IFLNK
- return fuse.OK
-}
-
-func (c *configEntryNode) Readlink(ctx *fuse.Context) ([]byte, fuse.Status) {
- return c.link, fuse.OK
-}
-
-func (c *configEntryNode) Deletable() bool { return false }
-
-func (c *configNode) Unlink(name string, ctx *fuse.Context) fuse.Status {
- child := c.root.Inode().RmChild(name)
- if child == nil {
- return fuse.ENOENT
- }
-
- // Notify the kernel this part of the tree disappeared.
- c.root.fsConn.DeleteNotify(c.root.Inode(), child, name)
-
- c.Inode().RmChild(name)
-
- // No need to notify for the removed symlink. Since we're in
- // the Unlink method, will VFS already knows about the
- // deletion once we return OK.
-
- if dir := c.root.options.ManifestDir; dir != "" {
- os.Remove(filepath.Join(dir, name))
- }
-
- return fuse.OK
-}
-
-func (c *configNode) Symlink(name, content string, ctx *fuse.Context) (*nodefs.Inode, fuse.Status) {
- mfBytes, err := ioutil.ReadFile(content)
- if err != nil {
- return nil, fuse.ToStatus(err)
- }
-
- mf, err := manifest.Parse(mfBytes)
- if err != nil {
- log.Printf("Parse(%s): %v", content, err)
- return nil, fuse.EINVAL
- }
-
- options := ManifestOptions{
- Manifest: mf,
- RepoCloneOption: c.root.options.RepoCloneOption,
- FileCloneOption: c.root.options.FileCloneOption,
- }
-
- fs, err := NewManifestFS(c.root.gitiles, c.root.cache, options)
- if err != nil {
- log.Printf("NewManifestFS(%s): %v", string(content), err)
- return nil, fuse.EIO
- }
- fs.(*manifestFSRoot).nodeCache = c.root.nodeCache
-
- child := c.root.Inode().NewChild(name, true, fs)
- if child == nil {
- // TODO(hanwen): can this ever happen?
- return nil, fuse.EINVAL
- }
-
- config := c.Inode().NewChild(name, false, &configEntryNode{
- Node: nodefs.NewDefaultNode(),
- // This is sneaky, but it appears to work.
- link: []byte(filepath.Join("..", name, ".slothfs", "manifest.xml")),
- })
-
- if err := fs.(*manifestFSRoot).onMount(c.root.fsConn); err != nil {
- log.Printf("onMount(%s): %v", name, err)
- for k := range child.Children() {
- child.RmChild(k)
- }
-
- child.NewChild("ERROR", false, &dataNode{nodefs.NewDefaultNode(), []byte(err.Error())})
- } else {
- if dir := c.root.options.ManifestDir; dir != "" {
- for {
- f, err := ioutil.TempFile(dir, "")
- if err != nil {
- break
- }
-
- _, err = f.Write(mfBytes)
- if err != nil {
- break
- }
-
- if err := f.Close(); err != nil {
- break
- }
-
- os.Rename(f.Name(), filepath.Join(dir, name))
- break
- }
- }
- }
-
- c.root.fsConn.EntryNotify(c.root.Inode(), name)
-
- return config, fuse.OK
-}