From b35b371065165c014bf4f3673d322fb0d7027b04 Mon Sep 17 00:00:00 2001 From: Terin Stock Date: Wed, 20 Nov 2019 11:10:58 -0800 Subject: feat: add open source code Add the initial open source version of K9P with support for viewing namespaces and deployments. Current support is entirely read-only. Performance is pretty terrible. Needs much more love and care. --- LICENSE | 22 +++++ README.md | 59 ++++++++++++ cmd/k9p/main.go | 102 +++++++++++++++++++++ go.mod | 14 +++ go.sum | 214 +++++++++++++++++++++++++++++++++++++++++++ pkg/k9p/logger/logger.go | 195 +++++++++++++++++++++++++++++++++++++++ pkg/k9p/session.go | 205 +++++++++++++++++++++++++++++++++++++++++ pkg/resources/deployments.go | 184 +++++++++++++++++++++++++++++++++++++ pkg/resources/files.go | 60 ++++++++++++ pkg/resources/namespace.go | 68 ++++++++++++++ pkg/resources/namespaces.go | 110 ++++++++++++++++++++++ pkg/resources/refs.go | 13 +++ pkg/resources/resources.go | 12 +++ pkg/resources/staticdir.go | 76 +++++++++++++++ 14 files changed, 1334 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 cmd/k9p/main.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 pkg/k9p/logger/logger.go create mode 100644 pkg/k9p/session.go create mode 100644 pkg/resources/deployments.go create mode 100644 pkg/resources/files.go create mode 100644 pkg/resources/namespace.go create mode 100644 pkg/resources/namespaces.go create mode 100644 pkg/resources/refs.go create mode 100644 pkg/resources/resources.go create mode 100644 pkg/resources/staticdir.go 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) +} -- cgit 1.4.1