Initial checkin: simple Keep server handling block reads only. (refs #2291)
[arvados.git] / services / keep / keep.go
1 package main
2
3 import (
4         "bufio"
5         "fmt"
6         "github.com/gorilla/mux"
7         "io"
8         "log"
9         "net/http"
10         "os"
11         "strings"
12 )
13
14 const DEFAULT_PORT = 25107
15
16 var KeepVolumes []string
17
18 func main() {
19         // Look for local keep volumes.
20         KeepVolumes = FindKeepVolumes()
21         if len(KeepVolumes) == 0 {
22                 log.Fatal("could not find any keep volumes")
23         }
24         for _, v := range KeepVolumes {
25                 log.Println("keep volume:", v)
26         }
27
28         // Set up REST handlers.
29         rest := mux.NewRouter()
30         rest.HandleFunc("/{hash}", GetBlock).Methods("GET")
31         http.Handle("/", rest)
32
33         port := fmt.Sprintf(":%d", DEFAULT_PORT)
34         http.ListenAndServe(port, nil)
35 }
36
37 func GetBlock(w http.ResponseWriter, req *http.Request) {
38         hash := mux.Vars(req)["hash"]
39
40         // Attempt to read the requested hash from a keep volume.
41         for _, vol := range KeepVolumes {
42                 path := fmt.Sprintf("%s/%s/%s", vol, hash[0:3], hash)
43                 if f, err := os.Open(path); err == nil {
44                         io.Copy(w, f)
45                         break
46                 } else {
47                         log.Printf("%s: reading block %s: %s\n", vol, hash, err)
48                 }
49         }
50 }
51
52 // FindKeepVolumes
53 //     Returns a list of Keep volumes mounted on this system.
54 //
55 //     A Keep volume is a normal or tmpfs volume with a /keep
56 //     directory at the top level of the mount point.
57 //
58 func FindKeepVolumes() []string {
59         vols := make([]string, 0)
60
61         if f, err := os.Open("/proc/mounts"); err != nil {
62                 log.Fatal("could not read /proc/mounts: ", err)
63         } else {
64                 scanner := bufio.NewScanner(f)
65                 for scanner.Scan() {
66                         args := strings.Fields(scanner.Text())
67                         dev, mount := args[0], args[1]
68                         if (dev == "tmpfs" || strings.HasPrefix(dev, "/dev/")) && mount != "/" {
69                                 keep := mount + "/keep"
70                                 if st, err := os.Stat(keep); err == nil && st.IsDir() {
71                                         vols = append(vols, keep)
72                                 }
73                         }
74                 }
75                 if err := scanner.Err(); err != nil {
76                         log.Fatal(err)
77                 }
78         }
79         return vols
80 }