diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/k9p/logger/logger.go | 195 | ||||
-rw-r--r-- | pkg/k9p/session.go | 205 | ||||
-rw-r--r-- | pkg/resources/deployments.go | 184 | ||||
-rw-r--r-- | pkg/resources/files.go | 60 | ||||
-rw-r--r-- | pkg/resources/namespace.go | 68 | ||||
-rw-r--r-- | pkg/resources/namespaces.go | 110 | ||||
-rw-r--r-- | pkg/resources/refs.go | 13 | ||||
-rw-r--r-- | pkg/resources/resources.go | 12 | ||||
-rw-r--r-- | pkg/resources/staticdir.go | 76 |
9 files changed, 923 insertions, 0 deletions
diff --git a/pkg/k9p/logger/logger.go b/pkg/k9p/logger/logger.go new file mode 100644 index 0000000..6b2e437 --- /dev/null +++ b/pkg/k9p/logger/logger.go @@ -0,0 +1,195 @@ +package logger + +import ( + "context" + "time" + + p9p "github.com/docker/go-p9p" + "github.com/rs/zerolog" +) + +type Logger struct { + logger zerolog.Logger + session p9p.Session +} + +func New(logger zerolog.Logger, session p9p.Session) *Logger { + return &Logger{ + logger: logger, + session: session, + } +} + +func (l *Logger) Auth(ctx context.Context, afid p9p.Fid, uname string, aname string) (qid p9p.Qid, err error) { + defer func(t1 time.Time) { + l.logger.Debug(). + Str("request", "auth"). + Uint32("afid", uint32(afid)). + Str("uname", uname). + Str("aname", aname). + TimeDiff("duration", time.Now(), t1). + Dict("ret", zerolog.Dict(). + Str("qid", qid.String()). + Err(err)). + Msg("") + }(time.Now()) + + return l.session.Auth(ctx, afid, uname, aname) +} + +func (l *Logger) Attach(ctx context.Context, fid p9p.Fid, afid p9p.Fid, uname string, aname string) (qid p9p.Qid, err error) { + defer func(t1 time.Time) { + l.logger.Debug(). + Str("request", "attach"). + Uint32("fid", uint32(fid)). + Uint32("afid", uint32(afid)). + Str("uname", uname). + Str("aname", aname). + TimeDiff("duration", time.Now(), t1). + Dict("ret", zerolog.Dict(). + Str("qid", qid.String()). + Err(err)). + Msg("") + }(time.Now()) + return l.session.Attach(ctx, fid, afid, uname, aname) +} + +func (l *Logger) Clunk(ctx context.Context, fid p9p.Fid) (err error) { + defer func(t1 time.Time) { + l.logger.Debug(). + Str("request", "clunk"). + Uint32("fid", uint32(fid)). + TimeDiff("duration", time.Now(), t1). + Dict("ret", zerolog.Dict(). + Err(err)). + Msg("") + }(time.Now()) + return l.session.Clunk(ctx, fid) +} + +func (l *Logger) Remove(ctx context.Context, fid p9p.Fid) (err error) { + defer func(t1 time.Time) { + l.logger.Debug(). + Str("request", "remove"). + Uint32("fid", uint32(fid)). + TimeDiff("duration", time.Now(), t1). + Dict("ret", zerolog.Dict(). + Err(err)). + Msg("") + }(time.Now()) + return l.session.Remove(ctx, fid) +} + +func (l *Logger) Walk(ctx context.Context, fid p9p.Fid, newfid p9p.Fid, names ...string) (qids []p9p.Qid, err error) { + defer func(t1 time.Time) { + arr := zerolog.Arr() + for _, qid := range qids { + arr = arr.Str(qid.String()) + } + + l.logger.Debug(). + Str("request", "walk"). + Uint32("fid", uint32(fid)). + Uint32("newfid", uint32(newfid)). + Strs("names", names). + TimeDiff("duration", time.Now(), t1). + Dict("ret", zerolog.Dict(). + Array("qids", arr). + Err(err)). + Msg("") + }(time.Now()) + return l.session.Walk(ctx, fid, newfid, names...) +} + +// Read follows the semantics of io.ReaderAt.ReadAtt method except it takes +// a contxt and Fid. +func (l *Logger) Read(ctx context.Context, fid p9p.Fid, p []byte, offset int64) (n int, err error) { + defer func(t1 time.Time) { + l.logger.Debug(). + Str("request", "read"). + Uint32("fid", uint32(fid)). + Int64("offset", offset). + TimeDiff("duration", time.Now(), t1). + Dict("ret", zerolog.Dict(). + Int("n", n). + Err(err)). + Msg("") + }(time.Now()) + return l.session.Read(ctx, fid, p, offset) +} + +// Write follows the semantics of io.WriterAt.WriteAt except takes a context and an Fid. +// +// If n == len(p), no error is returned. +// If n < len(p), io.ErrShortWrite will be returned. +func (l *Logger) Write(ctx context.Context, fid p9p.Fid, p []byte, offset int64) (n int, err error) { + defer func(t1 time.Time) { + l.logger.Debug(). + Str("request", "write"). + Uint32("fid", uint32(fid)). + Int64("offset", offset). + TimeDiff("duration", time.Now(), t1). + Dict("ret", zerolog.Dict(). + Int("n", n). + Err(err)). + Msg("") + }(time.Now()) + return l.session.Write(ctx, fid, p, offset) +} + +func (l *Logger) Open(ctx context.Context, fid p9p.Fid, mode p9p.Flag) (qid p9p.Qid, iounit uint32, err error) { + defer func(t1 time.Time) { + l.logger.Debug(). + Str("request", "open"). + Uint32("fid", uint32(fid)). + Uint8("mode", uint8(mode)). + TimeDiff("duration", time.Now(), t1). + Dict("ret", zerolog.Dict(). + Str("qid", qid.String()). + Uint32("iounit", iounit). + Err(err)). + Msg("") + }(time.Now()) + return l.session.Open(ctx, fid, mode) +} + +func (l *Logger) Create(ctx context.Context, parent p9p.Fid, name string, perm uint32, mode p9p.Flag) (p9p.Qid, uint32, error) { + l.logger.Debug(). + Str("request", "create"). + Msg("") + return l.session.Create(ctx, parent, name, perm, mode) +} + +func (l *Logger) Stat(ctx context.Context, fid p9p.Fid) (dir p9p.Dir, err error) { + defer func(t1 time.Time) { + l.logger.Debug(). + Str("request", "stat"). + Uint32("fid", uint32(fid)). + Dict("ret", zerolog.Dict(). + Dict("dir", zerolog.Dict(). + Str("name", dir.Name). + Str("qid", dir.Qid.String()). + Uint64("length", dir.Length). + Time("modTime", dir.ModTime)). + Err(err)). + Msg("") + }(time.Now()) + return l.session.Stat(ctx, fid) +} + +func (l *Logger) WStat(ctx context.Context, fid p9p.Fid, dir p9p.Dir) error { + l.logger.Debug(). + Str("request", "stat"). + Msg("") + return l.session.WStat(ctx, fid, dir) +} + +// Version returns the supported version and msize of the session. This +// can be affected by negotiating or the level of support provided by the +// session implementation. +func (l *Logger) Version() (msize int, version string) { + l.logger.Debug(). + Str("request", "stat"). + Msg("") + return l.session.Version() +} diff --git a/pkg/k9p/session.go b/pkg/k9p/session.go new file mode 100644 index 0000000..471a6bc --- /dev/null +++ b/pkg/k9p/session.go @@ -0,0 +1,205 @@ +package k9p + +import ( + "context" + "runtime" + "sync" + + "github.com/docker/go-p9p" + "go.terinstock.com/k9p/pkg/resources" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" +) + +type Session struct { + sync.Mutex + aname string + uname string + + client kubernetes.Interface + sharedInformer informers.SharedInformerFactory + refs map[p9p.Fid]resources.Ref +} + +func New(ctx context.Context, client kubernetes.Interface) *Session { + sharedInformer := informers.NewSharedInformerFactory(client, 0) + + sharedInformer.Core().V1().Namespaces().Informer() + sharedInformer.Apps().V1().Deployments().Informer() + + go sharedInformer.Start(ctx.Done()) + runtime.Gosched() + + return &Session{ + client: client, + sharedInformer: sharedInformer, + refs: make(map[p9p.Fid]resources.Ref), + } +} + +func (k *Session) getRef(fid p9p.Fid) (resources.Ref, error) { + k.Lock() + defer k.Unlock() + + if fid == p9p.NOFID { + return nil, p9p.ErrUnknownfid + } + + ref, found := k.refs[fid] + if !found { + return nil, p9p.ErrUnknownfid + } + + return ref, nil +} + +func (k *Session) newRef(fid p9p.Fid, resource resources.Ref) (resources.Ref, error) { + k.Lock() + defer k.Unlock() + + if fid == p9p.NOFID { + return nil, p9p.ErrUnknownfid + } + + _, ok := k.refs[fid] + if ok { + return nil, p9p.ErrDupfid + } + + ref := resource + k.refs[fid] = ref + return ref, nil +} + +func (k *Session) Auth(ctx context.Context, afid p9p.Fid, uname string, aname string) (p9p.Qid, error) { + return p9p.Qid{}, nil +} + +func (k *Session) Attach(ctx context.Context, fid p9p.Fid, afid p9p.Fid, uname string, aname string) (p9p.Qid, error) { + if uname == "" { + return p9p.Qid{}, p9p.MessageRerror{Ename: "no user"} + } + + if aname == "" { + aname = "/" + } + + k.uname = uname + k.aname = aname + + ref, err := k.newRef(fid, resources.NewDirRef("/", k, map[string]resources.Ref{ + "namespaces": resources.NewNamespacesRef(k.client, k), + "cluster": resources.NewDirRef("cluster", k, map[string]resources.Ref{}), + })) + if err != nil { + return p9p.Qid{}, err + } + + return ref.Info().Qid, nil +} + +func (k *Session) Clunk(ctx context.Context, fid p9p.Fid) error { + _, err := k.getRef(fid) + if err != nil { + return err + } + + k.Lock() + defer k.Unlock() + delete(k.refs, fid) + + return nil +} + +func (k *Session) Remove(ctx context.Context, fid p9p.Fid) error { + return p9p.ErrUnknownMsg +} + +func (k *Session) Walk(ctx context.Context, fid p9p.Fid, newfid p9p.Fid, names ...string) ([]p9p.Qid, error) { + var qids []p9p.Qid + + ref, err := k.getRef(fid) + if err != nil { + return qids, err + } + + current := ref + for _, name := range names { + newResource, err := current.Get(name) + if err != nil { + break + } + + qids = append(qids, newResource.Info().Qid) + current = newResource + } + + if len(qids) != len(names) { + return qids, nil + } + + _, err = k.newRef(newfid, current) + if err != nil { + return qids, err + } + + return qids, nil +} + +func (k *Session) Read(ctx context.Context, fid p9p.Fid, p []byte, offset int64) (n int, err error) { + ref, err := k.getRef(fid) + if err != nil { + return 0, err + } + + return ref.Read(ctx, p, offset) +} + +func (k *Session) Write(ctx context.Context, fid p9p.Fid, p []byte, offset int64) (n int, err error) { + return 0, p9p.ErrUnknownMsg +} + +func (k *Session) Open(ctx context.Context, fid p9p.Fid, mode p9p.Flag) (p9p.Qid, uint32, error) { + ref, err := k.getRef(fid) + if err != nil { + return p9p.Qid{}, 0, err + } + + return ref.Info().Qid, 0, nil +} + +func (k *Session) Create(ctx context.Context, parent p9p.Fid, name string, perm uint32, mode p9p.Flag) (p9p.Qid, uint32, error) { + return p9p.Qid{}, 0, p9p.ErrUnknownMsg +} + +func (k *Session) Stat(ctx context.Context, fid p9p.Fid) (p9p.Dir, error) { + ref, err := k.getRef(fid) + if err != nil { + return p9p.Dir{}, err + } + + return ref.Info(), nil +} + +func (k *Session) WStat(ctx context.Context, fid p9p.Fid, dir p9p.Dir) error { + return p9p.ErrUnknownMsg +} + +// Version returns the supported version and msize of the session. This +// can be affected by negotiating or the level of support provided by the +// session implementation. +func (k *Session) Version() (msize int, version string) { + return p9p.DefaultMSize, p9p.DefaultVersion +} + +func (k *Session) WaitForCacheSync(stopCh <-chan struct{}) { + k.sharedInformer.WaitForCacheSync(stopCh) +} + +func (k *Session) GetAuth() (uname, aname string) { + return k.uname, k.aname +} + +func (k *Session) Informer() informers.SharedInformerFactory { + return k.sharedInformer +} diff --git a/pkg/resources/deployments.go b/pkg/resources/deployments.go new file mode 100644 index 0000000..065d03b --- /dev/null +++ b/pkg/resources/deployments.go @@ -0,0 +1,184 @@ +package resources + +import ( + "context" + "io" + "math/rand" + "strconv" + "time" + + "github.com/docker/go-p9p" + v1 "k8s.io/api/apps/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + appsv1 "k8s.io/client-go/informers/apps/v1" + "sigs.k8s.io/yaml" +) + +type Deployments struct { + namespace string + deploymentInformer appsv1.DeploymentInformer + session Session + info *p9p.Dir + readdir *p9p.Readdir +} + +func NewDeployments(namespace string, session Session) *Deployments { + deploymentInformer := session.Informer().Apps().V1().Deployments() + return &Deployments{ + namespace: namespace, + deploymentInformer: deploymentInformer, + session: session, + } +} + +func (r *Deployments) Info() p9p.Dir { + if r.info != nil { + return *r.info + } + + dir := p9p.Dir{} + dir.Qid.Path = rand.Uint64() + dir.Qid.Version = 0 + + dir.Name = "deployments" + dir.Mode = 0664 + dir.Length = 0 + dir.AccessTime = time.Now() + dir.ModTime = time.Now() + dir.MUID = "none" + + uname, _ := r.session.GetAuth() + dir.UID = uname + dir.GID = uname + + dir.Qid.Type |= p9p.QTDIR + dir.Mode |= p9p.DMDIR + r.info = &dir + + return dir +} + +func (r *Deployments) Get(name string) (Ref, error) { + deployment, err := r.deploymentInformer.Lister().Deployments(r.namespace).Get(name) + if apierrors.IsNotFound(err) { + return nil, p9p.ErrNotfound + } + if err != nil { + return nil, err + } + + return NewDeploymentRef(deployment, r.session), nil +} + +func (r *Deployments) Read(ctx context.Context, p []byte, offset int64) (n int, err error) { + if r.readdir != nil { + return r.readdir.Read(ctx, p, offset) + } + + deployments, err := r.deploymentInformer.Lister().Deployments(r.namespace).List(labels.Everything()) + if err != nil { + return 0, err + } + + deploymentRefs := make([]Ref, 0, len(deployments)) + + for _, deployment := range deployments { + deployment := deployment + deploymentRefs = append(deploymentRefs, NewDeploymentRef(deployment, r.session)) + } + + r.readdir = p9p.NewReaddir(p9p.NewCodec(), func() (p9p.Dir, error) { + if len(deploymentRefs) == 0 { + return p9p.Dir{}, io.EOF + } + + deployment := deploymentRefs[0] + deploymentRefs = deploymentRefs[1:] + + return deployment.Info(), nil + }) + + n, err = r.readdir.Read(ctx, p, offset) + + return n, err +} + +type DeploymentRef struct { + deployment *v1.Deployment + session Session + info *p9p.Dir + readdir *p9p.Readdir + children map[string]Ref +} + +func NewDeploymentRef(deployment *v1.Deployment, session Session) *DeploymentRef { + y, _ := yaml.Marshal(deployment) + children := map[string]Ref{ + "data.yaml": &Static{ + name: "data.yaml", + content: y, + session: session, + }, + "scale": &Static{ + name: "scale", + content: []byte(strconv.Itoa(int(*deployment.Spec.Replicas))), + session: session, + }, + } + return &DeploymentRef{ + deployment: deployment, + session: session, + children: children, + } +} + +func (r *DeploymentRef) Info() p9p.Dir { + if r.info != nil { + return *r.info + } + + dir := p9p.Dir{} + dir.Qid.Path = rand.Uint64() + dir.Qid.Version = 0 + + dir.Name = r.deployment.Name + dir.Mode = 0664 + dir.Length = 0 + dir.AccessTime = r.deployment.CreationTimestamp.Time + dir.ModTime = r.deployment.CreationTimestamp.Time + dir.MUID = "none" + + uname, _ := r.session.GetAuth() + dir.UID = uname + dir.GID = uname + + dir.Qid.Type |= p9p.QTDIR + dir.Mode |= p9p.DMDIR + r.info = &dir + + return dir +} + +func (r *DeploymentRef) Get(name string) (Ref, error) { + ref, ok := r.children[name] + if !ok { + return nil, p9p.ErrNotfound + } + + return ref, nil +} + +func (r *DeploymentRef) Read(ctx context.Context, p []byte, offset int64) (int, error) { + if r.readdir != nil { + return r.readdir.Read(ctx, p, offset) + } + + dir := make([]p9p.Dir, 0, len(r.children)) + for _, child := range r.children { + dir = append(dir, child.Info()) + } + + r.readdir = p9p.NewFixedReaddir(p9p.NewCodec(), dir) + return r.readdir.Read(ctx, p, offset) +} diff --git a/pkg/resources/files.go b/pkg/resources/files.go new file mode 100644 index 0000000..64cf892 --- /dev/null +++ b/pkg/resources/files.go @@ -0,0 +1,60 @@ +package resources + +import ( + "context" + "math/rand" + "time" + + "github.com/docker/go-p9p" + "github.com/rs/zerolog/log" +) + +type Static struct { + name string + offset int64 + content []byte + info *p9p.Dir + session Session +} + +func (r *Static) Info() p9p.Dir { + if r.info != nil { + return *r.info + } + + dir := p9p.Dir{} + dir.Qid.Path = rand.Uint64() + dir.Qid.Version = 0 + + dir.Name = r.name + dir.Mode = 0664 + dir.Length = 0 + dir.AccessTime = time.Now() + dir.ModTime = time.Now() + dir.MUID = "none" + + uname, _ := r.session.GetAuth() + dir.UID = uname + dir.GID = uname + + dir.Qid.Type |= p9p.QTFILE + + r.info = &dir + return dir +} + +func (r *Static) Get(name string) (Ref, error) { + return nil, p9p.ErrWalknodir +} + +func (r *Static) Read(ctx context.Context, p []byte, offset int64) (n int, err error) { + log.Debug().Int64("offset", r.offset).Str("name", r.name).Send() + if offset != r.offset { + return 0, p9p.ErrBadoffset + } + + n = copy(p, r.content[offset:]) + r.offset += int64(n) + + return n, nil +} diff --git a/pkg/resources/namespace.go b/pkg/resources/namespace.go new file mode 100644 index 0000000..df08c2d --- /dev/null +++ b/pkg/resources/namespace.go @@ -0,0 +1,68 @@ +package resources + +import ( + "context" + "math/rand" + + "github.com/docker/go-p9p" + v1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes" +) + +type NamespaceRef struct { + namespace *v1.Namespace + client kubernetes.Interface + session Session + info *p9p.Dir + readdir *p9p.Readdir +} + +func (r *NamespaceRef) Info() p9p.Dir { + if r.info != nil { + return *r.info + } + + dir := p9p.Dir{} + dir.Qid.Path = rand.Uint64() + dir.Qid.Version = uint32(r.namespace.Generation) + + dir.Name = r.namespace.Name + dir.Mode = 0664 + dir.Length = 0 + dir.AccessTime = r.namespace.CreationTimestamp.Time + dir.ModTime = r.namespace.CreationTimestamp.Time + dir.MUID = "none" + + uname, _ := r.session.GetAuth() + dir.UID = uname + dir.GID = uname + + dir.Qid.Type |= p9p.QTDIR + dir.Mode |= p9p.DMDIR + + r.info = &dir + return dir +} + +func (r *NamespaceRef) Get(name string) (Ref, error) { + switch name { + case "deployments": + return NewDeployments(r.namespace.Name, r.session), nil + } + + return nil, p9p.ErrNotfound +} + +func (r *NamespaceRef) Read(ctx context.Context, p []byte, offset int64) (n int, err error) { + if r.readdir != nil { + return r.readdir.Read(ctx, p, offset) + } + + deployments := NewDeployments(r.namespace.Name, r.session) + dir := []p9p.Dir{ + deployments.Info(), + } + + r.readdir = p9p.NewFixedReaddir(p9p.NewCodec(), dir) + return r.readdir.Read(ctx, p, offset) +} diff --git a/pkg/resources/namespaces.go b/pkg/resources/namespaces.go new file mode 100644 index 0000000..15ea9d6 --- /dev/null +++ b/pkg/resources/namespaces.go @@ -0,0 +1,110 @@ +package resources + +import ( + "context" + "io" + "math/rand" + "time" + + "github.com/docker/go-p9p" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + corev1 "k8s.io/client-go/informers/core/v1" + "k8s.io/client-go/kubernetes" +) + +type NamespacesRef struct { + client kubernetes.Interface + namespaceInformer corev1.NamespaceInformer + session Session + info *p9p.Dir + readdir *p9p.Readdir +} + +func NewNamespacesRef(client kubernetes.Interface, session Session) *NamespacesRef { + namespaceInformer := session.Informer().Core().V1().Namespaces() + + return &NamespacesRef{ + client: client, + namespaceInformer: namespaceInformer, + session: session, + } +} + +func (r *NamespacesRef) Info() p9p.Dir { + if r.info != nil { + return *r.info + } + + dir := p9p.Dir{} + dir.Qid.Path = rand.Uint64() + dir.Qid.Version = 0 + + dir.Name = "namespaces" + dir.Mode = 0664 + dir.Length = 0 + dir.AccessTime = time.Now() + dir.ModTime = time.Now() + dir.MUID = "none" + + uname, _ := r.session.GetAuth() + dir.UID = uname + dir.GID = uname + + dir.Qid.Type |= p9p.QTDIR + dir.Mode |= p9p.DMDIR + r.info = &dir + + return dir +} + +func (r *NamespacesRef) Get(name string) (Ref, error) { + namespace, err := r.namespaceInformer.Lister().Get(name) + if apierrors.IsNotFound(err) { + return nil, p9p.ErrNotfound + } + if err != nil { + return nil, err + } + + return &NamespaceRef{ + namespace: namespace, + client: r.client, + session: r.session, + }, nil +} + +func (r *NamespacesRef) Read(ctx context.Context, p []byte, offset int64) (n int, err error) { + if r.readdir != nil { + return r.readdir.Read(ctx, p, offset) + } + + namespaces, err := r.namespaceInformer.Lister().List(labels.Everything()) + if err != nil { + return 0, err + } + + namespaceRefs := make([]NamespaceRef, 0, len(namespaces)) + + for _, namespace := range namespaces { + namespace := namespace + namespaceRefs = append(namespaceRefs, NamespaceRef{ + namespace: namespace, + client: r.client, + session: r.session, + }) + } + + r.readdir = p9p.NewReaddir(p9p.NewCodec(), func() (p9p.Dir, error) { + if len(namespaceRefs) == 0 { + return p9p.Dir{}, io.EOF + } + + ns := namespaceRefs[0] + namespaceRefs = namespaceRefs[1:] + + return ns.Info(), nil + }) + + return r.readdir.Read(ctx, p, offset) +} diff --git a/pkg/resources/refs.go b/pkg/resources/refs.go new file mode 100644 index 0000000..3ede9e7 --- /dev/null +++ b/pkg/resources/refs.go @@ -0,0 +1,13 @@ +package resources + +import ( + "context" + + "github.com/docker/go-p9p" +) + +type Ref interface { + Info() p9p.Dir + Get(name string) (Ref, error) + Read(ctx context.Context, p []byte, offset int64) (n int, err error) +} diff --git a/pkg/resources/resources.go b/pkg/resources/resources.go new file mode 100644 index 0000000..8c2eed5 --- /dev/null +++ b/pkg/resources/resources.go @@ -0,0 +1,12 @@ +package resources + +import ( + "github.com/docker/go-p9p" + "k8s.io/client-go/informers" +) + +type Session interface { + p9p.Session + GetAuth() (uname, aname string) + Informer() informers.SharedInformerFactory +} diff --git a/pkg/resources/staticdir.go b/pkg/resources/staticdir.go new file mode 100644 index 0000000..cfa969a --- /dev/null +++ b/pkg/resources/staticdir.go @@ -0,0 +1,76 @@ +package resources + +import ( + "context" + "math/rand" + "time" + + "github.com/docker/go-p9p" +) + +type DirRef struct { + path string + info p9p.Dir + session Session + children map[string]Ref + readdir *p9p.Readdir +} + +func NewDirRef(path string, session Session, children map[string]Ref) *DirRef { + d := &DirRef{ + path: path, + session: session, + children: children, + } + d.info = d.createInfo() + + return d +} + +func (d *DirRef) createInfo() p9p.Dir { + dir := p9p.Dir{} + dir.Qid.Path = rand.Uint64() + dir.Qid.Version = 0 + + dir.Name = d.path + dir.Mode = 0664 + dir.Length = 0 + dir.AccessTime = time.Now() + dir.ModTime = time.Now() + dir.MUID = "none" + + uname, _ := d.session.GetAuth() + dir.UID = uname + dir.GID = uname + + dir.Qid.Type |= p9p.QTDIR + dir.Mode |= p9p.DMDIR + + return dir +} + +func (d *DirRef) Info() p9p.Dir { + return d.info +} + +func (d *DirRef) Get(name string) (Ref, error) { + child, ok := d.children[name] + if !ok { + return nil, p9p.ErrNotfound + } + + return child, nil +} + +func (d *DirRef) Read(ctx context.Context, p []byte, offset int64) (n int, err error) { + if d.readdir != nil { + return d.readdir.Read(ctx, p, offset) + } + + dir := make([]p9p.Dir, 0, len(d.children)) + for _, child := range d.children { + dir = append(dir, child.Info()) + } + d.readdir = p9p.NewFixedReaddir(p9p.NewCodec(), dir) + return d.readdir.Read(ctx, p, offset) +} |