Chromium Code Reviews| Index: service/datastore/datastore.go |
| diff --git a/service/datastore/datastore.go b/service/datastore/datastore.go |
| index 68fae10a88300f0fe0773b21bfb45a09654af4a1..c087490e788716318d27729054ca7f01e356c411 100644 |
| --- a/service/datastore/datastore.go |
| +++ b/service/datastore/datastore.go |
| @@ -6,9 +6,17 @@ package datastore |
| import ( |
| "fmt" |
| + "io" |
| + "io/ioutil" |
| + "os" |
| + "path/filepath" |
| "reflect" |
| + "runtime" |
| + "strings" |
| "github.com/luci/luci-go/common/errors" |
| + |
| + "gopkg.in/yaml.v2" |
| ) |
| type datastoreImpl struct { |
| @@ -348,3 +356,78 @@ func (d *datastoreImpl) DeleteMulti(keys []*Key) (err error) { |
| func (d *datastoreImpl) Raw() RawInterface { |
| return d.RawInterface |
| } |
| + |
| +// ParseIndexYAML parses the contents of a index YAML file into a list of |
| +// IndexDefinitions. |
| +func ParseIndexYAML(content io.Reader) ([]*IndexDefinition, error) { |
| + serialized, err := ioutil.ReadAll(content) |
| + if err != nil { |
| + return nil, err |
| + } |
| + |
| + var m map[string][]*IndexDefinition |
| + if err := yaml.Unmarshal(serialized, &m); err != nil { |
| + return nil, err |
| + } |
| + |
| + if _, ok := m["indexes"]; !ok { |
| + return nil, fmt.Errorf("datastore: missing key `indexes`: %v", m) |
| + } |
| + return m["indexes"], nil |
| +} |
| + |
| +// getCallingTestFilePath looks up the call stack until the specified |
| +// maxStackDepth and returns the path of the first source filename ending with |
| +// `_test.go`. If no test file is found, getCallingTestFilePath returns a |
| +// non-nil error. |
| +func getCallingTestFilePath(maxStackDepth int) (string, error) { |
| + pc := make([]uintptr, maxStackDepth) |
| + n := runtime.Callers(0, pc) |
|
iannucci
2016/01/15 04:32:53
try
for _, elem := range pc[:runtime.Callers(0, p
nishanths (utexas)
2016/01/15 06:17:04
Done.
|
| + |
| + for i := 0; i < n; i += 1 { |
| + path, _ := runtime.FuncForPC(pc[i] - 1).FileLine(pc[i] - 1) |
| + if filename := filepath.Base(path); strings.HasSuffix(filename, "_test.go") { |
| + return path, nil |
| + } |
| + } |
| + |
| + return "", fmt.Errorf("datastore: failed to determine source file name") |
| +} |
| + |
| +// FindAndParseIndexYAML walks up from the directory of the calling test file |
| +// until it finds a `index.yaml` or `index.yml` file. |
| +// If an index YAML file is found, it opens and parses the file, |
| +// and returns all the indexes found. |
| +// |
| +// FindAndParseIndexYAML returns a non-nil error if the root of the drive is |
| +// reached without finding an index YAML file, or if there was |
| +// an error reading the found index YAML file. |
| +func FindAndParseIndexYAML() ([]*IndexDefinition, error) { |
| + path, err := getCallingTestFilePath(100) |
|
iannucci
2016/01/15 04:32:53
document the 100 element limit in the docstring ab
nishanths (utexas)
2016/01/15 06:17:04
Yep :) Done.
|
| + if err != nil { |
| + return nil, err |
| + } |
| + |
| + isRoot := func(currentDir string) bool { |
| + parentDir := filepath.Dir(currentDir) |
| + return os.IsPathSeparator(currentDir[len(currentDir)-1]) && os.IsPathSeparator(parentDir[len(parentDir)-1]) |
| + } |
| + |
| + currentDir := filepath.Dir(path) |
| + |
| + for { |
| + for _, filename := range []string{"index.yml", "index.yaml"} { |
| + file, err := os.Open(filepath.Join(currentDir, filename)) |
| + if err == nil { |
| + defer file.Close() |
| + return ParseIndexYAML(file) |
| + } |
| + } |
| + |
| + if isRoot(currentDir) { |
| + return nil, fmt.Errorf("datastore: failed to find index YAML file") |
| + } |
| + |
| + currentDir = filepath.Dir(currentDir) |
| + } |
| +} |