11644: Add /mounts endpoint using random mount UUIDs assigned at runtime.
authorTom Clegg <tom@curoverse.com>
Fri, 12 May 2017 21:21:00 +0000 (17:21 -0400)
committerTom Clegg <tom@curoverse.com>
Fri, 12 May 2017 21:21:00 +0000 (17:21 -0400)
services/keepstore/handlers.go
services/keepstore/volume.go

index adaaa361e96177080a9df4e2b2f1d77aac98424d..789d97b24c9ab54779e984132a327fe3ce92610b 100644 (file)
@@ -13,7 +13,6 @@ import (
        "crypto/md5"
        "encoding/json"
        "fmt"
-       "github.com/gorilla/mux"
        "io"
        "net/http"
        "os"
@@ -24,6 +23,8 @@ import (
        "sync"
        "time"
 
+       "github.com/gorilla/mux"
+
        "git.curoverse.com/arvados.git/sdk/go/httpserver"
        log "github.com/Sirupsen/logrus"
 )
@@ -59,6 +60,9 @@ func MakeRESTRouter() *router {
        // List volumes: path, device number, bytes used/avail.
        rest.HandleFunc(`/status.json`, rtr.StatusHandler).Methods("GET", "HEAD")
 
+       // List mounts: UUID, readonly, tier, device ID, ...
+       rest.HandleFunc(`/mounts`, rtr.Mounts).Methods("GET")
+
        // Replace the current pull queue.
        rest.HandleFunc(`/pull`, PullHandler).Methods("PUT")
 
@@ -249,6 +253,14 @@ func IndexHandler(resp http.ResponseWriter, req *http.Request) {
        resp.Write([]byte{'\n'})
 }
 
+// Mounts responds to "GET /mounts" requests.
+func (rtr *router) Mounts(resp http.ResponseWriter, req *http.Request) {
+       err := json.NewEncoder(resp).Encode(KeepVM.Mounts())
+       if err != nil {
+               http.Error(resp, err.Error(), http.StatusInternalServerError)
+       }
+}
+
 // PoolStatus struct
 type PoolStatus struct {
        Alloc uint64 `json:"BytesAllocated"`
index 778f27fcde87cbc324a246aec571b3fe7a5c2b8a..7e001a2470943e439e24cd6a149a4410754982ee 100644 (file)
@@ -2,7 +2,9 @@ package main
 
 import (
        "context"
+       "crypto/rand"
        "io"
+       "math/big"
        "sync/atomic"
        "time"
 )
@@ -241,6 +243,9 @@ type VolumeWithExamples interface {
 // A VolumeManager tells callers which volumes can read, which volumes
 // can write, and on which volume the next write should be attempted.
 type VolumeManager interface {
+       // Mounts returns all mounts (volume attachments).
+       Mounts() []*VolumeMount
+
        // AllReadable returns all volumes.
        AllReadable() []Volume
 
@@ -263,10 +268,35 @@ type VolumeManager interface {
        Close()
 }
 
+// A VolumeMount is an attachment of a Volume to a VolumeManager.
+type VolumeMount struct {
+       UUID     string
+       DeviceID string
+       ReadOnly bool
+       Tier     int
+       volume   Volume
+}
+
+// Generate a UUID the way API server would for a "KeepVolumeMount"
+// object.
+func (*VolumeMount) generateUUID() string {
+       var max big.Int
+       _, ok := max.SetString("zzzzzzzzzzzzzzz", 36)
+       if !ok {
+               panic("big.Int parse failed")
+       }
+       r, err := rand.Int(rand.Reader, &max)
+       if err != nil {
+               panic(err)
+       }
+       return "zzzzz-ivpuk-" + r.Text(36)
+}
+
 // RRVolumeManager is a round-robin VolumeManager: the Nth call to
 // NextWritable returns the (N % len(writables))th writable Volume
 // (where writables are all Volumes v where v.Writable()==true).
 type RRVolumeManager struct {
+       mounts    []*VolumeMount
        readables []Volume
        writables []Volume
        counter   uint32
@@ -279,7 +309,20 @@ func MakeRRVolumeManager(volumes []Volume) *RRVolumeManager {
                iostats: make(map[Volume]*ioStats),
        }
        for _, v := range volumes {
+               mnt := &VolumeMount{
+                       UUID:     (*VolumeMount)(nil).generateUUID(),
+                       DeviceID: "",
+                       ReadOnly: !v.Writable(),
+                       Tier:     1,
+                       volume:   v,
+               }
+               if v, ok := v.(interface {
+                       DeviceID() string
+               }); ok {
+                       mnt.DeviceID = v.DeviceID()
+               }
                vm.iostats[v] = &ioStats{}
+               vm.mounts = append(vm.mounts, mnt)
                vm.readables = append(vm.readables, v)
                if v.Writable() {
                        vm.writables = append(vm.writables, v)
@@ -288,6 +331,10 @@ func MakeRRVolumeManager(volumes []Volume) *RRVolumeManager {
        return vm
 }
 
+func (vm *RRVolumeManager) Mounts() []*VolumeMount {
+       return vm.mounts
+}
+
 // AllReadable returns an array of all readable volumes
 func (vm *RRVolumeManager) AllReadable() []Volume {
        return vm.readables