finish basic fs commands, stub out file on server side
This commit is contained in:
parent
8a91fdcb4e
commit
a4f53875bc
5 changed files with 294 additions and 19 deletions
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
||||||
210
filesystem.go
210
filesystem.go
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
70
fs_file.go
Normal 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
|
||||||
|
}
|
||||||
29
request.go
29
request.go
|
|
@ -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"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue