veyron/services/mgmt/binary/impl: code refactoring.
Add stats.go for stats object, and fs_utils.go for common functions
that manipulate the filesystem for storing/retrieving binary parts.
Change-Id: I394d62af1deb0d23656fab3270d0b7f19a3b47d4
diff --git a/services/mgmt/binary/impl/dispatcher.go b/services/mgmt/binary/impl/dispatcher.go
index 465b0a6..1b80de9 100644
--- a/services/mgmt/binary/impl/dispatcher.go
+++ b/services/mgmt/binary/impl/dispatcher.go
@@ -1,13 +1,6 @@
package impl
import (
- "crypto/md5"
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
-
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/services/mgmt/repository"
@@ -24,31 +17,6 @@
state *state
}
-// TODO(caprita): Move this together with state into a new file, state.go.
-
-// 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) {
- 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)
- }
- path := filepath.Join(root, VersionFile)
- output, err := ioutil.ReadFile(path)
- if err != nil {
- return nil, fmt.Errorf("ReadFile(%v) failed: %v", path, err)
- }
- if expected, got := Version, strings.TrimSpace(string(output)); expected != got {
- return nil, fmt.Errorf("Unexpected version: expected %v, got %v", expected, got)
- }
- return &state{
- depth: depth,
- root: root,
- }, nil
-}
-
// NewDispatcher is the dispatcher factory.
func NewDispatcher(state *state, authorizer security.Authorizer) ipc.Dispatcher {
return &dispatcher{
diff --git a/services/mgmt/binary/impl/fs_utils.go b/services/mgmt/binary/impl/fs_utils.go
new file mode 100644
index 0000000..bc4c818
--- /dev/null
+++ b/services/mgmt/binary/impl/fs_utils.go
@@ -0,0 +1,80 @@
+package impl
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strconv"
+
+ "veyron.io/veyron/veyron2/vlog"
+)
+
+const (
+ checksum = "checksum"
+ data = "data"
+ lock = "lock"
+)
+
+// checksumExists checks whether the given part path is valid and
+// contains a checksum. The implementation uses the existence of
+// the path dir to determine whether the part is valid, and the
+// existence of checksum to determine whether the binary part
+// exists.
+func checksumExists(path string) error {
+ switch _, err := os.Stat(path); {
+ case os.IsNotExist(err):
+ return errInvalidPart
+ case err != nil:
+ vlog.Errorf("Stat(%v) failed: %v", path, err)
+ return errOperationFailed
+ }
+ checksumFile := filepath.Join(path, checksum)
+ _, err := os.Stat(checksumFile)
+ switch {
+ case os.IsNotExist(err):
+ return errNotFound
+ case err != nil:
+ vlog.Errorf("Stat(%v) failed: %v", checksumFile, err)
+ return errOperationFailed
+ default:
+ return nil
+ }
+}
+
+// generatePartPath generates a path for the given binary part.
+func (i *invoker) generatePartPath(part int) string {
+ return generatePartPath(i.path, part)
+}
+
+func generatePartPath(dir string, part int) string {
+ return filepath.Join(dir, fmt.Sprintf("%d", part))
+}
+
+// getParts returns a collection of paths to the parts of the binary.
+func getParts(path string) ([]string, error) {
+ infos, err := ioutil.ReadDir(path)
+ if err != nil {
+ vlog.Errorf("ReadDir(%v) failed: %v", path, err)
+ return []string{}, errOperationFailed
+ }
+ result := make([]string, len(infos))
+ for _, info := range infos {
+ if info.IsDir() {
+ partName := info.Name()
+ idx, err := strconv.Atoi(partName)
+ if err != nil {
+ vlog.Errorf("Atoi(%v) failed: %v", partName, err)
+ return []string{}, errOperationFailed
+ }
+ if idx < 0 || idx >= len(infos) || result[idx] != "" {
+ return []string{}, errOperationFailed
+ }
+ result[idx] = filepath.Join(path, partName)
+ } else {
+ // The only entries should correspond to the part dirs.
+ return []string{}, errOperationFailed
+ }
+ }
+ return result, nil
+}
diff --git a/services/mgmt/binary/impl/http.go b/services/mgmt/binary/impl/http.go
index 2c189b3..df6ec69 100644
--- a/services/mgmt/binary/impl/http.go
+++ b/services/mgmt/binary/impl/http.go
@@ -29,7 +29,7 @@
func (r httpRoot) Open(name string) (http.File, error) {
name = strings.TrimPrefix(name, "/")
vlog.Infof("HTTP handler opening %s", name)
- parts, err := getParts(dir(name, r.state))
+ parts, err := getParts(r.state.dir(name))
if err != nil {
return nil, err
}
diff --git a/services/mgmt/binary/impl/invoker.go b/services/mgmt/binary/impl/invoker.go
index 893abcf..182b128 100644
--- a/services/mgmt/binary/impl/invoker.go
+++ b/services/mgmt/binary/impl/invoker.go
@@ -21,12 +21,10 @@
import (
"crypto/md5"
"encoding/hex"
- "fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
- "strconv"
"syscall"
"veyron.io/veyron/veyron2/ipc"
@@ -36,30 +34,6 @@
"veyron.io/veyron/veyron2/vlog"
)
-// state holds the state shared across different binary repository
-// invocations.
-type state struct {
- // depth determines the depth of the directory hierarchy that the
- // binary repository uses to organize binaries in the local file
- // system. There is a trade-off here: smaller values lead to faster
- // access, while higher values allow the performance to scale to
- // larger collections of binaries. The number should be a value
- // between 0 and (md5.Size - 1).
- //
- // Note that the cardinality of each level (except the leaf level)
- // is at most 256. If you expect to have X total binary items, and
- // you want to limit directories to at most Y entries (because of
- // filesystem limitations), then you should set depth to at least
- // log_256(X/Y). For example, using hierarchyDepth = 3 with a local
- // filesystem that can handle up to 1,000 entries per directory
- // before its performance degrades allows the binary repository to
- // store 16B objects.
- depth int
- // root identifies the local filesystem directory in which the
- // binary repository stores its objects.
- root string
-}
-
// invoker holds the state of a binary repository invocation.
type invoker struct {
// path is the local filesystem path to the object identified by the
@@ -73,12 +47,6 @@
suffix string
}
-const (
- checksum = "checksum"
- data = "data"
- lock = "lock"
-)
-
var (
errExists = verror.Existsf("binary already exists")
errNotFound = verror.NoExistf("binary not found")
@@ -95,22 +63,10 @@
Size: binary.MissingSize,
}
-// dir generates the local filesystem path for the binary identified by suffix.
-func dir(suffix string, state *state) string {
- h := md5.New()
- h.Write([]byte(suffix))
- hash := hex.EncodeToString(h.Sum(nil))
- dir := ""
- for j := 0; j < state.depth; j++ {
- dir = filepath.Join(dir, hash[j*2:(j+1)*2])
- }
- return filepath.Join(state.root, dir, hash)
-}
-
// newInvoker is the invoker factory.
func newInvoker(state *state, suffix string) *invoker {
return &invoker{
- path: dir(suffix, state),
+ path: state.dir(suffix),
state: state,
suffix: suffix,
}
@@ -120,69 +76,6 @@
const bufferLength = 4096
-// checksumExists checks whether the given part path is valid and
-// contains a checksum. The implementation uses the existence of
-// the path dir to determine whether the part is valid, and the
-// existence of checksum to determine whether the binary part
-// exists.
-func checksumExists(path string) error {
- switch _, err := os.Stat(path); {
- case os.IsNotExist(err):
- return errInvalidPart
- case err != nil:
- vlog.Errorf("Stat(%v) failed: %v", path, err)
- return errOperationFailed
- }
- checksumFile := filepath.Join(path, checksum)
- _, err := os.Stat(checksumFile)
- switch {
- case os.IsNotExist(err):
- return errNotFound
- case err != nil:
- vlog.Errorf("Stat(%v) failed: %v", checksumFile, err)
- return errOperationFailed
- default:
- return nil
- }
-}
-
-// generatePartPath generates a path for the given binary part.
-func (i *invoker) generatePartPath(part int) string {
- return generatePartPath(i.path, part)
-}
-
-func generatePartPath(dir string, part int) string {
- return filepath.Join(dir, fmt.Sprintf("%d", part))
-}
-
-// getParts returns a collection of paths to the parts of the binary.
-func getParts(path string) ([]string, error) {
- infos, err := ioutil.ReadDir(path)
- if err != nil {
- vlog.Errorf("ReadDir(%v) failed: %v", path, err)
- return []string{}, errOperationFailed
- }
- result := make([]string, len(infos))
- for _, info := range infos {
- if info.IsDir() {
- partName := info.Name()
- idx, err := strconv.Atoi(partName)
- if err != nil {
- vlog.Errorf("Atoi(%v) failed: %v", partName, err)
- return []string{}, errOperationFailed
- }
- if idx < 0 || idx >= len(infos) || result[idx] != "" {
- return []string{}, errOperationFailed
- }
- result[idx] = filepath.Join(path, partName)
- } else {
- // The only entries should correspond to the part dirs.
- return []string{}, errOperationFailed
- }
- }
- return result, nil
-}
-
func (i *invoker) Create(_ ipc.ServerContext, nparts int32) error {
vlog.Infof("%v.Create(%v)", i.suffix, nparts)
if nparts < 1 {
diff --git a/services/mgmt/binary/impl/state.go b/services/mgmt/binary/impl/state.go
new file mode 100644
index 0000000..cf6c3b1
--- /dev/null
+++ b/services/mgmt/binary/impl/state.go
@@ -0,0 +1,70 @@
+package impl
+
+import (
+ "crypto/md5"
+ "encoding/hex"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+// state holds the state shared across different binary repository
+// invocations.
+type state struct {
+ // depth determines the depth of the directory hierarchy that the
+ // binary repository uses to organize binaries in the local file
+ // system. There is a trade-off here: smaller values lead to faster
+ // access, while higher values allow the performance to scale to
+ // larger collections of binaries. The number should be a value
+ // between 0 and (md5.Size - 1).
+ //
+ // Note that the cardinality of each level (except the leaf level)
+ // is at most 256. If you expect to have X total binary items, and
+ // you want to limit directories to at most Y entries (because of
+ // filesystem limitations), then you should set depth to at least
+ // log_256(X/Y). For example, using hierarchyDepth = 3 with a local
+ // filesystem that can handle up to 1,000 entries per directory
+ // before its performance degrades allows the binary repository to
+ // store 16B objects.
+ depth int
+ // root identifies the local filesystem directory in which the
+ // binary repository stores its objects.
+ root 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) {
+ 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)
+ }
+ path := filepath.Join(root, VersionFile)
+ output, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, fmt.Errorf("ReadFile(%v) failed: %v", path, err)
+ }
+ if expected, got := Version, strings.TrimSpace(string(output)); expected != got {
+ return nil, fmt.Errorf("Unexpected version: expected %v, got %v", expected, got)
+ }
+ return &state{
+ depth: depth,
+ root: root,
+ }, nil
+}
+
+// dir generates the local filesystem path for the binary identified by suffix.
+func (s *state) dir(suffix string) string {
+ h := md5.New()
+ h.Write([]byte(suffix))
+ hash := hex.EncodeToString(h.Sum(nil))
+ dir := ""
+ for j := 0; j < s.depth; j++ {
+ dir = filepath.Join(dir, hash[j*2:(j+1)*2])
+ }
+ return filepath.Join(s.root, dir, hash)
+}