diff --git a/netlify.toml b/netlify.toml index c54889f7..2c71d377 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,7 +1,8 @@ [build] base = "site/" publish = "public/" -command = "hugo" +command = "hugo && cd ./functions && go build -o api ./server" +functions = "functions/" [build.environment] HUGO_VERSION = "0.65.1" @@ -10,7 +11,9 @@ HUGO_VERSION = "0.65.1" HUGO_ENV = "production" [context.deploy-preview] -command = "hugo --buildFuture -b $DEPLOY_PRIME_URL" +command = "hugo --buildFuture -b $DEPLOY_PRIME_URL && cd ./functions && go build -o api ./server" + [context.branch-deploy] -command = "hugo --buildFuture -b $DEPLOY_PRIME_URL" +command = "hugo --buildFuture -b $DEPLOY_PRIME_URL && cd ./functions && go build -o api ./server" + diff --git a/site/config.yaml b/site/config.yaml index 2b4adfee..594f4435 100644 --- a/site/config.yaml +++ b/site/config.yaml @@ -9,3 +9,6 @@ disableKinds: markup: highlight: style: dracula + goldmark: + renderer: + unsafe: true # required for dynamic JS content editing diff --git a/site/content/_index.md b/site/content/_index.md index b7e1ef85..e3655c10 100644 --- a/site/content/_index.md +++ b/site/content/_index.md @@ -11,7 +11,8 @@ Krew helps you: - install them on your machine, - and keep the installed plugins up-to-date. -There are [over 90 kubectl plugins][list] currently distributed on Krew. +There are [ kubectl plugins][list] +currently distributed on Krew. Krew works across all major platforms, like macOS, Linux and Windows. @@ -20,4 +21,4 @@ plugins on multiple platforms easily and makes them discoverable through a centralized plugin repository with Krew. [kpl]: https://kubernetes.io/docs/tasks/extend-kubectl/kubectl-plugins/ -[list]: http://sigs.k8s.io/krew-index/plugins.md +[list]: {{< relref "plugins.md" >}} diff --git a/site/content/docs/user-guide/quickstart.md b/site/content/docs/user-guide/quickstart.md index 4c3f5716..5720c990 100644 --- a/site/content/docs/user-guide/quickstart.md +++ b/site/content/docs/user-guide/quickstart.md @@ -57,4 +57,4 @@ auth-proxy Authentication proxy to a pod or service This is pretty much all you need to know as a user to use Krew. [kpl]: https://kubernetes.io/docs/tasks/extend-kubectl/kubectl-plugins/ -[list]: https://github.com/kubernetes-sigs/krew-index/blob/master/plugins.md +[list]: {{< relref "plugins.md" >}} diff --git a/site/content/docs/user-guide/search.md b/site/content/docs/user-guide/search.md index 4416894c..2e99d8be 100644 --- a/site/content/docs/user-guide/search.md +++ b/site/content/docs/user-guide/search.md @@ -59,5 +59,4 @@ DESCRIPTION: ...{{}} ``` - -[list]: https://github.com/kubernetes-sigs/krew-index/blob/master/plugins.md +[list]: {{< relref "plugins.md" >}} diff --git a/site/content/plugins.md b/site/content/plugins.md new file mode 100644 index 00000000..7b452202 --- /dev/null +++ b/site/content/plugins.md @@ -0,0 +1,27 @@ +--- +title: Kubectl plugins available +slug: plugins +--- + +Below you will find the list of kubectl plugins distributed on the centralized +[krew-index](https://sigs.k8s.io/krew-index). To install these plugins on +your machine: + +1. [Install Krew]({{< relref "docs/user-guide/setup/install.md" >}}) +2. Run `kubectl krew install ` to install a plugin via Krew. + + + + + + + + + + + + + + + +
NameDescriptionRepository
Loading...
diff --git a/site/functions/README.md b/site/functions/README.md new file mode 100644 index 00000000..0de88019 --- /dev/null +++ b/site/functions/README.md @@ -0,0 +1,41 @@ +# Dynamic functions on Krew documentation + +Krew site makes use of Netlify Functions (lambdas) to fetch plugin list +dynamically from `krew-index` repository using GitHub API. + +## Set up on Netlify + +Functions require a one-time set up in the Netlify console. + +Regarding **GitHub API rate limits**: + +- In production, make **sure to set a `GITHUB_ACCESS_TOKEN` environment + variable** with a permissionless "personal access token" to elevate the rate + limits for our functions. + + Dynamic responses from the functions are cached on + Netlify’s CDN for a long time, so this is not a huge problem and a single + token is very likely to suffice a long period of time. + +- During local development, you can hit the GitHub API rate limit as well. + You should set the `GITHUB_ACCESS_TOKEN` environment variable as needed. + +## Local development + +Start `hugo` local iteration server on port 1313: + +``` +cd ./site +hugo serve +``` + +In another terminal window, build and start the functions server on port 8080: + +``` +cd ./functions +go run ./server -port=8080 +``` + +Now, you can reach the website at http://localhost:8080 and the functions at +paths defined in the code e.g. +http://localhost:8080/.netlify/functions/api/plugins. diff --git a/site/functions/go.mod b/site/functions/go.mod new file mode 100644 index 00000000..6042be03 --- /dev/null +++ b/site/functions/go.mod @@ -0,0 +1,17 @@ +module sigs.k8s.io/krew/site/functions + +go 1.15 + +require ( + github.com/apex/gateway v1.1.1 + github.com/aws/aws-lambda-go v1.19.1 + github.com/google/go-github/v32 v32.1.0 + github.com/pkg/errors v0.9.1 + golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be + golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 + gopkg.in/yaml.v2 v2.2.8 + sigs.k8s.io/krew v0.4.0 + sigs.k8s.io/yaml v1.2.0 +) + +// replace sigs.k8s.io/krew => ../../ diff --git a/site/functions/go.sum b/site/functions/go.sum new file mode 100644 index 00000000..bb6a60e3 --- /dev/null +++ b/site/functions/go.sum @@ -0,0 +1,140 @@ +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/apex/gateway v1.1.1 h1:dPE3y2LQ/fSJuZikCOvekqXLyn/Wrbgt10MSECobH/Q= +github.com/apex/gateway v1.1.1/go.mod h1:x7iPY22zu9D8sfrynawEwh1wZEO/kQTRaOM5ye02tWU= +github.com/aws/aws-lambda-go v1.19.1 h1:5iUHbIZ2sG6Yq/J1IN3sWm3+vAB1CWwhI21NffLNuNI= +github.com/aws/aws-lambda-go v1.19.1/go.mod h1:jJmlefzPfGnckuHdXX7/80O3BvUUi12XOkbv4w9SGLU= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +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/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.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +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.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= +github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II= +github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +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/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +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/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +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 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/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/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sahilm/fuzzy v0.0.5/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +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/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/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-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/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-20180909124046-d0be0721c37e/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/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.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +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= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/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.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/apimachinery v0.0.0-20190717022731-0bb8574e0887 h1:JVVkMN2P4a3MNzTkjgCCRwBDAGAENrCsZGLoLtQ5jvI= +k8s.io/apimachinery v0.0.0-20190717022731-0bb8574e0887/go.mod h1:sBJWIJZfxLhp7mRsRyuAE/NfKTr3kXGR1iaqg8O0gJo= +k8s.io/client-go v7.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= +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.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +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-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4= +sigs.k8s.io/krew v0.4.0 h1:Y9qeOcShVUKD0IAAhOwKuwivWkcTfxljZsdlJS8ATCQ= +sigs.k8s.io/krew v0.4.0/go.mod h1:e2cG2GilCwX2JjmeVblZacw7aUyHzIlL27uclbwJY98= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/site/functions/server/main.go b/site/functions/server/main.go new file mode 100644 index 00000000..1bcf552a --- /dev/null +++ b/site/functions/server/main.go @@ -0,0 +1,272 @@ +// Copyright 2020 The Kubernetes Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "net/http" + "net/http/httputil" + "net/url" + "os" + "regexp" + "strings" + "sync" + "time" + + "github.com/apex/gateway" + "github.com/google/go-github/v32/github" + "github.com/pkg/errors" + "golang.org/x/oauth2" + "golang.org/x/sync/errgroup" + "sigs.k8s.io/yaml" + + "sigs.k8s.io/krew/pkg/constants" + krew "sigs.k8s.io/krew/pkg/index" +) + +const ( + orgName = "kubernetes-sigs" + repoName = "krew-index" + pluginsDir = "plugins" + + pluginFetchWorkers = 40 + cacheSeconds = 60 * 60 +) + +var ( + githubRepoPattern = regexp.MustCompile(`.*github\.com/([^/]+/[^/#]+)`) +) + +type PluginCountResponse struct { + Data struct { + Count int `json:"count"` + } `json:"data"` + Error ErrorResponse `json:"error,omitempty"` +} + +type pluginInfo struct { + Name string `json:"name,omitempty"` + Homepage string `json:"homepage,omitempty"` + ShortDescription string `json:"short_description,omitempty"` + GithubRepo string `json:"github_repo,omitempty"` +} + +type ErrorResponse struct { + Message string `json:"message,omitempty"` +} + +type PluginsResponse struct { + Data struct { + Plugins []pluginInfo `json:"plugins,omitempty"` + } `json:"data,omitempty"` + Error ErrorResponse `json:"error"` +} + +func githubClient(ctx context.Context) *github.Client { + var hc *http.Client + + // if not configured, you should configure a GITHUB_ACCESS_TOKEN + // variable on Netlify dashboard for the site. You can create a + // permission-less "personal access token" on GitHub account settings. + if v := os.Getenv("GITHUB_ACCESS_TOKEN"); v != "" { + ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: v}) + hc = oauth2.NewClient(ctx, ts) + } + return github.NewClient(hc) +} + +func pluginCountHandler(w http.ResponseWriter, req *http.Request) { + _, dir, resp, err := githubClient(req.Context()). + Repositories.GetContents(req.Context(), orgName, repoName, pluginsDir, &github.RepositoryContentGetOptions{}) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + writeJSON(w, PluginCountResponse{Error: ErrorResponse{Message: fmt.Sprintf("error retrieving repo contents: %v", err)}}) + return + } + yamls := filterYAMLs(dir) + count := len(yamls) + log.Printf("github response=%s count=%d rate: limit=%d remaining=%d", + resp.Status, count, resp.Rate.Limit, resp.Rate.Remaining) + + var out PluginCountResponse + out.Data.Count = count + + w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", cacheSeconds)) + writeJSON(w, out) +} + +func writeJSON(w io.Writer, v interface{}) { + e := json.NewEncoder(w) + if err := e.Encode(v); err != nil { + log.Printf("json write error: %v", err) + } +} + +func filterYAMLs(entries []*github.RepositoryContent) []*github.RepositoryContent { + var out []*github.RepositoryContent + for _, v := range entries { + if v == nil { + continue + } + if v.GetType() == "file" && strings.HasSuffix(v.GetName(), constants.ManifestExtension) { + out = append(out, v) + } + } + return out +} + +func loggingHandler(f http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + start := time.Now() + log.Printf("[req] > method=%s path=%s", req.Method, req.URL) + defer func() { + log.Printf("[resp] < method=%s path=%s took=%v", req.Method, req.URL, time.Since(start)) + }() + f.ServeHTTP(w, req) + }) +} + +func pluginsHandler(w http.ResponseWriter, req *http.Request) { + ctx := req.Context() + _, dir, resp, err := githubClient(ctx). + Repositories.GetContents(ctx, orgName, repoName, pluginsDir, &github.RepositoryContentGetOptions{}) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + writeJSON(w, PluginsResponse{Error: ErrorResponse{Message: fmt.Sprintf("error retrieving repo contents: %v", err)}}) + return + } + log.Printf("github response=%s rate: limit=%d remaining=%d", + resp.Status, resp.Rate.Limit, resp.Rate.Remaining) + var out PluginsResponse + + plugins, err := fetchPlugins(ctx, filterYAMLs(dir)) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + writeJSON(w, PluginsResponse{Error: ErrorResponse{Message: fmt.Sprintf("failed to fetch plugins: %v", err)}}) + return + } + + for _, v := range plugins { + pi := pluginInfo{ + Name: v.Name, + Homepage: v.Spec.Homepage, + ShortDescription: v.Spec.ShortDescription, + GithubRepo: findRepo(v.Spec.Homepage), + } + out.Data.Plugins = append(out.Data.Plugins, pi) + } + + w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", cacheSeconds)) + writeJSON(w, out) +} + +func fetchPlugins(ctx context.Context, entries []*github.RepositoryContent) ([]*krew.Plugin, error) { + var ( + mu sync.Mutex + out []*krew.Plugin + ) + + queue := make(chan string) + g, ctx := errgroup.WithContext(ctx) + + for i := 0; i < pluginFetchWorkers; i++ { + g.Go(func() error { + for url := range queue { + p, err := readPlugin(url) + if err != nil { + return err + } + mu.Lock() + out = append(out, p) + mu.Unlock() + } + return nil + }) + } + + for _, v := range entries { + url := v.GetDownloadURL() + select { + case <-ctx.Done(): + break + case queue <- url: + } + } + + close(queue) + + return out, g.Wait() +} + +func readPlugin(url string) (*krew.Plugin, error) { + resp, err := http.Get(url) + if err != nil { + return nil, errors.Wrapf(err, "failed to get %s", url) + } + defer resp.Body.Close() + + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.Wrapf(err, "failed to read file %s: %w", url) + } + + var v krew.Plugin + if err = yaml.Unmarshal(b, &v); err != nil { + return nil, errors.Wrapf(err, "failed to parse plugin manifest for %s", url) + } + return &v, nil +} + +func findRepo(homePage string) string { + if matches := githubRepoPattern.FindStringSubmatch(homePage); matches != nil { + return matches[1] + } + + knownHomePages := map[string]string{ + `https://krew.sigs.k8s.io/`: "kubernetes-sigs/krew", + `https://sigs.k8s.io/krew`: "kubernetes-sigs/krew", + `https://kubernetes.github.io/ingress-nginx/kubectl-plugin/`: "kubernetes/ingress-nginx", + `https://kudo.dev/`: "kudobuilder/kudo", + `https://kubevirt.io`: "kubevirt/kubectl-virt-plugin", + `https://popeyecli.io`: "derailed/popeye", + `https://soluble-ai.github.io/kubetap/`: "soluble-ai/kubetap", + } + return knownHomePages[homePage] +} + +func main() { + port := flag.Int("port", -1, `"to debug locally, set a port number"`) + flag.Parse() + local := *port != -1 + + mux := http.NewServeMux() + mux.HandleFunc("/.netlify/functions/api/pluginCount", pluginCountHandler) + mux.HandleFunc("/.netlify/functions/api/plugins", pluginsHandler) + if local { + mux.Handle("/", httputil.NewSingleHostReverseProxy(&url.URL{Scheme: "http", Host: "localhost:1313"})) + } + + handler := loggingHandler(mux) + if local { + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), handler)) + } + log.Fatal(gateway.ListenAndServe("n/a", handler)) +} diff --git a/site/layouts/docs/single.html b/site/layouts/_default/single.html similarity index 100% rename from site/layouts/docs/single.html rename to site/layouts/_default/single.html diff --git a/site/layouts/partials/footer.html b/site/layouts/partials/footer.html index fda5921f..d3a66868 100644 --- a/site/layouts/partials/footer.html +++ b/site/layouts/partials/footer.html @@ -44,5 +44,58 @@ {{ end }} + + + diff --git a/site/layouts/partials/navbar.html b/site/layouts/partials/navbar.html index a3115b9a..4028a4f3 100644 --- a/site/layouts/partials/navbar.html +++ b/site/layouts/partials/navbar.html @@ -16,6 +16,7 @@ {{ $isQuickstart := (eq .Slug "quickstart") }} {{ $isDocs := (and (eq .Section "docs") (ne .Slug "quickstart")) }} + {{ $isPlugins := (and (not .Section) (eq .Slug "plugins"))}} Quickstart @@ -25,6 +26,10 @@ Documentation {{if $isDocs}}(current){{end}} + + Plugins + {{if $isPlugins}}(current){{end}} + GitHub