first cross-websocket ls
This commit is contained in:
parent
d7a1eb7e30
commit
1158ac4760
3 changed files with 206 additions and 0 deletions
91
client/connect.go
Normal file
91
client/connect.go
Normal 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
14
fs.go
Normal 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
101
fs/local.go
Normal 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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue