Skip to content

Commit b06e802

Browse files
committed
Check mountpoint before doing slow operations
This change also has a major refactor of the nr library in part due to the improvements in the SDL API. This refactor also cleans up debug statements and removes logging from the nr library altogether.
1 parent a764a2c commit b06e802

15 files changed

+338
-290
lines changed

.travis.yml

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
language: go
22
os: osx
33
before_deploy:
4-
- cd $HOME/gopath/src/github.com/mitre/fusera/cmd/fusera
4+
- cd $HOME/gopath/src/github.com/mitre/fusera
55
- go build -o fusera-darwin-amd64
66
- env GOOS=linux GOARCH=amd64 go build -o fusera-linux-amd64
7-
- cd $HOME/gopath/src/github.com/mitre/fusera/cmd/sracp
7+
- cd $HOME/gopath/src/github.com/mitre/fusera/sracp
88
- go build -o sracp-darwin-amd64
99
- env GOOS=linux GOARCH=amd64 go build -o sracp-linux-amd64
1010
deploy:
@@ -14,9 +14,9 @@ deploy:
1414
skip_cleanup: true
1515
overwrite: true
1616
file:
17-
- $HOME/gopath/src/github.com/mitre/fusera/cmd/fusera/fusera-linux-amd64
18-
- $HOME/gopath/src/github.com/mitre/fusera/cmd/fusera/fusera-darwin-amd64
19-
- $HOME/gopath/src/github.com/mitre/fusera/cmd/sracp/sracp-linux-amd64
20-
- $HOME/gopath/src/github.com/mitre/fusera/cmd/sracp/sracp-darwin-amd64
17+
- $HOME/gopath/src/github.com/mitre/fusera/fusera-linux-amd64
18+
- $HOME/gopath/src/github.com/mitre/fusera/fusera-darwin-amd64
19+
- $HOME/gopath/src/github.com/mitre/fusera/sracp/sracp-linux-amd64
20+
- $HOME/gopath/src/github.com/mitre/fusera/sracp/sracp-darwin-amd64
2121
on:
2222
tags: true

cmd/errors.go

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Modifications Copyright 2018 The MITRE Corporation
2+
// Authors: Matthew Bianchi
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package cmd
17+
18+
import (
19+
"fmt"
20+
"strings"
21+
22+
"github.com/mattrbianchi/twig"
23+
)
24+
25+
func prettyPrintError(err error) {
26+
// Accession errors
27+
if err.Error() == "no accessions provided" {
28+
twig.Debug(err)
29+
fmt.Println("No accessions provided: Fusera needs accession(s) in order to know what files to provide in its file system.")
30+
}
31+
if strings.Contains(err.Error(), "couldn't open cart file") {
32+
twig.Debug(err)
33+
fmt.Println("Bad cart file or path: Fusera interpreted the accession flag as a path to a cart file, but could not open the file at the path specified. Make sure the path leads to a valid cart file and that you have permissions to read that file. If you do and you're still getting this message, run with debug enabled for a more detailed error message and contact your IT administrator with its contents.")
34+
}
35+
if strings.Contains(err.Error(), "cart file was empty") {
36+
twig.Debug(err)
37+
fmt.Println("Bad cart file: Fusera interpreted the accession flag as a path to a cart file, but the file seems empty. Make sure the path leads to a valid cart file that has properly formatted contents and isn't corrupted. If you're still getting this message after assuring the file is correct, run with debug enabled for a more detailed error message and contact your IT administrator with its contents.")
38+
}
39+
40+
// Location errors
41+
if err.Error() == "no location provided" {
42+
twig.Debug(err)
43+
fmt.Println("No location provided: A location was not provided so Fusera attempted to resolve the location itself and could not do so. This feature is only supported when Fusera is running on Amazon or Google's cloud platforms. If you are running on a server in either of these two cloud platforms and are still getting this message, run fusera with debug enabled for a more detailed error message and contact your IT administrator with its contents.")
44+
}
45+
46+
// Ngc errors
47+
if strings.Contains(err.Error(), "couldn't open ngc file") {
48+
twig.Debug(err)
49+
fmt.Println("Bad ngc file path: Fusera tried to read the cart file at the path specified and couldn't. Make sure the path leads to a valid ngc file and that you have permissions to read that file. If you do and you're still getting this message, run with debug enabled for a more detailed error message and contact your IT administrator with its contents.")
50+
}
51+
52+
// Filetype errors
53+
if err.Error() == "filetype was empty" {
54+
twig.Debug(err)
55+
fmt.Println("Filetype was empty: Fusera tried to parse the list of filetypes given but couldn't find anything. Example of a well formatted list to the filetype flag: -f \"bai,crai,cram\".")
56+
}
57+
58+
// Mount errors
59+
if strings.Contains(err.Error(), "mountpoint doesn't exist") {
60+
twig.Debug(err)
61+
fmt.Println("Mountpoint doesn't exist: It seems like the directory you want to mount to does not exist. Please create the directory before trying again.")
62+
}
63+
if strings.Contains(err.Error(), "no such file or directory") {
64+
twig.Debug(err)
65+
fmt.Println("Failed to mount: It seems like the directory you want to mount to does not exist or you do not have correct permissions to access it. Please create the directory or correct the permissions on it before trying again.")
66+
}
67+
if strings.Contains(err.Error(), "EOF") {
68+
twig.Debug(err)
69+
fmt.Println("Failed to mount: It seems like the directory you want to mount to is already mounted by another instance of Fusera or another device. Choose another directory or try using the unmount command to unmount the other instance of Fusera before trying again. Be considerate of the unmount command, if anything is using Fusera while attempting to unmount, the unmount attempt will fail and that instance of Fusera will keep running.")
70+
}
71+
72+
// API errors
73+
if strings.Contains(err.Error(), "failed to locate accessions") {
74+
twig.Debug(err)
75+
fmt.Println("Failed to locate accessions: It seems that Fusera has encountered an error while using the SRA Data Locator API to determine the file locations for accessions. This is an issue between Fusera and the API. In order to get more information, run Fusera with debug enabled and contact your IT administrator with its contents.")
76+
}
77+
78+
// Fatal errors
79+
if strings.Contains(err.Error(), "FATAL") {
80+
twig.Debug(err)
81+
fmt.Println("Fatal: It seems like fusera encountered an internal issue, please run fusera with debug enabled for a more detailed error message and contact your IT administrator with its contents.")
82+
}
83+
}

cmd/flags.go

-34
This file was deleted.

cmd/mount.go

+29-38
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,15 @@
1414
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1515
// See the License for the specific language governing permissions and
1616
// limitations under the License.
17+
1718
package cmd
1819

