finish basic fs commands, stub out file on server side

This commit is contained in:
Barak Michener 2018-03-31 15:11:02 -07:00
parent 8a91fdcb4e
commit a4f53875bc
5 changed files with 294 additions and 19 deletions

View file

@ -5,7 +5,7 @@ It's great to have a consistent environment in containers, and it's great to hav
It would be great if your live development working directory could be mounted into a running pod on the powerful metal in the cloud. Plus, it would obviate the need for workaround services such as [ngrok](https://ngrok.com) to share a running WIP instance, as it would have a valid service on the dev cluster, complete with Ingress resources and everything. Plus, it's running in-cluster; so all the configurations and access controls and service discoveries are available to your dev instance. With the absolute latest, WIP code from your laptop. It would be great if your live development working directory could be mounted into a running pod on the powerful metal in the cloud. Plus, it would obviate the need for workaround services such as [ngrok](https://ngrok.com) to share a running WIP instance, as it would have a valid service on the dev cluster, complete with Ingress resources and everything. Plus, it's running in-cluster; so all the configurations and access controls and service discoveries are available to your dev instance. With the absolute latest, WIP code from your laptop.
Use it in tandem with automatically refreshing dev environments -- JS auto-reloaders for frontend devs, [entr](https://entrproject.org) for general reloads, or any other tooling you like. Use it in tandem with automatically refreshing dev environments -- JS auto-reloaders for frontend devs, [entr](http://entrproject.org) for general reloads, or any other tooling you like.
Kubelwagen is a sidecar ([hah](https://en.wikipedia.org/wiki/Volkswagen_K%C3%BCbelwagen)) container that provides a FUSE directory to the running pod that is mounted in from a client connecting in over an HTTPS websocket. Kubelwagen is a sidecar ([hah](https://en.wikipedia.org/wiki/Volkswagen_K%C3%BCbelwagen)) container that provides a FUSE directory to the running pod that is mounted in from a client connecting in over an HTTPS websocket.

View file

@ -1,7 +1,10 @@
package kubelwagen package kubelwagen
import ( import (
"time"
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
"github.com/hanwen/go-fuse/fuse/pathfs" "github.com/hanwen/go-fuse/fuse/pathfs"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -46,6 +49,9 @@ func (fs *WsFs) getResponse(r *Request) (Response, bool) {
response: c, response: c,
} }
resp, ok := <-c resp, ok := <-c
if !ok {
logrus.Errorln("Response to request channel closed")
}
return resp, ok return resp, ok
} }
@ -56,7 +62,6 @@ func (fs *WsFs) OpenDir(name string, context *fuse.Context) ([]fuse.DirEntry, fu
} }
resp, ok := fs.getResponse(&r) resp, ok := fs.getResponse(&r)
if !ok { if !ok {
logrus.Errorln("Response to request channel closed")
return fs.FileSystem.OpenDir(name, context) return fs.FileSystem.OpenDir(name, context)
} }
return resp.Dirents, resp.Code return resp.Dirents, resp.Code
@ -69,7 +74,6 @@ func (fs *WsFs) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.St
} }
resp, ok := fs.getResponse(&r) resp, ok := fs.getResponse(&r)
if !ok { if !ok {
logrus.Errorln("Response to request channel closed")
return fs.FileSystem.GetAttr(name, context) return fs.FileSystem.GetAttr(name, context)
} }
return resp.Stat, resp.Code return resp.Stat, resp.Code
@ -82,9 +86,207 @@ func (fs *WsFs) StatFs(name string) *fuse.StatfsOut {
} }
resp, ok := fs.getResponse(&r) resp, ok := fs.getResponse(&r)
if !ok { if !ok {
logrus.Errorln("Response to request channel closed")
return fs.FileSystem.StatFs(name) return fs.FileSystem.StatFs(name)
} }
return resp.Statfs return resp.Statfs
}
func (fs *WsFs) GetXAttr(name string, attr string, context *fuse.Context) ([]byte, fuse.Status) {
return nil, fuse.ENOATTR
}
func (fs *WsFs) SetXAttr(name string, attr string, data []byte, flags int, context *fuse.Context) fuse.Status {
return fuse.ENOSYS
}
func (fs *WsFs) ListXAttr(name string, context *fuse.Context) ([]string, fuse.Status) {
return nil, fuse.ENOSYS
}
func (fs *WsFs) RemoveXAttr(name string, attr string, context *fuse.Context) fuse.Status {
return fuse.ENOSYS
}
func (fs *WsFs) Readlink(name string, context *fuse.Context) (string, fuse.Status) {
r := Request{
Method: MethodReadLink,
Path: name,
}
resp, ok := fs.getResponse(&r)
if !ok {
return "", fuse.ENOSYS
}
return resp.LinkStr, resp.Code
}
func (fs *WsFs) Mknod(name string, mode uint32, dev uint32, context *fuse.Context) fuse.Status {
r := Request{
Method: MethodMknod,
Path: name,
Mode: mode,
Dev: int32(dev),
}
resp, ok := fs.getResponse(&r)
if !ok {
return fuse.ENOSYS
}
return resp.Code
}
func (fs *WsFs) Mkdir(name string, mode uint32, context *fuse.Context) fuse.Status {
r := Request{
Method: MethodMkdir,
Path: name,
Mode: mode,
}
resp, ok := fs.getResponse(&r)
if !ok {
return fuse.ENOSYS
}
return resp.Code
}
func (fs *WsFs) Unlink(name string, context *fuse.Context) (code fuse.Status) {
r := Request{
Method: MethodUnlink,
Path: name,
}
resp, ok := fs.getResponse(&r)
if !ok {
return fuse.ENOSYS
}
return resp.Code
}
func (fs *WsFs) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
r := Request{
Method: MethodRmdir,
Path: name,
}
resp, ok := fs.getResponse(&r)
if !ok {
return fuse.ENOSYS
}
return resp.Code
}
func (fs *WsFs) Symlink(value string, linkName string, context *fuse.Context) (code fuse.Status) {
r := Request{
Method: MethodSymlink,
Path: linkName,
NewPath: value,
}
resp, ok := fs.getResponse(&r)
if !ok {
return fuse.ENOSYS
}
return resp.Code
}
func (fs *WsFs) Rename(oldName string, newName string, context *fuse.Context) (code fuse.Status) {
r := Request{
Method: MethodRename,
Path: oldName,
NewPath: newName,
}
resp, ok := fs.getResponse(&r)
if !ok {
return fuse.ENOSYS
}
return resp.Code
}
func (fs *WsFs) Link(oldName string, newName string, context *fuse.Context) (code fuse.Status) {
r := Request{
Method: MethodLink,
Path: oldName,
NewPath: newName,
}
resp, ok := fs.getResponse(&r)
if !ok {
return fuse.ENOSYS
}
return resp.Code
}
func (fs *WsFs) Chmod(name string, mode uint32, context *fuse.Context) (code fuse.Status) {
r := Request{
Method: MethodChmod,
Path: name,
Mode: mode,
}
resp, ok := fs.getResponse(&r)
if !ok {
return fuse.ENOSYS
}
return resp.Code
}
func (fs *WsFs) Chown(name string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
r := Request{
Method: MethodChown,
Path: name,
UID: uid,
GID: gid,
}
resp, ok := fs.getResponse(&r)
if !ok {
return fuse.ENOSYS
}
return resp.Code
}
func (fs *WsFs) Truncate(name string, offset uint64, context *fuse.Context) (code fuse.Status) {
r := Request{
Method: MethodChown,
Path: name,
Offset: offset,
}
resp, ok := fs.getResponse(&r)
if !ok {
return fuse.ENOSYS
}
return resp.Code
}
func (fs *WsFs) Access(name string, mode uint32, context *fuse.Context) (code fuse.Status) {
r := Request{
Method: MethodAccess,
Path: name,
Mode: mode,
}
resp, ok := fs.getResponse(&r)
if !ok {
return fuse.ENOSYS
}
return resp.Code
}
func (fs *WsFs) Utimens(name string, Atime *time.Time, Mtime *time.Time, context *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (fs *WsFs) Open(name string, flags uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
r := Request{
Method: MethodOpen,
Flags: flags,
}
resp, ok := fs.getResponse(&r)
if !ok {
return nil, fuse.ENOSYS
}
return newWsFsFile(*resp.FileHandle, fs), resp.Code
}
func (fs *WsFs) Create(name string, flags uint32, mode uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
r := Request{
Method: MethodCreate,
Flags: flags,
Mode: mode,
}
resp, ok := fs.getResponse(&r)
if !ok {
return nil, fuse.ENOSYS
}
return newWsFsFile(*resp.FileHandle, fs), resp.Code
} }

View file

@ -36,7 +36,7 @@ func (fs *LocalFs) Handle(r *kubelwagen.Request) *kubelwagen.Response {
case kubelwagen.MethodChown: case kubelwagen.MethodChown:
return fs.serveFromErr(r, os.Chown(fs.getPath(r), int(r.UID), int(r.GID))) return fs.serveFromErr(r, os.Chown(fs.getPath(r), int(r.UID), int(r.GID)))
case kubelwagen.MethodTruncate: case kubelwagen.MethodTruncate:
return fs.serveFromErr(r, os.Truncate(fs.getPath(r), r.Offset)) return fs.serveFromErr(r, os.Truncate(fs.getPath(r), int64(r.Offset)))
case kubelwagen.MethodMknod: case kubelwagen.MethodMknod:
return fs.serveFromErr(r, syscall.Mknod(fs.getPath(r), r.Mode, int(r.Dev))) return fs.serveFromErr(r, syscall.Mknod(fs.getPath(r), r.Mode, int(r.Dev)))
case kubelwagen.MethodMkdir: case kubelwagen.MethodMkdir:

70
fs_file.go Normal file
View file

@ -0,0 +1,70 @@
package kubelwagen
import (
"time"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
)
type WsFsFile struct {
nodefs.File
h int
fs *WsFs
}
func newWsFsFile(handle int, fs *WsFs) *WsFsFile {
return &WsFsFile{
File: nodefs.NewDefaultFile(),
h: handle,
fs: fs,
}
}
func (f *WsFsFile) String() string {
return "WsFsFile"
}
func (f *WsFsFile) Read(buf []byte, off int64) (fuse.ReadResult, fuse.Status) {
return nil, fuse.ENOSYS
}
func (f *WsFsFile) Write(data []byte, off int64) (uint32, fuse.Status) {
return 0, fuse.ENOSYS
}
func (f *WsFsFile) Flock(flags int) fuse.Status { return fuse.ENOSYS }
func (f *WsFsFile) Flush() fuse.Status {
return fuse.OK
}
func (f *WsFsFile) Release() {
}
func (f *WsFsFile) GetAttr(*fuse.Attr) fuse.Status {
return fuse.ENOSYS
}
func (f *WsFsFile) Fsync(flags int) (code fuse.Status) {
return fuse.ENOSYS
}
func (f *WsFsFile) Utimens(atime *time.Time, mtime *time.Time) fuse.Status {
return fuse.ENOSYS
}
func (f *WsFsFile) Truncate(size uint64) fuse.Status {
return fuse.ENOSYS
}
func (f *WsFsFile) Chown(uid uint32, gid uint32) fuse.Status {
return fuse.ENOSYS
}
func (f *WsFsFile) Chmod(perms uint32) fuse.Status {
return fuse.ENOSYS
}
func (f *WsFsFile) Allocate(off uint64, size uint64, mode uint32) (code fuse.Status) {
return fuse.ENOSYS
}

View file

@ -21,6 +21,8 @@ const (
MethodLink MethodLink
MethodReadLink MethodReadLink
MethodAccess MethodAccess
MethodOpen
MethodCreate
) )
type Request struct { type Request struct {
@ -28,12 +30,12 @@ type Request struct {
Method Method `json:"method"` Method Method `json:"method"`
Path string `json:"path"` Path string `json:"path"`
Mode uint32 Mode uint32
UID int32 UID uint32
GID int32 GID uint32
Size int32 Size uint32
Dev int32 Dev int32
Flags int32 Flags uint32
Offset int64 Offset uint64
NewPath string `json:"newpath,omitempty"` NewPath string `json:"newpath,omitempty"`
Attr string `json:"attr,omitempty"` Attr string `json:"attr,omitempty"`
Data []byte `json:"data,omitempty"` Data []byte `json:"data,omitempty"`
@ -45,12 +47,13 @@ type RequestCallback struct {
} }
type Response struct { type Response struct {
ID int `json:"id"` ID int `json:"id"`
Code fuse.Status `json:"status"` Code fuse.Status `json:"status"`
Data []byte `json:"data,omitempty"` Data []byte `json:"data,omitempty"`
Stat *fuse.Attr `json:"stat,omitempty"` Stat *fuse.Attr `json:"stat,omitempty"`
Attrs []string `json:"attrs,omitempty"` Attrs []string `json:"attrs,omitempty"`
Dirents []fuse.DirEntry `json:"dirents,omitempty"` Dirents []fuse.DirEntry `json:"dirents,omitempty"`
LinkStr string `json:"linkstr,omitempty"` LinkStr string `json:"linkstr,omitempty"`
Statfs *fuse.StatfsOut `json:"statfs,omitempty"` Statfs *fuse.StatfsOut `json:"statfs,omitempty"`
FileHandle *int `json:"filehandle,omitempty"`
} }