From 1410140af83d00b970c54869f261164b1dc50ec1 Mon Sep 17 00:00:00 2001 From: maxine Date: Thu, 14 Nov 2024 19:56:55 -0500 Subject: [PATCH] Did a TON of shit --- main.go | 239 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 197 insertions(+), 42 deletions(-) diff --git a/main.go b/main.go index 23f8a83..2856d50 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,11 @@ import ( "errors" "strconv" "context" + "os/user" + "io" + "sync" + + "github.com/knusbaum/go9p" "github.com/knusbaum/go9p/fs" "github.com/knusbaum/go9p/proto" @@ -12,70 +17,101 @@ import ( "github.com/docker/docker/client" ) +/// +/// +/// Dynamic Tree Implementation +/// +/// + type NewStat func (name, uid, gid string, mode uint32) *proto.Stat -type NodeGenerator func (t Tree) []fs.FSNode; +type Mappable interface { + MapFS(t *Tree) []fs.FSNode +} + +type NodeGenerator func (t *Tree) []fs.FSNode; + +func (ng NodeGenerator) MapFS(t *Tree) []fs.FSNode { return ng(t) }; type Tree struct { tStat proto.Stat tParent fs.Dir + //tcache []fs.FSNode NodeNS NewStat - NodeGen NodeGenerator + //NodeGen NodeGenerator + NodeGen Mappable } -func (t Tree) Stat() proto.Stat { return t.tStat } +func (t *Tree) Stat() proto.Stat { return t.tStat } -func (t Tree) WriteStat(s *proto.Stat) error { return errors.New("attributes are read only") } +func (t *Tree) WriteStat(s *proto.Stat) error { return errors.New("attributes are read only") } -func (t Tree) SetParent(p fs.Dir) {} +func (t *Tree) SetParent(p fs.Dir) {} -func (t Tree) Parent() fs.Dir { return t.tParent } +func (t *Tree) Parent() fs.Dir { return t.tParent } -func (t Tree) Children() map[string]fs.FSNode { +func (t *Tree) Children() map[string]fs.FSNode { ret := make(map[string]fs.FSNode) - for _, n := range t.NodeGen(t) { + for _, n := range t.NodeGen.MapFS(t) { //t.tcache { //t.NodeGen.MapFS(t) { //t.NodeGen(t) { ret[n.Stat().Name] = n } return ret } -func NewTree(s *proto.Stat, p fs.Dir, ns NewStat, ng NodeGenerator) Tree { - t := Tree{ +//func (t *Tree) Update() { +// t.tcache = t.NodeGen.MapFS(t) +//} + +//func NewTree(s *proto.Stat, p fs.Dir, ns NewStat, ng NodeGenerator) Tree { +func NewTree(s *proto.Stat, p fs.Dir, ns NewStat, ng Mappable) *Tree { + t := &Tree{ tStat: *s, tParent: p, + //tcache: make([]fs.FSNode, 0), NodeNS: ns, NodeGen: ng, } t.tStat.Mode |= proto.DMDIR t.tStat.Qid.Qtype = uint8(t.tStat.Mode >> 24) + //t.Update() return t } -/* -type TestMFS struct { - a int +// This is just a helper for merging Mappables/NodeGenerators together +// Allows for making distinct node generators for sections of a Dynamic Tree +// without much hassle +func ConcatMapFS(m ...Mappable) NodeGenerator { + return func(t *Tree) []fs.FSNode { + ret := make([]fs.FSNode, 0) + + for _, n := range m { + ret = append(ret, n.MapFS(t)...) + } + return ret + } } -func NewTestMFS(a int) *TestMFS { - return &TestMFS{a} -} -*/ +/// +/// +/// DockerFS Implementation +/// +/// type ContainerNode dtypes.Container -func (cn ContainerNode) ContainerToFS(t Tree) []fs.FSNode { +func (cn ContainerNode) MapFS(t *Tree) []fs.FSNode { hnewstat := func(name string) *proto.Stat { return t.NodeNS(name, t.Stat().Uid, t.Stat().Gid, 0444) } ID_Node := fs.NewStaticFile(hnewstat("ID"), []byte(cn.ID)) - Names_Node_ng := func(t Tree) []fs.FSNode { + Names_Node_ng := NodeGenerator(func(t *Tree) []fs.FSNode { ret := make([]fs.FSNode, 0) for i, n := range cn.Names { ret = append(ret, fs.NewStaticFile(hnewstat(strconv.Itoa(i)), []byte(n))) } return ret - } + }) Names_Node := NewTree(t.NodeNS("Names", t.Stat().Uid, t.Stat().Gid, 0777), t, t.NodeNS, Names_Node_ng) Image_Node := fs.NewStaticFile(hnewstat("Image"), []byte(cn.Image)) @@ -86,37 +122,156 @@ func (cn ContainerNode) ContainerToFS(t Tree) []fs.FSNode { Created_Node := fs.NewStaticFile(hnewstat("Created"), []byte(strconv.Itoa(int(cn.Created)))) - return []fs.FSNode{ID_Node, Names_Node, Image_Node, ImageID_Node, Command_Node, Created_Node} } +type ClientNode struct { + *client.Client + ctx context.Context +} + +// Alot of this was hacked together with ideas from fs.DynamicFile +type ContainerLogsNode struct { + nStat proto.Stat + nParent fs.Dir + sync.RWMutex + fidContent map[uint64]io.ReadCloser + cli *ClientNode + containerNode ContainerNode +} + +func (n *ContainerLogsNode) Stat() proto.Stat { return n.nStat } +func (n *ContainerLogsNode) WriteStat(s *proto.Stat) error { return errors.New("attributes are read only") } +func (n *ContainerLogsNode) SetParent(p fs.Dir) {} +func (n *ContainerLogsNode) Parent() fs.Dir { return n.nParent } +func (n *ContainerLogsNode) Open(fid uint64, omode proto.Mode) error { + n.Lock() + defer n.Unlock() + out, err := n.cli.ContainerLogs( + //n.cli.ctx, n.nParent.Stat().Name, + n.cli.ctx, n.containerNode.ID, + // Tail is set to 0, but a default 1024 lines is planned + // Follow is set to true so the file can be read with a + // stream of the latest logs + // Options structs will later just be incorporated as + // part of either the ClientNode or ContainerNode with + // a ctl file for changing the options + containertypes.LogsOptions{ShowStdout: true, ShowStderr: true, Tail: "0", Follow: true}, + ) + if err != nil { + panic(err) + } + n.fidContent[fid] = out + return nil +} +func (n *ContainerLogsNode) Read(fid uint64, offset uint64, count uint64) ([]byte, error) { + n.Lock() + defer n.Unlock() + + bs := make([]byte, count) + switch _, err := n.fidContent[fid].Read(bs); err { + case nil: + return bs, nil + case io.EOF: + return []byte{}, nil + default: + println(err.Error()) + return []byte{}, err + } +} +func (n *ContainerLogsNode) Write(fid uint64, offset uint64, data []byte) (uint32, error) { + return 0, errors.New("Cannot write to file.") +} +func (n *ContainerLogsNode) Close(fid uint64) error { delete(n.fidContent, fid); return nil } + + +// This wasn't really that clever, idk maybe we'll see +/* +func (n *ContainerLogsNode) MapFS(t *Tree) []fs.FSNode { + n.nStat = *t.NodeNS("Logs", t.Stat().Uid, t.Stat().Gid, 0777) + n.nParent = t + n.fidContent = make(map[uint64]io.ReadCloser) + return []fs.FSNode{n} +} +*/ + +//func (n *ContainerLogsNode) MapFS(t *Tree) + +func NewContainerLogsNode(s *proto.Stat, p fs.Dir, cliNode *ClientNode, containerNode ContainerNode) *ContainerLogsNode { + return &ContainerLogsNode{ + nStat: *s, + nParent: p, + fidContent: make(map[uint64]io.ReadCloser), + cli: cliNode, + containerNode: containerNode, + } +} + +func (cn *ClientNode) MapRunningContainers(t *Tree) []fs.FSNode { + ret := make([]fs.FSNode, 0) + + containers, err := cn.ContainerList(cn.ctx, containertypes.ListOptions{}) + if err != nil { + panic(err) + } + + for _, c := range containers { + //cl := &ContainerLogsNode{cli: cn} + containerNode := ContainerNode(c) + api_ng := func(t *Tree) []fs.FSNode { + return []fs.FSNode{ + NewContainerLogsNode( + t.NodeNS("Logs", t.Stat().Uid, t.Stat().Gid, 0777), t, + cn, containerNode, + ), + } + } + ctree := NewTree( + t.NodeNS(c.ID, t.Stat().Uid, t.Stat().Gid, 0777), + t, t.NodeNS, + ConcatMapFS( + //ContainerNode(c), + containerNode, + NodeGenerator(api_ng), + ), + ) + + ret = append(ret, ctree) + } + return ret + +} + +func (cn *ClientNode) MapFS(t *Tree) []fs.FSNode { + ret := make([]fs.FSNode, 0) + + running_ng := NodeGenerator(cn.MapRunningContainers) + + ret = append(ret, NewTree( + t.NodeNS("running", t.Stat().Uid, t.Stat().Gid, 0777), + t, t.NodeNS, running_ng, + )) + + return ret +} + func InitFS() *fs.FS { var rfs fs.FS - - ng := func(t Tree) []fs.FSNode { - ctx := context.Background() - cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) - if err != nil { + + ctx := context.Background() + cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + if err != nil { panic(err) - } - defer cli.Close() - - containers, err := cli.ContainerList(ctx, containertypes.ListOptions{}) - if err != nil { - panic(err) - } - - ret := make([]fs.FSNode, 0) - for _, c := range containers { - ret = append(ret, NewTree(t.NodeNS(c.ID, "maxine", "maxine", 0777), t, t.NodeNS, ContainerNode(c).ContainerToFS)) - - } - return ret } - s := rfs.NewStat("/", "maxine", "maxine", 0777) + clinode := &ClientNode{cli, ctx} - t := NewTree(s, nil, rfs.NewStat, ng) + + u, _ := user.Current() + + s := rfs.NewStat("/", u.Username, u.Username, 0777) + + t := NewTree(s, nil, rfs.NewStat, clinode) rfs.Root = t