diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..88595de
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2019, Terin Stock
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7dc0626
--- /dev/null
+++ b/README.md
@@ -0,0 +1,59 @@
+# K9P
+
+A virtual filesystem for Kubernetes cluster state.
+
+## Install
+
+K9P uses Go modules, and can be installed with a Go 1.13+. Goal is to allow operators to
+use familiar unix tools to interact with their Kubernetes clusters, rather than learning kubectl.
+
+```console
+$ git clone https://git.terinstock.com/k9p && cd k9p
+$ go get ./cmd/k9p/...
+```
+
+## Mounting
+
+### Plan 9
+
+K9P can be mounded like other remote 9P servers:
+
+```console
+$ import 'tcp!localhost!1564` /k8s
+```
+
+### Linux
+
+Supported on systems compiled with `CONFIG_NET_9P`, otherwise use 9pfuse.
+
+```console
+# mount -t 9p -o trans=tcp,port=1564 127.0.0.1 /mnt/k8s
+```
+
+### FUSE
+
+K9P can be mounted as a remote 9P server using 9pfuse:
+
+```console
+$ 9pfuse 'tcp!127.0.0.1!1564' $HOME/k8s
+```
+
+### Applications
+
+So applications can directly interact with 9P servers, such as 9p from plan9ports:
+
+```console
+$ p9 -a 'tcp!9p.example.com!1564' ls -l /
+d-r-xr-x-r-x I 0 terin terin 0 July 3 2019 cluster-scoped
+d-r-xr-x-r-x I 0 terin terin 0 July 3 2019 namespaces
+```
+
+## Future
+
+K9P is in a very early WIP state. There's lots of improvements, contributions welcome.
+
+* Support passing authentication to the server for the attach mount.
+* Load resources on-demand, rather than creating informers on attach.
+* Add support for way more resource types, including CRDs.
+* Mutate and remove resources.
+* Port forwards modeled after Plan 9's netfs?
diff --git a/cmd/k9p/main.go b/cmd/k9p/main.go
new file mode 100644
index 0000000..dd0ed80
--- /dev/null
+++ b/cmd/k9p/main.go
@@ -0,0 +1,102 @@
+package main
+
+import (
+ "context"
+ "flag"
+ "net"
+ "os"
+
+ p9p "github.com/docker/go-p9p"
+ "github.com/oklog/run"
+ "github.com/rs/zerolog"
+ "go.terinstock.com/k9p/pkg/k9p"
+ "go.terinstock.com/k9p/pkg/k9p/logger"
+ "k8s.io/client-go/kubernetes"
+ _ "k8s.io/client-go/plugin/pkg/client/auth"
+ "k8s.io/client-go/tools/clientcmd"
+ "k8s.io/klog"
+)
+
+func main() {
+ fs := flag.NewFlagSet("k9p", flag.ExitOnError)
+ klog.InitFlags(fs)
+
+ fs.Set("logtostderr", "false")
+ fs.Set("alsologtostderr", "false")
+
+ var (
+ prettyLog = fs.Bool("pretty-log", false, "output human-friendly logs")
+ master = fs.String("master", "", "The address of the Kubernetes API server (overrides any value in kubeconfig).")
+ kubeconfig = fs.String("kubeconfig", "", "Path to kubeconfig file with authorization and master location information.")
+ bind9p = fs.String("bind-9p", ":564", "The address the 9P server should bind and listen on")
+ )
+ fs.Parse(os.Args[1:])
+
+ ctx := context.Background()
+
+ var log zerolog.Logger
+ if *prettyLog {
+ log = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().Timestamp().Logger()
+ } else {
+ log = zerolog.New(os.Stderr)
+ }
+
+ client, err := createClient(*master, *kubeconfig)
+ if err != nil {
+ log.Fatal().Err(err).Send()
+ }
+
+ klog.SetOutput(log.With().Str("component", "klog").Logger())
+
+ var g run.Group
+ {
+ ln, err := net.Listen("tcp", *bind9p)
+ if err != nil {
+ log.Fatal().Err(err).Msg("error listening")
+ }
+
+ g.Add(func() error {
+ for {
+ c, err := ln.Accept()
+ if err != nil {
+ log.Warn().Err(err).Msg("error accepting")
+ continue
+ }
+
+ go func(conn net.Conn) {
+ defer conn.Close()
+
+ ctx, cancel := context.WithCancel(context.WithValue(ctx, "conn", conn))
+ defer cancel()
+
+ log.Info().Str("remote", conn.RemoteAddr().String()).Msg("connected")
+
+ var session p9p.Session
+ {
+ ksession := k9p.New(ctx, client)
+ ksession.WaitForCacheSync(ctx.Done())
+ session = logger.New(
+ log.With().Str("component", "9p").Logger(),
+ ksession,
+ )
+ }
+ if err := p9p.ServeConn(ctx, conn, p9p.Dispatch(session)); err != nil {
+ log.Warn().Err(err).Msg("ServeConn")
+ }
+ }(c)
+ }
+ }, func(error) {
+ ln.Close()
+ })
+ }
+
+ log.Info().Err(g.Run())
+}
+
+func createClient(master string, kubeconfig string) (kubernetes.Interface, error) {
+ config, err := clientcmd.BuildConfigFromFlags(master, kubeconfig)
+ if err != nil {
+ return nil, err
+ }
+ return kubernetes.NewForConfig(config)
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..606f59d
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,14 @@
+module go.terinstock.com/k9p
+
+go 1.13
+
+require (
+ github.com/docker/go-p9p v0.0.0-20191112112554-37d97cf40d03
+ github.com/oklog/run v1.0.0
+ github.com/rs/zerolog v1.17.2
+ k8s.io/api v0.0.0-20191114100352-16d7abae0d2a
+ k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb
+ k8s.io/client-go v0.0.0-20191114101535-6c5935290e33
+ k8s.io/klog v1.0.0
+ sigs.k8s.io/yaml v1.1.0
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..fa07d88
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,214 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs=
+github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
+github.com/Azure/go-autorest/autorest/adal v0.5.0 h1:q2gDruN08/guU9vAjuPWff0+QIrpH6ediguzdAzXAUU=
+github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
+github.com/Azure/go-autorest/autorest/date v0.1.0 h1:YGrhWfrgtFs84+h0o46rJrlmsZtyZRg470CqAXTZaGM=
+github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
+github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
+github.com/Azure/go-autorest/autorest/mocks v0.2.0 h1:Ww5g4zThfD/6cLb4z6xxgeyDa7QDkizMkJKe0ysZXp0=
+github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
+github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
+github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
+github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
+github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
+github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/docker/go-p9p v0.0.0-20191112112554-37d97cf40d03 h1:HiIKimWyR71ORJgvm/aWL/cqeYMpOy4eObwJogG8FAw=
+github.com/docker/go-p9p v0.0.0-20191112112554-37d97cf40d03/go.mod h1:GDue7j/yh3AtNoUK0ihznL9JiZVn92CV9bUrYaD4NOc=
+github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
+github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
+github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
+github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
+github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
+github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
+github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
+github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
+github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
+github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8=
+github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
+github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k=
+github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
+github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o=
+github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
+github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
+github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
+github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
+github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
+github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
+github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
+github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
+github.com/rs/zerolog v1.17.2 h1:RMRHFw2+wF7LO0QqtELQwo8hqSmqISyCJeFeAAuWcRo=
+github.com/rs/zerolog v1.17.2/go.mod h1:9nvC1axdVrAHcu/s9taAVfBuIdTZLVQmKQyvrUjF5+I=
+github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
+golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc h1:gkKoSkUmnU6bpS/VhkuO27bzQeSA51uaEfbOW5dNb68=
+golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o=
+gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+k8s.io/api v0.0.0-20191114100352-16d7abae0d2a h1:86XISgFlG7lPOWj6wYLxd+xqhhVt/WQjS4Tf39rP09s=
+k8s.io/api v0.0.0-20191114100352-16d7abae0d2a/go.mod h1:qetVJgs5i8jwdFIdoOZ70ks0ecgU+dYwqZ2uD1srwOU=
+k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb h1:ZUNsbuPdXWrj0rZziRfCWcFg9ZP31OKkziqCbiphznI=
+k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ=
+k8s.io/client-go v0.0.0-20191114101535-6c5935290e33 h1:07mhG/2oEoo3N+sHVOo0L9PJ/qvbk3N5n2dj8IWefnQ=
+k8s.io/client-go v0.0.0-20191114101535-6c5935290e33/go.mod h1:4L/zQOBkEf4pArQJ+CMk1/5xjA30B5oyWv+Bzb44DOw=
+k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
+k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
+k8s.io/klog v0.4.0 h1:lCJCxf/LIowc2IGS9TPjWDyXY4nOmdGdfcwwDQCOURQ=
+k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
+k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
+k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
+k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
+k8s.io/utils v0.0.0-20190801114015-581e00157fb1 h1:+ySTxfHnfzZb9ys375PXNlLhkJPLKgHajBU0N62BDvE=
+k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
+sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
+sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
+sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
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)
+}
|