first cross-websocket ls

This commit is contained in:
Barak Michener 2018-03-30 11:39:11 -07:00
parent d7a1eb7e30
commit 1158ac4760
3 changed files with 206 additions and 0 deletions

91
client/connect.go Normal file
View file

@ -0,0 +1,91 @@
package client
import (
"fmt"
"net/url"
"os"
"os/signal"
"time"
"github.com/barakmich/kubelwagen"
"github.com/barakmich/kubelwagen/fs"
"github.com/gorilla/websocket"
"github.com/sirupsen/logrus"
)
func Connect(addr string, dir string) {
closer := make(chan bool)
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, os.Interrupt)
go func() {
for range signalChan {
fmt.Println("\nReceived an interrupt, disconnecting...")
close(closer)
}
}()
handler := fs.NewLocalFs(dir)
c := &client{
handler: handler,
closer: closer,
}
err := c.DialAndServe(addr)
if err != nil {
logrus.Fatalf("Error dialing %s: %s\n", addr, err)
}
<-closer
time.Sleep(500 * time.Millisecond)
}
type client struct {
handler kubelwagen.FS
closer chan bool
txchan chan *kubelwagen.Response
conn *websocket.Conn
}
func (c *client) DialAndServe(addr string) error {
var err error
c.txchan = make(chan *kubelwagen.Response, 20)
u := url.URL{Scheme: "ws", Host: addr, Path: "/"}
logrus.Printf("connecting to %s", u.String())
c.conn, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil {
return err
}
defer c.conn.Close()
go c.tx()
go c.rx()
<-c.closer
return nil
}
func (c *client) tx() {
for x := range c.txchan {
err := c.conn.WriteJSON(x)
if err != nil {
logrus.Errorf("tx error: %s\n", err)
}
}
}
func (c *client) rx() {
defer close(c.closer)
defer close(c.txchan)
for {
req := &kubelwagen.Request{}
err := c.conn.ReadJSON(req)
if err != nil {
logrus.Errorf("rx error: %s\n", err)
return
}
go func(req *kubelwagen.Request) {
resp := c.handler.Handle(req)
c.txchan <- resp
}(req)
}
}

14
fs.go Normal file
View file

@ -0,0 +1,14 @@
package kubelwagen
import "github.com/hanwen/go-fuse/fuse"
type FS interface {
Handle(req *Request) *Response
}
func ErrorResp(r *Request, err error) *Response {
return &Response{
ID: r.ID,
Code: fuse.ToStatus(err),
}
}

101
fs/local.go Normal file
View file

@ -0,0 +1,101 @@
package fs
import (
"io"
"os"
"path/filepath"
"github.com/barakmich/kubelwagen"
"github.com/hanwen/go-fuse/fuse"
"github.com/sirupsen/logrus"
)
type LocalFs struct {
base string
open map[int]*os.File
}
func NewLocalFs(path string) *LocalFs {
return &LocalFs{
base: path,
open: make(map[int]*os.File),
}
}
func (fs *LocalFs) Handle(r *kubelwagen.Request) *kubelwagen.Response {
switch r.Method {
case kubelwagen.MethodOpenDir:
return fs.openDir(r)
case kubelwagen.MethodGetAttr:
return fs.getAttr(r)
}
return &kubelwagen.Response{
ID: r.ID,
Code: fuse.ENOSYS,
}
}
func (fs *LocalFs) getPath(r *kubelwagen.Request) string {
return filepath.Join(fs.base, r.Path)
}
func (fs *LocalFs) openDir(r *kubelwagen.Request) *kubelwagen.Response {
out := &kubelwagen.Response{
ID: r.ID,
Code: fuse.OK,
}
// returns dirents and status
f, err := os.Open(fs.getPath(r))
if err != nil {
logrus.Errorf("LocalFS openDir (%s): %s\n", r.Path, err)
return kubelwagen.ErrorResp(r, err)
}
want := 100
output := make([]fuse.DirEntry, 0, want)
for {
infos, err := f.Readdir(want)
for i := range infos {
// workaround for https://code.google.com/p/go/issues/detail?id=5960
if infos[i] == nil {
continue
}
n := infos[i].Name()
d := fuse.DirEntry{
Name: n,
}
if s := fuse.ToStatT(infos[i]); s != nil {
d.Mode = uint32(s.Mode)
d.Ino = s.Ino
} else {
logrus.Printf("ReadDir entry %q for %q has no stat info", n, r.Path)
}
output = append(output, d)
}
if len(infos) < want || err == io.EOF {
break
}
if err != nil {
logrus.Errorln("Readdir() returned err:", err)
break
}
}
f.Close()
out.Dirents = output
return out
}
func (fs *LocalFs) getAttr(r *kubelwagen.Request) *kubelwagen.Response {
out := &kubelwagen.Response{
ID: r.ID,
Code: fuse.OK,
}
fi, err := os.Stat(fs.getPath(r))
if err != nil {
logrus.Errorf("LocalFS getAttr (%s): %s\n", r.Path, err)
return kubelwagen.ErrorResp(r, err)
}
out.Stat = fuse.ToAttr(fi)
return out
}