1920
import (
2021
"context"
21-
"fmt"
2222
"os"
2323
"os/signal"
2424
"os/user"
2525
"strconv"
26-
"strings"
2726
"syscall"
2827

2928
"github.com/mattrbianchi/twig"
@@ -97,14 +96,11 @@ var mountCmd = &cobra.Command{
9796
RunE: mount,
9897
}
9998

99+
// mount locates the files for each accession its given with the SDL API
100+
// and then mounts a FUSE system.
100101
func mount(cmd *cobra.Command, args []string) (err error) {
101102
setConfig()
102-
twig.Debug("got mount command")
103-
twig.Debug("args:")
104-
twig.Debug(args)
105103
foldEnvVarsIntoFlagValues()
106-
twig.Debug("location: " + location)
107-
twig.Debug("accessions: " + accession)
108104
var ngc []byte
109105
if ngcpath != "" {
110106
ngc, err = flags.ResolveNgcFile(ngcpath)
@@ -113,29 +109,35 @@ func mount(cmd *cobra.Command, args []string) (err error) {
113109
}
114110
}
115111
if accession == "" {
116-
return errors.New("No accessions provided: Fusera needs a list of accessions in order to know what files to provide in its file system.")
112+
return errors.New("no accessions provided")
117113
}
118-
// Now resolveAccession's value
119114
accs, err := flags.ResolveAccession(accession)
120115
if err != nil {
121116
return err
122117
}
123-
// Validate mount point
124-
// Do mount stuff
125-
118+
var types map[string]bool
119+
if filetype != "" {
120+
types, err = flags.ResolveFileType(filetype)
121+
if err != nil {
122+
return err
123+
}
124+
}
125+
// Validate the mount point before trying to mount to it.
126+
// So it must exist
127+
mountpoint := args[0]
128+
if !flags.FileExists(mountpoint) {
129+
return errors.New("mountpoint doesn't exist")
130+
}
131+
// So it must be readable
132+
if !flags.HavePermissions(mountpoint) {
133+
return errors.New("incorrect permissions for mountpoint")
134+
}
126135
// Location takes longest if there's a failure, so validate it last.
127136
if location == "" {
128137
location, err = flags.ResolveLocation()
129138
if err != nil {
130139
twig.Debug(err)
131-
return errors.New("No location: A location was not provided so Fusera attempted to resolve the location itself. This feature is only supported when Fusera is running on Amazon or Google's cloud platforms.")
132-
}
133-
}
134-
var types map[string]bool
135-
if filetype != "" {
136-
types, err = flags.ResolveFileType(filetype)
137-
if err != nil {
138-
return errors.New("Seems like something was wrong with the format of the filetype flag.")
140+
return errors.New("no location provided")
139141
}
140142
}
141143
uid, gid := myUserAndGroup()
@@ -146,41 +148,30 @@ func mount(cmd *cobra.Command, args []string) (err error) {
146148
Filetypes: types,
147149
Eager: eager,
148150

149-
ApiEndpoint: endpoint,
151+
APIEndpoint: endpoint,
150152
AwsBatch: awsBatch,
151153
GcpBatch: gcpBatch,
152154

153155
DirMode: 0555,
154156
FileMode: 0444,
155-
Uid: uint32(uid),
156-
Gid: uint32(gid),
157+
UID: uint32(uid),
158+
GID: uint32(gid),
157159
// TODO: won't need.
158160
MountOptions: make(map[string]string),
159-
MountPoint: args[0],
160-
MountPointArg: args[0],
161+
MountPoint: mountpoint,
162+
MountPointArg: mountpoint,
161163
}
162164
fs, mfs, err := fuseralib.Mount(context.Background(), opt)
163165
if err != nil {
164-
var msg string
165-
if strings.Contains(err.Error(), "no such file or directory") {
166-
msg = "Fusera failed to mount the file system.\nIt seems like the directory you want to mount to does not exist or you do not have correct permissions to access it. Please create the directory or correct the permissions on it before trying again."
167-
}
168-
if strings.Contains(err.Error(), "EOF") {
169-
msg = "Fusera failed to mount the file system.\nIt seems like the directory you want to mount to is already mounted by fusera or another device. Choose another directory or try using the unmount command before trying again. Be considerate of the unmount command, if anything is using the device mounted while attempting to unmount, it will fail."
170-
}
171-
twig.Debugf("%+v\n", err)
172-
return errors.New(msg)
166+
return err
173167
}
174-
twig.Debug("File system has been successfully mounted.")
175168
// Let the user unmount with Ctrl-C
176169
registerSIGINTHandler(fs, opt.MountPoint)
177170

178171
// Wait for the file system to be unmounted.
179172
err = mfs.Join(context.Background())
180173
if err != nil {
181-
fmt.Println("fusera encountered an internal issue, please rerun with the --debug flag to learn more.")
182-
twig.Debugf("FATAL: MountedFileSystem.Join: %+#v\n", err)
183-
os.Exit(1)
174+
return errors.Wrap(err, "FATAL")
184175
}
185176

186177
return nil

cmd/root.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
// Copyright 2015 - 2017 Ka-Hing Cheung
2-
// Copyright 2015 - 2017 Google Inc. All Rights Reserved.
31
// Modifications Copyright 2018 The MITRE Corporation
4-
// Authors: Ka-Hing Cheung, Matthew Bianchi
2+
// Authors: Matthew Bianchi
53
//
64
// Licensed under the Apache License, Version 2.0 (the "License");
75
// you may not use this file except in compliance with the License.
@@ -14,10 +12,10 @@
1412
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1513
// See the License for the specific language governing permissions and
1614
// limitations under the License.
15+
1716
package cmd
1817

1918
import (
20-
"fmt"
2119
"os"
2220

2321
"github.com/mattrbianchi/twig"
@@ -47,9 +45,11 @@ var rootCmd = &cobra.Command{
4745
Version: version,
4846
}
4947

48+
// Execute runs the main command of fusera, which has no action of its own,
49+
// so it evaluates which subcommand should be executed.
5050
func Execute() {
5151
if err := rootCmd.Execute(); err != nil {
52-
fmt.Println(err)
52+
prettyPrintError(err)
5353
os.Exit(1)
5454
}
5555
}

flags/flags.go

+14-9
Original file line numberDiff line numberDiff line change
@@ -69,25 +69,20 @@ func ResolveAccession(acc string) (map[string]bool, error) {
6969
}
7070
}
7171
if empty {
72-
return nil, errors.Errorf("No accessions were found in the content given to the --accession flag. --accession: %s.", acc)
72+
return nil, errors.New("cart file was empty")
7373
}
7474

7575
return accessions, nil
7676
}
7777

78+
// TODO: should be a private function
7879
func ParseAccessions(r rune) bool {
7980
return r == '\n' || r == '\t' || r == ',' || r == ' '
8081
}
8182

82-
func FileExists(path string) bool {
83-
_, err := os.Stat(path)
84-
return !os.IsNotExist(err)
85-
}
86-
8783
// Deduce whether path is on s3 or local.
8884
// Either way, read all of the file into a byte slice.
8985
func ResolveNgcFile(ngcpath string) (data []byte, err error) {
90-
// we were given a path to an ngc file. Let's read it.
9186
if strings.HasPrefix(ngcpath, "http") {
9287
// we were given a url on s3.
9388
data, err = awsutil.ReadFile(ngcpath)
@@ -107,7 +102,7 @@ func ResolveFileType(filetype string) (map[string]bool, error) {
107102
uniqTypes := make(map[string]bool)
108103
types := strings.Split(filetype, ",")
109104
if len(types) == 1 && types[0] == "" {
110-
return nil, errors.New("")
105+
return nil, errors.New("filetype was empty")
111106
}
112107
if len(types) > 0 {
113108
for _, t := range types {
@@ -117,7 +112,17 @@ func ResolveFileType(filetype string) (map[string]bool, error) {
117112
}
118113
return uniqTypes, nil
119114
}
120-
return nil, errors.New("")
115+
return nil, errors.New("filetype was empty")
116+
}
117+
118+
func FileExists(path string) bool {
119+
_, err := os.Stat(path)
120+
return !os.IsNotExist(err)
121+
}
122+
123+
func HavePermissions(path string) bool {
124+
_, err := os.Stat(path)
125+
return !os.IsPermission(err)
121126
}
122127

123128
func ResolveString(name string, value *string) {

fuseralib/dir.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,15 @@ func NewDirHandle(inode *Inode) (dh *DirHandle) {
6565

6666
func (inode *Inode) OpenDir() (dh *DirHandle) {
6767
parent := inode.Parent
68-
if parent != nil && inode.fs.opt.TypeCacheTTL != 0 {
68+
if parent != nil {
6969
parent.mu.Lock()
7070
defer parent.mu.Unlock()
7171

7272
num := len(parent.dir.Children)
7373

7474
if parent.dir.lastOpenDir == nil && num > 0 && *parent.dir.Children[0].Name == *inode.Name {
7575
if parent.dir.seqOpenDirScore < 255 {
76-
parent.dir.seqOpenDirScore += 1
76+
parent.dir.seqOpenDirScore++
7777
}
7878
// 2.1) if I open a/a, a/'s score is now 2
7979
// ie: handle the depth first search case
@@ -134,7 +134,6 @@ func (p sortedDirents) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
134134

135135
// LOCKS_REQUIRED(dh.mu)
136136
func (dh *DirHandle) ReadDir(offset fuseops.DirOffset) (en *DirHandleEntry, err error) {
137-
twig.Debug("dir.go/ReadDir called")
138137
// If the request is for offset zero, we assume that either this is the first
139138
// call or rewinddir has been called. Reset state.
140139
if offset == 0 {

0 commit comments

Comments
 (0)