From d591aa614333fec8a11acbd521ebe401d5110a09 Mon Sep 17 00:00:00 2001 From: Elijah Oyekunle Date: Thu, 1 Aug 2019 11:08:15 +0100 Subject: [PATCH] CRD Support - Frontend (#4030) * add crd to sidenav * add crd routing * add crd module * fix headers * add divider to sidebar * implement crd list * add crd detail interface * add more columns to table * update crd detail fetch * show crd conditions in crd detail page * added types for crd objects * implement crd objects list * add crd information card and accepted names * add versions table to crd detail * update version table * lint files * add actionbar buttons to crd detail * add status icon * lint fix * update crd object route * wip * update crdobject api * crd object extending resourcelistbase * update objects list * extend verber to support raw crd objects * enable action bar buttons on object detail page * implement raw data display for CRD object * fix failing test * add code section to crdobject * add syntax highlighting * fix issue in i18n file * fix html formatting --- angular.json | 1 + go.mod | 6 +- go.sum | 11 + i18n/messages.fr.xlf | 254 +++++++++++++++--- i18n/messages.ja.xlf | 198 +++++++++++++- i18n/messages.xlf | 201 +++++++++++++- i18n/messages.zh.xlf | 198 +++++++++++++- package-lock.json | 129 ++++----- package.json | 2 + src/app/backend/api/types.go | 12 +- src/app/backend/auth/manager_test.go | 4 +- src/app/backend/client/api/types.go | 4 +- src/app/backend/client/manager.go | 6 +- src/app/backend/client/manager_test.go | 3 +- src/app/backend/client/verber.go | 97 ++++--- src/app/backend/client/verber_test.go | 15 +- src/app/backend/errors/errors.go | 11 + src/app/backend/handler/apihandler.go | 39 ++- .../customresourcedefinition/client.go | 14 +- .../customresourcedefinition/common.go | 36 +-- .../customresourcedefinition/detail.go | 59 ++-- .../resource/customresourcedefinition/list.go | 30 ++- .../customresourcedefinition/list_test.go | 7 +- .../customresourcedefinition/objects.go | 139 ++++++---- src/app/frontend/chrome/nav/template.html | 9 + src/app/frontend/chrome/routing.ts | 3 + .../components/breadcrumbs/component.ts | 4 +- .../common/components/condition/component.ts | 27 +- .../common/components/condition/template.html | 4 +- src/app/frontend/common/components/module.ts | 6 + .../components/resourcelist/crd/component.ts | 78 ++++++ .../components/resourcelist/crd/template.html | 110 ++++++++ .../resourcelist/crdobject/component.ts | 58 ++++ .../resourcelist/crdobject/template.html | 88 ++++++ .../resourcelist/crdversion/component.ts | 37 +++ .../resourcelist/crdversion/template.html | 56 ++++ .../components/resourcelist/groupids.ts | 2 + .../common/services/resource/endpoint.ts | 4 +- .../common/services/resource/resource.ts | 2 +- src/app/frontend/crd/crdobject/component.ts | 105 ++++++++ src/app/frontend/crd/crdobject/template.html | 34 +++ src/app/frontend/crd/detail/component.ts | 63 +++++ src/app/frontend/crd/detail/template.html | 91 +++++++ src/app/frontend/crd/list/component.ts | 18 ++ src/app/frontend/crd/module.ts | 29 ++ src/app/frontend/crd/routing.ts | 51 ++++ src/app/frontend/typings/backendapi.d.ts | 55 +++- 47 files changed, 2110 insertions(+), 300 deletions(-) create mode 100644 src/app/frontend/common/components/resourcelist/crd/component.ts create mode 100644 src/app/frontend/common/components/resourcelist/crd/template.html create mode 100644 src/app/frontend/common/components/resourcelist/crdobject/component.ts create mode 100644 src/app/frontend/common/components/resourcelist/crdobject/template.html create mode 100644 src/app/frontend/common/components/resourcelist/crdversion/component.ts create mode 100644 src/app/frontend/common/components/resourcelist/crdversion/template.html create mode 100644 src/app/frontend/crd/crdobject/component.ts create mode 100644 src/app/frontend/crd/crdobject/template.html create mode 100644 src/app/frontend/crd/detail/component.ts create mode 100644 src/app/frontend/crd/detail/template.html create mode 100644 src/app/frontend/crd/list/component.ts create mode 100644 src/app/frontend/crd/module.ts create mode 100644 src/app/frontend/crd/routing.ts diff --git a/angular.json b/angular.json index 08fd4e144449..c82b97ba3d3e 100644 --- a/angular.json +++ b/angular.json @@ -24,6 +24,7 @@ } ], "styles": [ + "node_modules/highlight.js/styles/github.css", "node_modules/material-design-icons/iconfont/material-icons.css", "node_modules/roboto-fontface/css/roboto/roboto-fontface.css", "node_modules/xterm/dist/xterm.css", diff --git a/go.mod b/go.mod index dfa14dfc9bca..bc510a8ad572 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,8 @@ module github.com/kubernetes/dashboard go 1.12 require ( + github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect + github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 // indirect github.com/docker/distribution v2.7.0+incompatible github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f // indirect @@ -22,8 +24,9 @@ require ( github.com/onsi/gomega v1.5.0 // indirect github.com/opencontainers/go-digest v1.0.0-rc1 // indirect github.com/prometheus/client_golang v0.9.2 - github.com/prometheus/common v0.0.0-20181218105931-67670fe90761 // indirect + github.com/prometheus/common v0.0.0-20181218105931-67670fe90761 github.com/prometheus/procfs v0.0.0-20190102135031-14fa7590c24d // indirect + github.com/sirupsen/logrus v1.4.2 // indirect github.com/spf13/pflag v1.0.3 golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 // indirect golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 @@ -32,6 +35,7 @@ require ( golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f // indirect golang.org/x/text v0.3.2 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c // indirect + gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/igm/sockjs-go.v2 v2.0.0 gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index cffc9ffd1acf..7868a05236ad 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,9 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -58,6 +62,7 @@ github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 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= @@ -92,11 +97,14 @@ github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190102135031-14fa7590c24d h1:iSxvUGRUGQTUgEo5+8TqMVQoH5AdaphEIjh8AnM7TPo= github.com/prometheus/procfs v0.0.0-20190102135031-14fa7590c24d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/pflag v1.0.1/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/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= @@ -127,6 +135,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/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.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= @@ -143,6 +152,8 @@ google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO50 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= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 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= diff --git a/i18n/messages.fr.xlf b/i18n/messages.fr.xlf index 28f8f9791db3..bd419f760caf 100644 --- a/i18n/messages.fr.xlf +++ b/i18n/messages.fr.xlf @@ -605,6 +605,18 @@ ../src/app/frontend/common/components/creator/template.html 48 + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 51 + + + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 41 + + + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 36 + ../src/app/frontend/common/components/resourcelist/daemonset/template.html 57 @@ -693,6 +705,10 @@ ../src/app/frontend/common/components/resourcelist/configmap/template.html 54 + + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 52 + ../src/app/frontend/common/components/resourcelist/daemonset/template.html 70 @@ -777,6 +793,14 @@ ../src/app/frontend/common/components/resourcelist/configmap/template.html 70 + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 80 + + + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 58 + ../src/app/frontend/common/components/resourcelist/daemonset/template.html 94 @@ -1314,43 +1338,51 @@ Items:  Éléments :  - ../src/app/frontend/common/components/resourcelist/node/template.html + ../src/app/frontend/common/components/resourcelist/cronjob/template.html 23 - ../src/app/frontend/common/components/resourcelist/pod/template.html - 24 + ../src/app/frontend/common/components/resourcelist/clusterrole/template.html + 23 - ../src/app/frontend/common/components/resourcelist/replicaset/template.html + ../src/app/frontend/common/components/resourcelist/configmap/template.html + 23 + + + ../src/app/frontend/common/components/condition/template.html 26 - ../src/app/frontend/common/components/resourcelist/namespace/template.html + ../src/app/frontend/common/components/resourcelist/crd/template.html 23 - ../src/app/frontend/common/components/resourcelist/persistentvolume/template.html - 23 + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 24 - ../src/app/frontend/common/components/resourcelist/clusterrole/template.html - 23 + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 26 - ../src/app/frontend/common/components/resourcelist/storageclass/template.html + ../src/app/frontend/common/components/resourcelist/daemonset/template.html 23 - ../src/app/frontend/common/components/resourcelist/cronjob/template.html + ../src/app/frontend/common/components/resourcelist/deployment/template.html 23 - ../src/app/frontend/common/components/resourcelist/daemonset/template.html + ../src/app/frontend/common/components/endpoint/cardlist/template.html + 26 + + + ../src/app/frontend/common/components/resourcelist/event/template.html 23 - ../src/app/frontend/common/components/resourcelist/deployment/template.html + ../src/app/frontend/common/components/resourcelist/ingress/template.html 23 @@ -1358,25 +1390,29 @@ 26 - ../src/app/frontend/common/components/resourcelist/replicationcontroller/template.html + ../src/app/frontend/common/components/resourcelist/namespace/template.html 23 - ../src/app/frontend/common/components/resourcelist/statefulset/template.html + ../src/app/frontend/common/components/resourcelist/node/template.html 23 - ../src/app/frontend/common/components/resourcelist/configmap/template.html - 23 + ../src/app/frontend/common/components/resourcelist/pod/template.html + 24 - ../src/app/frontend/common/components/resourcelist/secret/template.html + ../src/app/frontend/common/components/resourcelist/persistentvolume/template.html 23 ../src/app/frontend/common/components/resourcelist/persistentvolumeclaim/template.html 23 + + ../src/app/frontend/common/components/policyrule/template.html + 26 + ../src/app/frontend/common/components/quotas/template.html 26 @@ -1386,28 +1422,28 @@ 26 - ../src/app/frontend/common/components/resourcelist/ingress/template.html - 23 + ../src/app/frontend/common/components/resourcelist/replicaset/template.html + 26 - ../src/app/frontend/common/components/resourcelist/service/template.html + ../src/app/frontend/common/components/resourcelist/replicationcontroller/template.html 23 - ../src/app/frontend/common/components/endpoint/cardlist/template.html - 26 + ../src/app/frontend/common/components/resourcelist/storageclass/template.html + 23 - ../src/app/frontend/common/components/resourcelist/event/template.html + ../src/app/frontend/common/components/resourcelist/statefulset/template.html 23 - ../src/app/frontend/common/components/condition/template.html - 26 + ../src/app/frontend/common/components/resourcelist/secret/template.html + 23 - ../src/app/frontend/common/components/policyrule/template.html - 26 + ../src/app/frontend/common/components/resourcelist/service/template.html + 23 @@ -1833,6 +1869,10 @@ ../src/app/frontend/common/components/creator/template.html 58 + + ../src/app/frontend/crd/detail/template.html + 62 + CPU requests (cores) @@ -1922,6 +1962,74 @@ 38 + + Custom Resource Definitions + Custom Resource Definitions + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 21 + + + + Group + Group + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 60 + + + ../src/app/frontend/crd/detail/template.html + 38 + + + + Full Name + Full Name + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 66 + + + + Namespaced + Namespaced + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 72 + + + + Objects + Objects + + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 21 + + + + Versions + Versions + + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 20 + + + + Served + Served + + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 42 + + + + Storage + Storage + + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 48 + + Namespaces Espacs de noms @@ -2330,6 +2438,78 @@ 35 + + Resource Information + Resource Information + + ../src/app/frontend/crd/detail/template.html + 22 + + + + Version + Version + + ../src/app/frontend/crd/detail/template.html + 28 + + + + Scope + Scope + + ../src/app/frontend/crd/detail/template.html + 33 + + + + Accepted Names + Accepted Names + + ../src/app/frontend/crd/detail/template.html + 46 + + + + Plural + Plural + + ../src/app/frontend/crd/detail/template.html + 52 + + + + Singular + Singular + + ../src/app/frontend/crd/detail/template.html + 57 + + + + List Kind + List Kind + + ../src/app/frontend/crd/detail/template.html + 67 + + + + Short Names + Short Names + + ../src/app/frontend/crd/detail/template.html + 72 + + + + Categories + Categories + + ../src/app/frontend/crd/detail/template.html + 77 + + About À propos @@ -2582,13 +2762,23 @@ 121 + + Custom Resource Definitions + + Custom Resource Definitions + + + ../src/app/frontend/chrome/nav/template.html + 130 + + Settings Paramètres ../src/app/frontend/chrome/nav/template.html - 130 + 139 @@ -2597,7 +2787,7 @@ À propos ../src/app/frontend/chrome/nav/template.html - 135 + 144 @@ -4245,6 +4435,10 @@ Data Données + + ../src/app/frontend/crd/crdobject/template.html + 22 + ../src/app/frontend/resource/config/configmap/detail/template.html 23 diff --git a/i18n/messages.ja.xlf b/i18n/messages.ja.xlf index b6f4ba2b78aa..9e1d704f8435 100644 --- a/i18n/messages.ja.xlf +++ b/i18n/messages.ja.xlf @@ -541,6 +541,18 @@ ../src/app/frontend/common/components/condition/template.html 26 + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 23 + + + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 24 + + + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 26 + ../src/app/frontend/common/components/resourcelist/daemonset/template.html 23 @@ -641,6 +653,18 @@ ../src/app/frontend/common/components/creator/template.html 48 + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 51 + + + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 41 + + + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 36 + ../src/app/frontend/common/components/resourcelist/daemonset/template.html 57 @@ -729,6 +753,10 @@ ../src/app/frontend/common/components/resourcelist/configmap/template.html 54 + + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 52 + ../src/app/frontend/common/components/resourcelist/daemonset/template.html 70 @@ -925,6 +953,14 @@ ../src/app/frontend/common/components/resourcelist/configmap/template.html 70 + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 80 + + + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 58 + ../src/app/frontend/common/components/resourcelist/daemonset/template.html 94 @@ -1245,6 +1281,10 @@ ../src/app/frontend/common/components/creator/template.html 58 + + ../src/app/frontend/crd/detail/template.html + 62 + Pods @@ -1359,6 +1399,74 @@ 38 + + Custom Resource Definitions + Custom Resource Definitions + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 21 + + + + Group + Group + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 60 + + + ../src/app/frontend/crd/detail/template.html + 38 + + + + Full Name + Full Name + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 66 + + + + Namespaced + Namespaced + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 72 + + + + Objects + Objects + + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 21 + + + + Versions + Versions + + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 20 + + + + Served + Served + + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 42 + + + + Storage + Storage + + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 48 + + Daemon Sets デーモンセット @@ -2392,13 +2500,23 @@ 121 + + Custom Resource Definitions + + Custom Resource Definitions + + + ../src/app/frontend/chrome/nav/template.html + 130 + + Settings 設定 ../src/app/frontend/chrome/nav/template.html - 130 + 139 @@ -2407,7 +2525,7 @@ Kubernetes Dashboard について ../src/app/frontend/chrome/nav/template.html - 135 + 144 @@ -2708,6 +2826,78 @@ 35 + + Resource Information + Resource Information + + ../src/app/frontend/crd/detail/template.html + 22 + + + + Version + Version + + ../src/app/frontend/crd/detail/template.html + 28 + + + + Scope + Scope + + ../src/app/frontend/crd/detail/template.html + 33 + + + + Accepted Names + Accepted Names + + ../src/app/frontend/crd/detail/template.html + 46 + + + + Plural + Plural + + ../src/app/frontend/crd/detail/template.html + 52 + + + + Singular + Singular + + ../src/app/frontend/crd/detail/template.html + 57 + + + + List Kind + List Kind + + ../src/app/frontend/crd/detail/template.html + 67 + + + + Short Names + Short Names + + ../src/app/frontend/crd/detail/template.html + 72 + + + + Categories + Categories + + ../src/app/frontend/crd/detail/template.html + 77 + + Create from input 入力して作成 @@ -4219,6 +4409,10 @@ Data データ + + ../src/app/frontend/crd/crdobject/template.html + 22 + ../src/app/frontend/resource/config/configmap/detail/template.html 23 diff --git a/i18n/messages.xlf b/i18n/messages.xlf index 8651984a996a..7817f6f8ca28 100644 --- a/i18n/messages.xlf +++ b/i18n/messages.xlf @@ -653,6 +653,18 @@ ../src/app/frontend/common/components/condition/template.html 26 + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 23 + + + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 24 + + + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 26 + ../src/app/frontend/common/components/resourcelist/daemonset/template.html 23 @@ -752,6 +764,18 @@ ../src/app/frontend/common/components/creator/template.html 48 + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 51 + + + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 41 + + + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 36 + ../src/app/frontend/common/components/resourcelist/daemonset/template.html 57 @@ -839,6 +863,10 @@ ../src/app/frontend/common/components/resourcelist/configmap/template.html 54 + + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 52 + ../src/app/frontend/common/components/resourcelist/daemonset/template.html 70 @@ -1029,6 +1057,14 @@ ../src/app/frontend/common/components/resourcelist/configmap/template.html 70 + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 80 + + + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 58 + ../src/app/frontend/common/components/resourcelist/daemonset/template.html 94 @@ -1329,6 +1365,10 @@ ../src/app/frontend/common/components/creator/template.html 58 + + ../src/app/frontend/crd/detail/template.html + 62 + Age @@ -1397,6 +1437,66 @@ 38 + + Custom Resource Definitions + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 21 + + + + Group + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 60 + + + ../src/app/frontend/crd/detail/template.html + 38 + + + + Full Name + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 66 + + + + Namespaced + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 72 + + + + Objects + + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 21 + + + + Versions + + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 20 + + + + Served + + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 42 + + + + Storage + + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 48 + + Endpoints @@ -2205,12 +2305,20 @@ 121 + + Custom Resource Definitions + + + ../src/app/frontend/chrome/nav/template.html + 130 + + Settings ../src/app/frontend/chrome/nav/template.html - 130 + 139 @@ -2218,7 +2326,7 @@ ../src/app/frontend/chrome/nav/template.html - 135 + 144 @@ -2475,6 +2583,84 @@ 35 + + Resource Information + + ../src/app/frontend/crd/detail/template.html + 22 + + + + Version + + ../src/app/frontend/crd/detail/template.html + 28 + + + + Scope + + ../src/app/frontend/crd/detail/template.html + 33 + + + + Accepted Names + + ../src/app/frontend/crd/detail/template.html + 46 + + + + Plural + + ../src/app/frontend/crd/detail/template.html + 52 + + + + Singular + + ../src/app/frontend/crd/detail/template.html + 57 + + + + List Kind + + ../src/app/frontend/crd/detail/template.html + 67 + + + + Short Names + + ../src/app/frontend/crd/detail/template.html + 72 + + + + Categories + + ../src/app/frontend/crd/detail/template.html + 77 + + + + Data + + ../src/app/frontend/crd/crdobject/template.html + 22 + + + ../src/app/frontend/resource/config/configmap/detail/template.html + 23 + + + ../src/app/frontend/resource/config/secret/detail/template.html + 22 + + Create from input @@ -3789,17 +3975,6 @@ 320 - - Data - - ../src/app/frontend/resource/config/configmap/detail/template.html - 23 - - - ../src/app/frontend/resource/config/secret/detail/template.html - 22 - - There is no data to display. diff --git a/i18n/messages.zh.xlf b/i18n/messages.zh.xlf index 4801355692d8..a3fbe59aaf4a 100644 --- a/i18n/messages.zh.xlf +++ b/i18n/messages.zh.xlf @@ -733,6 +733,18 @@ ../src/app/frontend/common/components/condition/template.html 26 + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 23 + + + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 24 + + + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 26 + ../src/app/frontend/common/components/resourcelist/daemonset/template.html 23 @@ -833,6 +845,18 @@ ../src/app/frontend/common/components/creator/template.html 48 + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 51 + + + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 41 + + + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 36 + ../src/app/frontend/common/components/resourcelist/daemonset/template.html 57 @@ -921,6 +945,10 @@ ../src/app/frontend/common/components/resourcelist/configmap/template.html 54 + + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 52 + ../src/app/frontend/common/components/resourcelist/daemonset/template.html 70 @@ -1117,6 +1145,14 @@ ../src/app/frontend/common/components/resourcelist/configmap/template.html 70 + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 80 + + + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 58 + ../src/app/frontend/common/components/resourcelist/daemonset/template.html 94 @@ -1437,6 +1473,10 @@ ../src/app/frontend/common/components/creator/template.html 58 + + ../src/app/frontend/crd/detail/template.html + 62 + Age @@ -1508,6 +1548,74 @@ 38 + + Custom Resource Definitions + Custom Resource Definitions + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 21 + + + + Group + Group + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 60 + + + ../src/app/frontend/crd/detail/template.html + 38 + + + + Full Name + Full Name + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 66 + + + + Namespaced + Namespaced + + ../src/app/frontend/common/components/resourcelist/crd/template.html + 72 + + + + Objects + Objects + + ../src/app/frontend/common/components/resourcelist/crdobject/template.html + 21 + + + + Versions + Versions + + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 20 + + + + Served + Served + + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 42 + + + + Storage + Storage + + ../src/app/frontend/common/components/resourcelist/crdversion/template.html + 48 + + Endpoints Endpoints @@ -2446,6 +2554,16 @@ 121 + + Custom Resource Definitions + + Custom Resource Definitions + + + ../src/app/frontend/chrome/nav/template.html + 130 + + Settings @@ -2453,7 +2571,7 @@ ../src/app/frontend/chrome/nav/template.html - 130 + 139 @@ -2463,7 +2581,7 @@ ../src/app/frontend/chrome/nav/template.html - 135 + 144 @@ -2770,6 +2888,78 @@ 35 + + Resource Information + Resource Information + + ../src/app/frontend/crd/detail/template.html + 22 + + + + Version + Version + + ../src/app/frontend/crd/detail/template.html + 28 + + + + Scope + Scope + + ../src/app/frontend/crd/detail/template.html + 33 + + + + Accepted Names + Accepted Names + + ../src/app/frontend/crd/detail/template.html + 46 + + + + Plural + Plural + + ../src/app/frontend/crd/detail/template.html + 52 + + + + Singular + Singular + + ../src/app/frontend/crd/detail/template.html + 57 + + + + List Kind + List Kind + + ../src/app/frontend/crd/detail/template.html + 67 + + + + Short Names + Short Names + + ../src/app/frontend/crd/detail/template.html + 72 + + + + Categories + Categories + + ../src/app/frontend/crd/detail/template.html + 77 + + Create from input 输入并创建 @@ -4353,6 +4543,10 @@ Data 数据 + + ../src/app/frontend/crd/crdobject/template.html + 22 + ../src/app/frontend/resource/config/configmap/detail/template.html 23 diff --git a/package-lock.json b/package-lock.json index 8f666d3286a4..4e1b235bdb24 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1995,9 +1995,9 @@ } }, "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", "dev": true, "requires": { "@babel/highlight": "^7.0.0" @@ -2112,6 +2112,15 @@ "lodash": "^4.17.13" }, "dependencies": { + "@babel/code-frame": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -2653,6 +2662,11 @@ "@types/node": "*" } }, + "@types/highlight.js": { + "version": "9.12.3", + "resolved": "https://registry.npmjs.org/@types/highlight.js/-/highlight.js-9.12.3.tgz", + "integrity": "sha512-pGF/zvYOACZ/gLGWdQH8zSwteQS1epp68yRcVLJMgUck/MjEn/FBYmPub9pXT8C1e4a8YZfHo1CKyV8q1vKUnQ==" + }, "@types/jasmine": { "version": "3.3.16", "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.3.16.tgz", @@ -2967,9 +2981,9 @@ "integrity": "sha512-wotVzxv5YClvwOjiuXNyGm4j/CnKoFIoTnnXNmi1nTHjr7hXMMjQeytcnbFua4thaJ5vvpVEDv0utmjqsrp3Jw==" }, "acorn": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.2.1.tgz", - "integrity": "sha512-JD0xT5FCRDNyjDda3Lrg/IxFscp9q4tiYtxE1/nOzlKCk7hIRuYjhq1kCNkbPjMRMZuFq20HNQn1I9k8Oj0E+Q==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.2.0.tgz", + "integrity": "sha512-8oe72N3WPMjA+2zVG71Ia0nXZ8DpQH+QyyHO+p06jT8eg8FGG3FbcUIi8KziHlAfheJQZeoqbvq1mQSQHXKYLw==", "dev": true }, "acorn-dynamic-import": { @@ -3203,9 +3217,9 @@ } }, "arg": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.1.tgz", - "integrity": "sha512-SlmP3fEA88MBv0PypnXZ8ZfJhwmDeIE3SP71j37AiXQBXYosPV0x6uISAaHYSlSVhmHOVkomen0tbGk6Anlebw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.0.tgz", + "integrity": "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==", "dev": true }, "argparse": { @@ -3435,20 +3449,12 @@ "dev": true }, "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", "dev": true, "requires": { - "lodash": "^4.17.14" - }, - "dependencies": { - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - } + "lodash": "^4.17.11" } }, "async-done": { @@ -4608,12 +4614,6 @@ "requires": { "ansi-regex": "^4.1.0" } - }, - "type-fest": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", - "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", - "dev": true } } }, @@ -7874,9 +7874,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.200", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.200.tgz", - "integrity": "sha512-PUurrpyDA74MuAjJRD+79ss5BqJlU3mdArRbuu4wO/dt6jc3Ic/6BDmFJxkdwbfq39cHf/XKm2vW98XSvut9Dg==", + "version": "1.3.188", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.188.tgz", + "integrity": "sha512-tEQcughYIMj8WDMc59EGEtNxdGgwal/oLLTDw+NEqJRJwGflQvH3aiyiexrWeZOETP4/ko78PVr6gwNhdozvuQ==", "dev": true }, "elegant-spinner": { @@ -10873,6 +10873,11 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, + "highlight.js": { + "version": "9.15.8", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.8.tgz", + "integrity": "sha512-RrapkKQWwE+wKdF73VsOa2RQdIoO3mxwJ4P8mhbI6KYJUraUHRKM5w5zQQKXNk0xNL4UVRdulV9SBJcmzJNzVA==" + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -11070,12 +11075,6 @@ "slash": "^3.0.0" }, "dependencies": { - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -11086,15 +11085,6 @@ "path-exists": "^4.0.0" } }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -11157,6 +11147,12 @@ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true } } }, @@ -12244,6 +12240,10 @@ "useragent": "2.3.0" }, "dependencies": { + "fsevents": { + "version": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz", + "integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==" + }, "mime": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", @@ -14087,9 +14087,9 @@ } }, "node-releases": { - "version": "1.1.26", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.26.tgz", - "integrity": "sha512-fZPsuhhUHMTlfkhDLGtfY80DSJTjOcx+qD1j5pqPkuhUHVS7xHZIg9EE4DHK8O3f0zTxXHX5VIkDG8pu98/wfQ==", + "version": "1.1.25", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.25.tgz", + "integrity": "sha512-fI5BXuk83lKEoZDdH3gRhtsNgh05/wZacuXkgbiYkceE7+QIMXOg98n9ZV7mz27B+kFHnqHcUpscZZlGRSmTpQ==", "dev": true, "requires": { "semver": "^5.3.0" @@ -15301,9 +15301,9 @@ "dev": true }, "portfinder": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.21.tgz", - "integrity": "sha512-ESabpDCzmBS3ekHbmpAIiESq3udRsCBGiBZLsC+HgBKv2ezb0R4oG+7RnYEVZ/ZCfhel5Tx3UzdNWA0Lox2QCA==", + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz", + "integrity": "sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==", "dev": true, "requires": { "async": "^1.5.2", @@ -15557,9 +15557,9 @@ }, "dependencies": { "@types/node": { - "version": "6.14.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.7.tgz", - "integrity": "sha512-YbPXbaynBTe0pVExPhL76TsWnxSPeFAvImIsmylpBWn/yfw+lHy+Q68aawvZHsgskT44ZAoeE67GM5f+Brekew==", + "version": "6.14.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.6.tgz", + "integrity": "sha512-rFs9zCFtSHuseiNXxYxFlun8ibu+jtZPgRM+2ILCmeLiGeGLiIGxuOzD+cNyHegI1GD+da3R/cIbs9+xCLp13w==", "dev": true }, "ansi-styles": { @@ -18378,21 +18378,22 @@ } }, "yargs": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", - "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "version": "13.2.4", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", + "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", "dev": true, "requires": { "cliui": "^5.0.0", "find-up": "^3.0.0", "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^3.0.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^13.1.1" + "yargs-parser": "^13.1.0" } }, "yargs-parser": { @@ -18499,9 +18500,9 @@ } }, "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", + "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==", "dev": true }, "spdy": { @@ -19549,9 +19550,9 @@ } }, "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", + "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", "dev": true }, "type-is": { diff --git a/package.json b/package.json index 9b08044e121b..b22ecb270331 100644 --- a/package.json +++ b/package.json @@ -105,12 +105,14 @@ "@types/d3": "5.7.2", "@types/d3-scale": "2.1.1", "@types/file-saver": "2.0.1", + "@types/highlight.js": "^9.12.3", "ace-builds": "1.4.5", "c3": "0.7.3", "core-js": "3.1.4", "d3": "5.9.7", "file-saver": "2.0.2", "hammerjs": "2.0.8", + "highlight.js": "^9.15.8", "js-yaml": "3.13.1", "material-design-icons": "3.0.1", "ng2-ace-editor": "0.3.9", diff --git a/src/app/backend/api/types.go b/src/app/backend/api/types.go index 95ee8c8bd6cf..cc214bbad290 100644 --- a/src/app/backend/api/types.go +++ b/src/app/backend/api/types.go @@ -154,17 +154,19 @@ const ( ClientTypeAPIExtensionsClient = "apiextensionsclient" ) -// Mapping from resource kind to K8s apiserver API path. This is mostly pluralization, because -// K8s apiserver uses plural paths and this project singular. -// Must be kept in sync with the list of supported kinds. -var KindToAPIMapping = map[string]struct { +type APIMapping struct { // Kubernetes resource name. Resource string // Client type used by given resource, i.e. deployments are using extension client. ClientType ClientType // Is this object global scoped (not below a namespace). Namespaced bool -}{ +} + +// Mapping from resource kind to K8s apiserver API path. This is mostly pluralization, because +// K8s apiserver uses plural paths and this project singular. +// Must be kept in sync with the list of supported kinds. +var KindToAPIMapping = map[string]APIMapping{ ResourceKindConfigMap: {"configmaps", ClientTypeDefault, true}, ResourceKindDaemonSet: {"daemonsets", ClientTypeExtensionClient, true}, ResourceKindDeployment: {"deployments", ClientTypeExtensionClient, true}, diff --git a/src/app/backend/auth/manager_test.go b/src/app/backend/auth/manager_test.go index 484c656e320e..f5d9a30ab17a 100644 --- a/src/app/backend/auth/manager_test.go +++ b/src/app/backend/auth/manager_test.go @@ -82,9 +82,9 @@ func (self *fakeClientManager) HasAccess(authInfo api.AuthInfo) error { return self.HasAccessError } -func (self *fakeClientManager) VerberClient(req *restful.Request) (clientapi.ResourceVerber, error) { +func (self *fakeClientManager) VerberClient(req *restful.Request, config *rest.Config) (clientapi.ResourceVerber, error) { return client.NewResourceVerber(nil, nil, nil, nil, nil, - nil, nil, nil, nil), nil + nil, nil, nil, nil, nil), nil } func (self *fakeClientManager) CanI(req *restful.Request, ssar *v1.SelfSubjectAccessReview) bool { diff --git a/src/app/backend/client/api/types.go b/src/app/backend/client/api/types.go index d7b6f5072886..c718681a3ca4 100644 --- a/src/app/backend/client/api/types.go +++ b/src/app/backend/client/api/types.go @@ -15,7 +15,7 @@ package api import ( - "github.com/emicklei/go-restful" + restful "github.com/emicklei/go-restful" authApi "github.com/kubernetes/dashboard/src/app/backend/auth/api" pluginclientset "github.com/kubernetes/dashboard/src/app/backend/plugin/client/clientset/versioned" v1 "k8s.io/api/authorization/v1" @@ -48,7 +48,7 @@ type ClientManager interface { ClientCmdConfig(req *restful.Request) (clientcmd.ClientConfig, error) CSRFKey() string HasAccess(authInfo api.AuthInfo) error - VerberClient(req *restful.Request) (ResourceVerber, error) + VerberClient(req *restful.Request, config *rest.Config) (ResourceVerber, error) SetTokenManager(manager authApi.TokenManager) } diff --git a/src/app/backend/client/manager.go b/src/app/backend/client/manager.go index 3be2266ed4ce..4b932f24acba 100644 --- a/src/app/backend/client/manager.go +++ b/src/app/backend/client/manager.go @@ -18,7 +18,7 @@ import ( "log" "strings" - "github.com/emicklei/go-restful" + restful "github.com/emicklei/go-restful" pluginclientset "github.com/kubernetes/dashboard/src/app/backend/plugin/client/clientset/versioned" v1 "k8s.io/api/authorization/v1" apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" @@ -241,7 +241,7 @@ func (self *clientManager) HasAccess(authInfo api.AuthInfo) error { } // VerberClient returns new verber client based on authentication information extracted from request -func (self *clientManager) VerberClient(req *restful.Request) (clientapi.ResourceVerber, error) { +func (self *clientManager) VerberClient(req *restful.Request, config *rest.Config) (clientapi.ResourceVerber, error) { k8sClient, err := self.Client(req) if err != nil { return nil, err @@ -255,7 +255,7 @@ func (self *clientManager) VerberClient(req *restful.Request) (clientapi.Resourc return NewResourceVerber(k8sClient.CoreV1().RESTClient(), k8sClient.ExtensionsV1beta1().RESTClient(), k8sClient.AppsV1().RESTClient(), k8sClient.BatchV1().RESTClient(), k8sClient.BatchV1beta1().RESTClient(), k8sClient.AutoscalingV1().RESTClient(), - k8sClient.StorageV1().RESTClient(), k8sClient.RbacV1().RESTClient(), apiextensionsclient.ApiextensionsV1beta1().RESTClient()), nil + k8sClient.StorageV1().RESTClient(), k8sClient.RbacV1().RESTClient(), apiextensionsclient.ApiextensionsV1beta1().RESTClient(), config), nil } // SetTokenManager sets the token manager that will be used for token decryption. diff --git a/src/app/backend/client/manager_test.go b/src/app/backend/client/manager_test.go index 1aa12b5da1f3..6ad1b15ac441 100644 --- a/src/app/backend/client/manager_test.go +++ b/src/app/backend/client/manager_test.go @@ -22,6 +22,7 @@ import ( restful "github.com/emicklei/go-restful" "github.com/kubernetes/dashboard/src/app/backend/args" "github.com/kubernetes/dashboard/src/app/backend/errors" + "k8s.io/client-go/rest" ) func TestNewClientManager(t *testing.T) { @@ -284,7 +285,7 @@ func TestClientCmdConfig(t *testing.T) { func TestVerberClient(t *testing.T) { manager := NewClientManager("", "http://localhost:8080") - _, err := manager.VerberClient(&restful.Request{Request: &http.Request{TLS: &tls.ConnectionState{}}}) + _, err := manager.VerberClient(&restful.Request{Request: &http.Request{TLS: &tls.ConnectionState{}}}, &rest.Config{}) if err != nil { t.Fatalf("VerberClient(): Expected verber client to be created but got error: %s", diff --git a/src/app/backend/client/verber.go b/src/app/backend/client/verber.go index 1ded3c523f48..a62e761baf80 100644 --- a/src/app/backend/client/verber.go +++ b/src/app/backend/client/verber.go @@ -20,7 +20,8 @@ import ( "github.com/kubernetes/dashboard/src/app/backend/api" clientapi "github.com/kubernetes/dashboard/src/app/backend/client/api" "github.com/kubernetes/dashboard/src/app/backend/errors" - + "github.com/kubernetes/dashboard/src/app/backend/resource/customresourcedefinition" + apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" restclient "k8s.io/client-go/rest" @@ -38,6 +39,7 @@ type resourceVerber struct { storageClient RESTClient rbacClient RESTClient apiExtensionsClient RESTClient + config *restclient.Config } func (verber *resourceVerber) getRESTClientByType(clientType api.ClientType) RESTClient { @@ -63,6 +65,46 @@ func (verber *resourceVerber) getRESTClientByType(clientType api.ClientType) RES } } +func (verber *resourceVerber) getResourceSpecFromKind(kind string, namespaceSet bool) (client RESTClient, resourceSpec api.APIMapping, err error) { + resourceSpec, ok := api.KindToAPIMapping[kind] + if !ok { + // check if kind is CRD + var crd apiextensions.CustomResourceDefinition + err = verber.apiExtensionsClient.Get().Resource("customresourcedefinitions").Name(kind).Do().Into(&crd) + if err != nil { + if errors.IsNotFoundError(err) { + err = errors.NewInvalid(fmt.Sprintf("Unknown resource kind: %s", kind)) + } + return + } + + client, err = customresourcedefinition.NewRESTClient(verber.config, &crd) + if err != nil { + return + } + + resourceSpec = api.APIMapping{ + Resource: crd.Status.AcceptedNames.Plural, + Namespaced: crd.Spec.Scope == apiextensions.NamespaceScoped, + } + } + + if namespaceSet != resourceSpec.Namespaced { + if namespaceSet { + err = errors.NewInvalid(fmt.Sprintf("Set namespace for not-namespaced resource kind: %s", kind)) + return + } else { + err = errors.NewInvalid(fmt.Sprintf("Set no namespace for namespaced resource kind: %s", kind)) + return + } + } + + if client == nil { + client = verber.getRESTClientByType(resourceSpec.ClientType) + } + return +} + // RESTClient is an interface for REST operations used in this file. type RESTClient interface { Delete() *restclient.Request @@ -73,28 +115,18 @@ type RESTClient interface { // NewResourceVerber creates a new resource verber that uses the given client for performing operations. func NewResourceVerber(client, extensionsClient, appsClient, batchClient, betaBatchClient, autoscalingClient, storageClient, - rbacClient, apiExtensionsClient RESTClient) clientapi.ResourceVerber { + rbacClient, apiExtensionsClient RESTClient, config *restclient.Config) clientapi.ResourceVerber { return &resourceVerber{client, extensionsClient, appsClient, - batchClient, betaBatchClient, autoscalingClient, storageClient, rbacClient, apiExtensionsClient} + batchClient, betaBatchClient, autoscalingClient, storageClient, rbacClient, apiExtensionsClient, config} } // Delete deletes the resource of the given kind in the given namespace with the given name. func (verber *resourceVerber) Delete(kind string, namespaceSet bool, namespace string, name string) error { - resourceSpec, ok := api.KindToAPIMapping[kind] - if !ok { - return errors.NewInvalid(fmt.Sprintf("Unknown resource kind: %s", kind)) + client, resourceSpec, err := verber.getResourceSpecFromKind(kind, namespaceSet) + if err != nil { + return err } - if namespaceSet != resourceSpec.Namespaced { - if namespaceSet { - return errors.NewInvalid(fmt.Sprintf("Set namespace for not-namespaced resource kind: %s", kind)) - } else { - return errors.NewInvalid(fmt.Sprintf("Set no namespace for namespaced resource kind: %s", kind)) - } - } - - client := verber.getRESTClientByType(resourceSpec.ClientType) - // Do cascade delete by default, as this is what users typically expect. defaultPropagationPolicy := v1.DeletePropagationForeground defaultDeleteOptions := &v1.DeleteOptions{ @@ -114,21 +146,11 @@ func (verber *resourceVerber) Delete(kind string, namespaceSet bool, namespace s func (verber *resourceVerber) Put(kind string, namespaceSet bool, namespace string, name string, object *runtime.Unknown) error { - resourceSpec, ok := api.KindToAPIMapping[kind] - if !ok { - return errors.NewInvalid(fmt.Sprintf("Unknown resource kind: %s", kind)) - } - - if namespaceSet != resourceSpec.Namespaced { - if namespaceSet { - return errors.NewInvalid(fmt.Sprintf("Set namespace for not-namespaced resource kind: %s", kind)) - } else { - return errors.NewInvalid(fmt.Sprintf("Set no namespace for namespaced resource kind: %s", kind)) - } + client, resourceSpec, err := verber.getResourceSpecFromKind(kind, namespaceSet) + if err != nil { + return err } - client := verber.getRESTClientByType(resourceSpec.ClientType) - req := client.Put(). Resource(resourceSpec.Resource). Name(name). @@ -144,20 +166,11 @@ func (verber *resourceVerber) Put(kind string, namespaceSet bool, namespace stri // Get gets the resource of the given kind in the given namespace with the given name. func (verber *resourceVerber) Get(kind string, namespaceSet bool, namespace string, name string) (runtime.Object, error) { - resourceSpec, ok := api.KindToAPIMapping[kind] - if !ok { - return nil, errors.NewInvalid(fmt.Sprintf("Unknown resource kind: %s", kind)) - } - - if namespaceSet != resourceSpec.Namespaced { - if namespaceSet { - return nil, errors.NewInvalid(fmt.Sprintf("Set namespace for not-namespaced resource kind: %s", kind)) - } else { - return nil, errors.NewInvalid(fmt.Sprintf("Set no namespace for namespaced resource kind: %s", kind)) - } + client, resourceSpec, err := verber.getResourceSpecFromKind(kind, namespaceSet) + if err != nil { + return nil, err } - client := verber.getRESTClientByType(resourceSpec.ClientType) result := &runtime.Unknown{} req := client.Get().Resource(resourceSpec.Resource).Name(name).SetHeader("Accept", "application/json") @@ -165,6 +178,6 @@ func (verber *resourceVerber) Get(kind string, namespaceSet bool, namespace stri req.Namespace(namespace) } - err := req.Do().Into(result) + err = req.Do().Into(result) return result, err } diff --git a/src/app/backend/client/verber_test.go b/src/app/backend/client/verber_test.go index 25e6c98a50f4..85b785de6735 100644 --- a/src/app/backend/client/verber_test.go +++ b/src/app/backend/client/verber_test.go @@ -121,7 +121,10 @@ func TestGetShouldPropagateErrorsAndChoseClient(t *testing.T) { } func TestDeleteShouldThrowErrorOnUnknownResourceKind(t *testing.T) { - verber := resourceVerber{client: &FakeRESTClient{}} + verber := resourceVerber{ + client: &FakeRESTClient{}, + apiExtensionsClient: &FakeRESTClient{err: errors.NewNotFound("err")}, + } err := verber.Delete("foo", true, "bar", "baz") @@ -131,7 +134,10 @@ func TestDeleteShouldThrowErrorOnUnknownResourceKind(t *testing.T) { } func TestGetShouldThrowErrorOnUnknownResourceKind(t *testing.T) { - verber := resourceVerber{client: &FakeRESTClient{}} + verber := resourceVerber{ + client: &FakeRESTClient{}, + apiExtensionsClient: &FakeRESTClient{err: errors.NewNotFound("err")}, + } _, err := verber.Get("foo", true, "bar", "baz") @@ -141,7 +147,10 @@ func TestGetShouldThrowErrorOnUnknownResourceKind(t *testing.T) { } func TestPutShouldThrowErrorOnUnknownResourceKind(t *testing.T) { - verber := resourceVerber{client: &FakeRESTClient{}} + verber := resourceVerber{ + client: &FakeRESTClient{}, + apiExtensionsClient: &FakeRESTClient{err: errors.NewNotFound("err")}, + } err := verber.Put("foo", false, "", "baz", nil) diff --git a/src/app/backend/errors/errors.go b/src/app/backend/errors/errors.go index b59640bb0f0b..0a03077fea0d 100644 --- a/src/app/backend/errors/errors.go +++ b/src/app/backend/errors/errors.go @@ -55,6 +55,17 @@ func NewInvalid(reason string) *errors.StatusError { } } +func NewNotFound(reason string) *errors.StatusError { + return &errors.StatusError{ + ErrStatus: metav1.Status{ + Status: metav1.StatusFailure, + Code: http.StatusNotFound, + Reason: metav1.StatusReasonNotFound, + Message: reason, + }, + } +} + func NewInternal(reason string) *errors.StatusError { return &errors.StatusError{ErrStatus: metav1.Status{ Status: metav1.StatusFailure, diff --git a/src/app/backend/handler/apihandler.go b/src/app/backend/handler/apihandler.go index 6191b0ac8b6c..d1263937e719 100644 --- a/src/app/backend/handler/apihandler.go +++ b/src/app/backend/handler/apihandler.go @@ -20,9 +20,7 @@ import ( "strconv" "strings" - "github.com/kubernetes/dashboard/src/app/backend/plugin" - - "github.com/emicklei/go-restful" + restful "github.com/emicklei/go-restful" "github.com/kubernetes/dashboard/src/app/backend/api" "github.com/kubernetes/dashboard/src/app/backend/auth" authApi "github.com/kubernetes/dashboard/src/app/backend/auth/api" @@ -30,6 +28,7 @@ import ( "github.com/kubernetes/dashboard/src/app/backend/errors" "github.com/kubernetes/dashboard/src/app/backend/integration" metricapi "github.com/kubernetes/dashboard/src/app/backend/integration/metric/api" + "github.com/kubernetes/dashboard/src/app/backend/plugin" "github.com/kubernetes/dashboard/src/app/backend/resource/clusterrole" "github.com/kubernetes/dashboard/src/app/backend/resource/common" "github.com/kubernetes/dashboard/src/app/backend/resource/configmap" @@ -548,12 +547,12 @@ func CreateHTTPAPIHandler(iManager integration.IntegrationManager, cManager clie Writes(customresourcedefinition.CustomResourceObjectList{})) apiV1Ws.Route( - apiV1Ws.GET("/crd/{namespace}/{crd}/object/{name}"). + apiV1Ws.GET("/crd/{namespace}/{crd}/{object}"). To(apiHandler.handleGetCustomResourceObjectDetail). - Writes(customresourcedefinition.CustomResourceObjectDetail{})) + Writes(customresourcedefinition.CustomResourceObject{})) apiV1Ws.Route( - apiV1Ws.GET("/crd/{namespace}/{crd}/object/{name}/event"). + apiV1Ws.GET("/crd/{namespace}/{crd}/{object}/event"). To(apiHandler.handleGetCustomResourceObjectEvents). Writes(common.EventList{})) @@ -1350,7 +1349,13 @@ func (apiHandler *APIHandler) handleUpdateReplicasCount(request *restful.Request } func (apiHandler *APIHandler) handleGetResource(request *restful.Request, response *restful.Response) { - verber, err := apiHandler.cManager.VerberClient(request) + config, err := apiHandler.cManager.Config(request) + if err != nil { + errors.HandleInternalError(response, err) + return + } + + verber, err := apiHandler.cManager.VerberClient(request, config) if err != nil { errors.HandleInternalError(response, err) return @@ -1370,7 +1375,13 @@ func (apiHandler *APIHandler) handleGetResource(request *restful.Request, respon func (apiHandler *APIHandler) handlePutResource( request *restful.Request, response *restful.Response) { - verber, err := apiHandler.cManager.VerberClient(request) + config, err := apiHandler.cManager.Config(request) + if err != nil { + errors.HandleInternalError(response, err) + return + } + + verber, err := apiHandler.cManager.VerberClient(request, config) if err != nil { errors.HandleInternalError(response, err) return @@ -1395,7 +1406,13 @@ func (apiHandler *APIHandler) handlePutResource( func (apiHandler *APIHandler) handleDeleteResource( request *restful.Request, response *restful.Response) { - verber, err := apiHandler.cManager.VerberClient(request) + config, err := apiHandler.cManager.Config(request) + if err != nil { + errors.HandleInternalError(response, err) + return + } + + verber, err := apiHandler.cManager.VerberClient(request, config) if err != nil { errors.HandleInternalError(response, err) return @@ -2152,7 +2169,7 @@ func (apiHandler *APIHandler) handleGetCustomResourceObjectDetail(request *restf return } - name := request.PathParameter("name") + name := request.PathParameter("object") crdName := request.PathParameter("crd") namespace := parseNamespacePathParameter(request) result, err := customresourcedefinition.GetCustomResourceObjectDetail(apiextensionsclient, namespace, config, crdName, name) @@ -2173,8 +2190,8 @@ func (apiHandler *APIHandler) handleGetCustomResourceObjectEvents(request *restf return } + name := request.PathParameter("object") namespace := request.PathParameter("namespace") - name := request.PathParameter("name") dataSelect := parseDataSelectPathParameter(request) dataSelect.MetricQuery = dataselect.StandardMetrics result, err := customresourcedefinition.GetEventsForCustomResourceObject(k8sClient, dataSelect, namespace, name) diff --git a/src/app/backend/resource/customresourcedefinition/client.go b/src/app/backend/resource/customresourcedefinition/client.go index 824ec6513273..b9ca64d34ede 100644 --- a/src/app/backend/resource/customresourcedefinition/client.go +++ b/src/app/backend/resource/customresourcedefinition/client.go @@ -15,14 +15,15 @@ package customresourcedefinition import ( + apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/client-go/rest" ) -func newRESTClient(config *rest.Config, groupVersion schema.GroupVersion) (*rest.RESTClient, error) { +func NewRESTClient(config *rest.Config, crd *apiextensions.CustomResourceDefinition) (*rest.RESTClient, error) { + groupVersion := getCustomResourceDefinitionGroupVersion(crd) scheme := runtime.NewScheme() schemeBuilder := runtime.NewSchemeBuilder( func(scheme *runtime.Scheme) error { @@ -30,8 +31,6 @@ func newRESTClient(config *rest.Config, groupVersion schema.GroupVersion) (*rest groupVersion, &metav1.ListOptions{}, &metav1.DeleteOptions{}, - &CustomResourceObject{}, - &CustomResourceObjectList{}, ) return nil }) @@ -44,10 +43,5 @@ func newRESTClient(config *rest.Config, groupVersion schema.GroupVersion) (*rest config.ContentType = runtime.ContentTypeJSON config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: serializer.NewCodecFactory(scheme)} - client, err := rest.RESTClientFor(config) - if err != nil { - return nil, err - } - - return client, nil + return rest.RESTClientFor(config) } diff --git a/src/app/backend/resource/customresourcedefinition/common.go b/src/app/backend/resource/customresourcedefinition/common.go index 8b8f68c88f5d..7a02ee7e373f 100644 --- a/src/app/backend/resource/customresourcedefinition/common.go +++ b/src/app/backend/resource/customresourcedefinition/common.go @@ -15,9 +15,9 @@ package customresourcedefinition import ( - "strings" - + "github.com/kubernetes/dashboard/src/app/backend/resource/common" "github.com/kubernetes/dashboard/src/app/backend/resource/dataselect" + api "k8s.io/api/core/v1" apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" "k8s.io/apimachinery/pkg/runtime/schema" ) @@ -54,17 +54,17 @@ func fromCells(cells []dataselect.DataCell) []apiextensions.CustomResourceDefini return std } -// The code below allows to perform complex data section on ThirdPartyResourceObject. +// The code below allows to perform complex data section on CustomResourceObject. type CustomResourceObjectCell CustomResourceObject func (self CustomResourceObjectCell) GetProperty(name dataselect.PropertyName) dataselect.ComparableValue { switch name { case dataselect.NameProperty: - return dataselect.StdComparableString(self.Metadata.Name) + return dataselect.StdComparableString(self.ObjectMeta.Name) case dataselect.CreationTimestampProperty: - return dataselect.StdComparableTime(self.Metadata.CreationTimestamp.Time) + return dataselect.StdComparableTime(self.ObjectMeta.CreationTimestamp.Time) case dataselect.NamespaceProperty: - return dataselect.StdComparableString(self.Metadata.Namespace) + return dataselect.StdComparableString(self.ObjectMeta.Namespace) default: // if name is not supported then just return a constant dummy value, sort will have no effect. return nil @@ -90,16 +90,22 @@ func fromObjectCells(cells []dataselect.DataCell) []CustomResourceObject { // getCustomResourceDefinitionGroupVersion returns first group version of custom resource definition. // It's also known as preferredVersion. func getCustomResourceDefinitionGroupVersion(crd *apiextensions.CustomResourceDefinition) schema.GroupVersion { - version := crd.Spec.Version - group := "" - if strings.Contains(crd.ObjectMeta.Name, ".") { - group = crd.ObjectMeta.Name[strings.Index(crd.ObjectMeta.Name, ".")+1:] - } else { - group = crd.ObjectMeta.Name + return schema.GroupVersion{ + Group: crd.Spec.Group, + Version: crd.Spec.Version, } +} - return schema.GroupVersion{ - Group: group, - Version: version, +func getCRDConditions(crd *apiextensions.CustomResourceDefinition) []common.Condition { + var conditions []common.Condition + for _, condition := range crd.Status.Conditions { + conditions = append(conditions, common.Condition{ + Type: string(condition.Type), + Status: api.ConditionStatus(condition.Status), + LastTransitionTime: condition.LastTransitionTime, + Reason: condition.Reason, + Message: condition.Message, + }) } + return conditions } diff --git a/src/app/backend/resource/customresourcedefinition/detail.go b/src/app/backend/resource/customresourcedefinition/detail.go index da0f14157ec6..c0fc47ecd8ed 100644 --- a/src/app/backend/resource/customresourcedefinition/detail.go +++ b/src/app/backend/resource/customresourcedefinition/detail.go @@ -15,7 +15,7 @@ package customresourcedefinition import ( - "github.com/kubernetes/dashboard/src/app/backend/api" + "github.com/kubernetes/dashboard/src/app/backend/errors" "github.com/kubernetes/dashboard/src/app/backend/resource/common" "github.com/kubernetes/dashboard/src/app/backend/resource/dataselect" apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" @@ -26,14 +26,19 @@ import ( type CustomResourceDefinitionDetail struct { CustomResourceDefinition `json:",inline"` + + Versions []CustomResourceDefinitionVersion `json:"versions,omitempty"` + Conditions []common.Condition `json:"conditions"` + Objects CustomResourceObjectList `json:"objects"` + + // List of non-critical errors, that occurred during resource retrieval. + Errors []error `json:"errors"` } -// CustomResourceDefinition represents a custom resource definition. -type CustomResourceDefinition struct { - ObjectMeta api.ObjectMeta `json:"objectMeta"` - TypeMeta api.TypeMeta `json:"typeMeta"` - Version string `json:"version"` - Objects CustomResourceObjectList `json:"objects"` +type CustomResourceDefinitionVersion struct { + Name string `json:"name"` + Served bool `json:"served"` + Storage bool `json:"storage"` } // GetCustomResourceDefinitionDetail returns detailed information about a custom resource definition. @@ -41,25 +46,41 @@ func GetCustomResourceDefinitionDetail(client apiextensionsclientset.Interface, customResourceDefinition, err := client.ApiextensionsV1beta1(). CustomResourceDefinitions(). Get(name, metav1.GetOptions{}) - if err != nil { - return nil, err + nonCriticalErrors, criticalError := errors.HandleError(err) + if criticalError != nil { + return nil, criticalError } objects, err := GetCustomResourceObjectList(client, config, &common.NamespaceQuery{}, dataselect.DefaultDataSelect, name) - if err != nil { - return nil, err + nonCriticalErrors, criticalError = errors.AppendError(err, nonCriticalErrors) + if criticalError != nil { + return nil, criticalError } - return toCustomResourceDefinitionDetail(customResourceDefinition, objects), nil + return toCustomResourceDefinitionDetail(customResourceDefinition, *objects, nonCriticalErrors), nil } -func toCustomResourceDefinitionDetail(customResourceDefinition *apiextensions.CustomResourceDefinition, objects CustomResourceObjectList) *CustomResourceDefinitionDetail { +func toCustomResourceDefinitionDetail(crd *apiextensions.CustomResourceDefinition, objects CustomResourceObjectList, nonCriticalErrors []error) *CustomResourceDefinitionDetail { return &CustomResourceDefinitionDetail{ - CustomResourceDefinition{ - ObjectMeta: api.NewObjectMeta(customResourceDefinition.ObjectMeta), - TypeMeta: api.NewTypeMeta(api.ResourceKindCustomResourceDefinition), - Version: customResourceDefinition.Spec.Version, - Objects: objects, - }, + CustomResourceDefinition: toCustomResourceDefinition(crd), + Versions: getCRDVersions(crd), + Conditions: getCRDConditions(crd), + Objects: objects, + Errors: nonCriticalErrors, + } +} + +func getCRDVersions(crd *apiextensions.CustomResourceDefinition) []CustomResourceDefinitionVersion { + crdVersions := make([]CustomResourceDefinitionVersion, 0, len(crd.Spec.Versions)) + if len(crd.Spec.Versions) > 0 { + for _, version := range crd.Spec.Versions { + crdVersions = append(crdVersions, CustomResourceDefinitionVersion{ + Name: version.Name, + Served: version.Served, + Storage: version.Storage, + }) + } } + + return crdVersions } diff --git a/src/app/backend/resource/customresourcedefinition/list.go b/src/app/backend/resource/customresourcedefinition/list.go index b2a186eaa3fc..c16e72df4026 100644 --- a/src/app/backend/resource/customresourcedefinition/list.go +++ b/src/app/backend/resource/customresourcedefinition/list.go @@ -34,6 +34,17 @@ type CustomResourceDefinitionList struct { Errors []error `json:"errors"` } +// CustomResourceDefinition represents a custom resource definition. +type CustomResourceDefinition struct { + ObjectMeta api.ObjectMeta `json:"objectMeta"` + TypeMeta api.TypeMeta `json:"typeMeta"` + Version string `json:"version,omitempty"` + Group string `json:"group"` + Scope apiextensions.ResourceScope `json:"scope"` + Names apiextensions.CustomResourceDefinitionNames `json:"names"` + Established apiextensions.ConditionStatus `json:"established"` +} + // GetCustomResourceDefinitionList returns all the custom resource definitions in the cluster. func GetCustomResourceDefinitionList(client apiextensionsclientset.Interface, dsQuery *dataselect.DataSelectQuery) (*CustomResourceDefinitionList, error) { channel := common.GetCustomResourceDefinitionChannel(client, 1) @@ -68,8 +79,21 @@ func toCustomResourceDefinitionList(crds []apiextensions.CustomResourceDefinitio func toCustomResourceDefinition(crd *apiextensions.CustomResourceDefinition) CustomResourceDefinition { return CustomResourceDefinition{ - ObjectMeta: api.NewObjectMeta(crd.ObjectMeta), - TypeMeta: api.NewTypeMeta(api.ResourceKindCustomResourceDefinition), - Version: crd.Spec.Version, + ObjectMeta: api.NewObjectMeta(crd.ObjectMeta), + TypeMeta: api.NewTypeMeta(api.ResourceKindCustomResourceDefinition), + Version: crd.Spec.Version, + Group: crd.Spec.Group, + Scope: crd.Spec.Scope, + Names: crd.Status.AcceptedNames, + Established: getCRDConditionStatus(crd, apiextensions.Established), + } +} + +func getCRDConditionStatus(node *apiextensions.CustomResourceDefinition, conditionType apiextensions.CustomResourceDefinitionConditionType) apiextensions.ConditionStatus { + for _, condition := range node.Status.Conditions { + if condition.Type == conditionType { + return condition.Status + } } + return apiextensions.ConditionUnknown } diff --git a/src/app/backend/resource/customresourcedefinition/list_test.go b/src/app/backend/resource/customresourcedefinition/list_test.go index 30df965ce0fa..ecdfeea5b43e 100644 --- a/src/app/backend/resource/customresourcedefinition/list_test.go +++ b/src/app/backend/resource/customresourcedefinition/list_test.go @@ -51,9 +51,10 @@ func TestGetCustomResourceDefinition(t *testing.T) { ListMeta: api.ListMeta{TotalItems: 1}, Items: []CustomResourceDefinition{ { - ObjectMeta: api.ObjectMeta{Name: "foos.samplecontroller.k8s.io"}, - TypeMeta: api.TypeMeta{Kind: api.ResourceKindCustomResourceDefinition}, - Version: "v1alpha1", + ObjectMeta: api.ObjectMeta{Name: "foos.samplecontroller.k8s.io"}, + TypeMeta: api.TypeMeta{Kind: api.ResourceKindCustomResourceDefinition}, + Version: "v1alpha1", + Established: apiextensions.ConditionUnknown, }, }, Errors: []error{}, diff --git a/src/app/backend/resource/customresourcedefinition/objects.go b/src/app/backend/resource/customresourcedefinition/objects.go index 828abec82781..c69d016d9bb2 100644 --- a/src/app/backend/resource/customresourcedefinition/objects.go +++ b/src/app/backend/resource/customresourcedefinition/objects.go @@ -15,47 +15,50 @@ package customresourcedefinition import ( + "encoding/json" + "github.com/kubernetes/dashboard/src/app/backend/api" "github.com/kubernetes/dashboard/src/app/backend/errors" "github.com/kubernetes/dashboard/src/app/backend/resource/common" "github.com/kubernetes/dashboard/src/app/backend/resource/dataselect" apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" ) -type CustomResourceObjectDetail struct { - CustomResourceObject `json:",inline"` -} - // CustomResourceObject represents a custom resource object. type CustomResourceObject struct { - metav1.TypeMeta `json:",inline"` - Metadata metav1.ObjectMeta `json:"metadata,omitempty"` + TypeMeta metav1.TypeMeta `json:"typeMeta"` + ObjectMeta metav1.ObjectMeta `json:"objectMeta"` } -func (in *CustomResourceObject) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } else { - return nil +func (r *CustomResourceObject) UnmarshalJSON(data []byte) error { + tempStruct := &struct { + metav1.TypeMeta `json:",inline"` + ObjectMeta metav1.ObjectMeta `json:"metadata,omitempty"` + }{} + + err := json.Unmarshal(data, &tempStruct) + if err != nil { + return err } + + r.TypeMeta = tempStruct.TypeMeta + r.ObjectMeta = tempStruct.ObjectMeta + return nil } -func (in *CustomResourceObject) DeepCopy() *CustomResourceObject { - if in == nil { - return nil - } - out := new(CustomResourceObject) - *out = *in - return out +type CustomResourceObjectDetail struct { + CustomResourceObject `json:",inline"` + + // List of non-critical errors, that occurred during resource retrieval. + Errors []error `json:"errors"` } // CustomResourceObjectList represents crd objects in a namespace. type CustomResourceObjectList struct { - ListMeta api.ListMeta `json:"listMeta"` - metav1.TypeMeta `json:",inline"` + TypeMeta metav1.TypeMeta `json:"typeMeta"` + ListMeta api.ListMeta `json:"listMeta"` // Unordered list of custom resource definitions Items []CustomResourceObject `json:"items"` @@ -64,83 +67,109 @@ type CustomResourceObjectList struct { Errors []error `json:"errors"` } -func (in *CustomResourceObjectList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } else { - return nil - } -} +func (r *CustomResourceObjectList) UnmarshalJSON(data []byte) error { + tempStruct := &struct { + metav1.TypeMeta `json:",inline"` + Items []CustomResourceObject `json:"items"` + }{} -func (in *CustomResourceObjectList) DeepCopy() *CustomResourceObjectList { - if in == nil { - return nil + err := json.Unmarshal(data, &tempStruct) + if err != nil { + return err } - out := new(CustomResourceObjectList) - *out = *in - return out + + r.TypeMeta = tempStruct.TypeMeta + r.Items = tempStruct.Items + return nil } // GetCustomResourceObjectList gets objects for a CR. func GetCustomResourceObjectList(client apiextensionsclientset.Interface, config *rest.Config, namespace *common.NamespaceQuery, - dsQuery *dataselect.DataSelectQuery, crdName string) (CustomResourceObjectList, error) { - var list CustomResourceObjectList + dsQuery *dataselect.DataSelectQuery, crdName string) (*CustomResourceObjectList, error) { + var list *CustomResourceObjectList customResourceDefinition, err := client.ApiextensionsV1beta1(). CustomResourceDefinitions(). Get(crdName, metav1.GetOptions{}) nonCriticalErrors, criticalError := errors.HandleError(err) if criticalError != nil { - return list, criticalError + return nil, criticalError } - restClient, err := newRESTClient(config, getCustomResourceDefinitionGroupVersion(customResourceDefinition)) + restClient, err := NewRESTClient(config, customResourceDefinition) nonCriticalErrors, criticalError = errors.AppendError(err, nonCriticalErrors) if criticalError != nil { - return list, criticalError + return nil, criticalError } - err = restClient.Get(). + raw, err := restClient.Get(). Namespace(namespace.ToRequestParam()). Resource(customResourceDefinition.Spec.Names.Plural). - Do().Into(&list) + Do().Raw() + nonCriticalErrors, criticalError = errors.AppendError(err, nonCriticalErrors) + if criticalError != nil { + return nil, criticalError + } + + err = json.Unmarshal(raw, &list) nonCriticalErrors, criticalError = errors.AppendError(err, nonCriticalErrors) if criticalError != nil { - return list, criticalError + return nil, criticalError } + list.Errors = nonCriticalErrors // Return only slice of data, pagination is done here. crdObjectCells, filteredTotal := dataselect.GenericDataSelectWithFilter(toObjectCells(list.Items), dsQuery) list.Items = fromObjectCells(crdObjectCells) list.ListMeta = api.ListMeta{TotalItems: filteredTotal} - list.Errors = nonCriticalErrors + + for i := range list.Items { + replaceCRDObjectKind(&list.Items[i], customResourceDefinition.Name) + } return list, nil } // GetCustomResourceObjectDetail returns details of a single object in a CR. -func GetCustomResourceObjectDetail(client apiextensionsclientset.Interface, namespace *common.NamespaceQuery, config *rest.Config, crdName string, name string) (CustomResourceObjectDetail, error) { - var detail CustomResourceObjectDetail +func GetCustomResourceObjectDetail(client apiextensionsclientset.Interface, namespace *common.NamespaceQuery, config *rest.Config, crdName string, name string) (*CustomResourceObjectDetail, error) { + var detail *CustomResourceObjectDetail customResourceDefinition, err := client.ApiextensionsV1beta1(). CustomResourceDefinitions(). Get(crdName, metav1.GetOptions{}) - if err != nil { - return detail, err + nonCriticalErrors, criticalError := errors.HandleError(err) + if criticalError != nil { + return nil, criticalError } - restClient, err := newRESTClient(config, getCustomResourceDefinitionGroupVersion(customResourceDefinition)) - if err != nil { - return detail, err + restClient, err := NewRESTClient(config, customResourceDefinition) + nonCriticalErrors, criticalError = errors.AppendError(err, nonCriticalErrors) + if criticalError != nil { + return nil, criticalError } - err = restClient.Get(). + raw, err := restClient.Get(). Namespace(namespace.ToRequestParam()). - Resource(customResourceDefinition.Spec.Names.Plural). - Name(name).Do().Into(&detail) - if err != nil { - return detail, err + Resource(customResourceDefinition.Status.AcceptedNames.Plural). + Name(name).Do().Raw() + nonCriticalErrors, criticalError = errors.AppendError(err, nonCriticalErrors) + if criticalError != nil { + return nil, criticalError + } + + err = json.Unmarshal(raw, &detail) + nonCriticalErrors, criticalError = errors.AppendError(err, nonCriticalErrors) + if criticalError != nil { + return nil, criticalError } + detail.Errors = nonCriticalErrors + replaceCRDObjectKind(&detail.CustomResourceObject, customResourceDefinition.Name) return detail, nil } + +// replaceCRDObjectKind sets the object kind to the full name of the CRD. +// E.g. changes "Foo" to "foos.samplecontroller.k8s.io" +func replaceCRDObjectKind(object *CustomResourceObject, kind string) { + object.TypeMeta.Kind = kind +} diff --git a/src/app/frontend/chrome/nav/template.html b/src/app/frontend/chrome/nav/template.html index 881075013359..26ae9d2cfa7f 100644 --- a/src/app/frontend/chrome/nav/template.html +++ b/src/app/frontend/chrome/nav/template.html @@ -124,6 +124,15 @@ +
+ Custom Resource Definitions + +
+ + +
{ diff --git a/src/app/frontend/common/components/condition/template.html b/src/app/frontend/common/components/condition/template.html index 1ff73cf6df5f..8f120f06c488 100644 --- a/src/app/frontend/common/components/condition/template.html +++ b/src/app/frontend/common/components/condition/template.html @@ -79,8 +79,8 @@ - - - + +
diff --git a/src/app/frontend/common/components/module.ts b/src/app/frontend/common/components/module.ts index 31f3275829e5..be299386841e 100644 --- a/src/app/frontend/common/components/module.ts +++ b/src/app/frontend/common/components/module.ts @@ -62,6 +62,9 @@ import {ProxyComponent} from './proxy/component'; import {ResourceQuotaListComponent} from './quotas/component'; import {ClusterRoleListComponent} from './resourcelist/clusterrole/component'; import {ConfigMapListComponent} from './resourcelist/configmap/component'; +import {CRDListComponent} from './resourcelist/crd/component'; +import {CRDObjectListComponent} from './resourcelist/crdobject/component'; +import {CRDVersionListComponent} from './resourcelist/crdversion/component'; import {CronJobListComponent} from './resourcelist/cronjob/component'; import {DaemonSetListComponent} from './resourcelist/daemonset/component'; import {DeploymentListComponent} from './resourcelist/deployment/component'; @@ -110,6 +113,9 @@ const components = [ ContainerCardComponent, ConditionListComponent, CreatorCardComponent, + CRDListComponent, + CRDObjectListComponent, + CRDVersionListComponent, GraphComponent, GraphCardComponent, diff --git a/src/app/frontend/common/components/resourcelist/crd/component.ts b/src/app/frontend/common/components/resourcelist/crd/component.ts new file mode 100644 index 000000000000..1e8307f4917b --- /dev/null +++ b/src/app/frontend/common/components/resourcelist/crd/component.ts @@ -0,0 +1,78 @@ +// Copyright 2017 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. + +import {HttpParams} from '@angular/common/http'; +import {Component, Input} from '@angular/core'; +import {CRD, CRDList} from '@api/backendapi'; +import {Observable} from 'rxjs'; + +import {ResourceListWithStatuses} from '../../../resources/list'; +import {NotificationsService} from '../../../services/global/notifications'; +import {EndpointManager, Resource} from '../../../services/resource/endpoint'; +import {ResourceService} from '../../../services/resource/resource'; +import {MenuComponent} from '../../list/column/menu/component'; +import {ListGroupIdentifier, ListIdentifier} from '../groupids'; + +@Component({ + selector: 'kd-crd-list', + templateUrl: './template.html', +}) +export class CRDListComponent extends ResourceListWithStatuses { + @Input() endpoint = EndpointManager.resource(Resource.crd).list(); + + constructor( + private readonly crd_: ResourceService, + notifications: NotificationsService, + ) { + super('crd', notifications); + this.id = ListIdentifier.crd; + this.groupId = ListGroupIdentifier.none; + + // Register action columns. + this.registerActionColumn('menu', MenuComponent); + + // Register status icon handlers + this.registerBinding(this.icon.checkCircle, 'kd-success', this.isInSuccessState); + this.registerBinding(this.icon.help, 'kd-muted', this.isInUnknownState); + this.registerBinding(this.icon.error, 'kd-error', this.isInErrorState); + } + + isNamespaced(crd: CRD): string { + return crd.scope === 'Namespaced' ? 'True' : 'False'; + } + + getResourceObservable(params?: HttpParams): Observable { + return this.crd_.get(this.endpoint, undefined, params); + } + + map(crdList: CRDList): CRD[] { + return crdList.items; + } + + isInErrorState(resource: CRD): boolean { + return resource.established === 'False'; + } + + isInUnknownState(resource: CRD): boolean { + return resource.established === 'Unknown'; + } + + isInSuccessState(resource: CRD): boolean { + return resource.established === 'True'; + } + + getDisplayColumns(): string[] { + return ['statusicon', 'name', 'group', 'fullName', 'namespaced', 'age']; + } +} diff --git a/src/app/frontend/common/components/resourcelist/crd/template.html b/src/app/frontend/common/components/resourcelist/crd/template.html new file mode 100644 index 000000000000..fcb10204abdb --- /dev/null +++ b/src/app/frontend/common/components/resourcelist/crd/template.html @@ -0,0 +1,110 @@ + + + +
Custom Resource Definitions
+
Items: {{totalItems}}
+
+ +
+ +
+
+ + + + + + + {{getStatus(crd).iconName}} + + + + + + Name + + {{crd.names.kind}} + + + + + Group + {{crd.group}} + + + + Full Name + {{crd.objectMeta.name}} + + + + Namespaced + {{isNamespaced(crd)}} + + + + Age + + + + + + + + + + + + + + + + + +
+ +
+ +
+
diff --git a/src/app/frontend/common/components/resourcelist/crdobject/component.ts b/src/app/frontend/common/components/resourcelist/crdobject/component.ts new file mode 100644 index 000000000000..961827b525b8 --- /dev/null +++ b/src/app/frontend/common/components/resourcelist/crdobject/component.ts @@ -0,0 +1,58 @@ +// Copyright 2017 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. + +import {Component, Input} from '@angular/core'; +import {HttpParams} from '@angular/common/http'; +import {Observable} from 'rxjs'; +import {CRDObject, CRDObjectList} from '@api/backendapi'; +import {ResourceListBase} from '../../../resources/list'; +import {NamespacedResourceService} from '../../../services/resource/resource'; +import {NotificationsService} from '../../../services/global/notifications'; +import {ActivatedRoute} from '@angular/router'; +import {ListGroupIdentifier, ListIdentifier} from '../groupids'; +import {MenuComponent} from '../../list/column/menu/component'; + +@Component({ + selector: 'kd-crd-object-list', + templateUrl: './template.html', +}) +export class CRDObjectListComponent extends ResourceListBase { + @Input() endpoint: string; + @Input() crdName: string; + + constructor( + private readonly crdObject_: NamespacedResourceService, + notifications: NotificationsService, + private readonly activatedRoute_: ActivatedRoute, + ) { + super(`crd/${activatedRoute_.snapshot.params.crdName}`, notifications); + this.id = ListIdentifier.crdObject; + this.groupId = ListGroupIdentifier.none; + + // Register action columns. + this.registerActionColumn('menu', MenuComponent); + } + + getResourceObservable(params?: HttpParams): Observable { + return this.crdObject_.get(this.endpoint, undefined, undefined, params); + } + + map(crdObjectList: CRDObjectList): CRDObject[] { + return crdObjectList.items; + } + + getDisplayColumns(): string[] { + return ['name', 'namespace', 'age']; + } +} diff --git a/src/app/frontend/common/components/resourcelist/crdobject/template.html b/src/app/frontend/common/components/resourcelist/crdobject/template.html new file mode 100644 index 000000000000..423eddcff45c --- /dev/null +++ b/src/app/frontend/common/components/resourcelist/crdobject/template.html @@ -0,0 +1,88 @@ + + + +
Objects
+
+ Items: {{totalItems}}
+
+ +
+ +
+
+ + + + Name + + + {{object.objectMeta.name}} + + + + + + Namespace + {{object.objectMeta.namespace}} + + + + Age + + + + + + + + + + + + + + + + + +
+ +
+ +
+
diff --git a/src/app/frontend/common/components/resourcelist/crdversion/component.ts b/src/app/frontend/common/components/resourcelist/crdversion/component.ts new file mode 100644 index 000000000000..e0c23dbb231f --- /dev/null +++ b/src/app/frontend/common/components/resourcelist/crdversion/component.ts @@ -0,0 +1,37 @@ +// Copyright 2017 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. + +import {Component, Input} from '@angular/core'; +import {CRDVersion} from '@api/backendapi'; +import {MatTableDataSource} from '@angular/material'; + +@Component({ + selector: 'kd-crd-versions-list', + templateUrl: './template.html', +}) +export class CRDVersionListComponent { + @Input() versions: CRDVersion[]; + @Input() initialized: boolean; + + getDisplayColumns(): string[] { + return ['name', 'served', 'storage']; + } + + getDataSource(): MatTableDataSource { + const tableData = new MatTableDataSource(); + tableData.data = this.versions; + + return tableData; + } +} diff --git a/src/app/frontend/common/components/resourcelist/crdversion/template.html b/src/app/frontend/common/components/resourcelist/crdversion/template.html new file mode 100644 index 000000000000..1110fd5dbc9b --- /dev/null +++ b/src/app/frontend/common/components/resourcelist/crdversion/template.html @@ -0,0 +1,56 @@ + + + +
Versions
+ +
+
+ Items:  + {{versions.length}} +
+
+ +
+ + + Name + {{version.name}} + + + + Served + {{version.served ? "True" : "False"}} + + + + Storage + {{version.storage ? "True" : "False"}} + + + + + +
+
diff --git a/src/app/frontend/common/components/resourcelist/groupids.ts b/src/app/frontend/common/components/resourcelist/groupids.ts index 03a9a347757e..ac7662a774e6 100644 --- a/src/app/frontend/common/components/resourcelist/groupids.ts +++ b/src/app/frontend/common/components/resourcelist/groupids.ts @@ -19,6 +19,8 @@ export enum ListIdentifier { persistentVolume = 'persistentVolumeList', storageClass = 'storageClassList', cronJob = 'cronJobList', + crd = 'crdList', + crdObject = 'crdObjectList', job = 'jobList', deployment = 'deploymentList', daemonSet = 'daemonSetList', diff --git a/src/app/frontend/common/services/resource/endpoint.ts b/src/app/frontend/common/services/resource/endpoint.ts index 6af01d62613e..aee389621abe 100644 --- a/src/app/frontend/common/services/resource/endpoint.ts +++ b/src/app/frontend/common/services/resource/endpoint.ts @@ -17,6 +17,8 @@ const baseHref = 'api/v1'; export enum Resource { job = 'job', cronJob = 'cronjob', + crd = 'crd', + crdObject = 'object', daemonSet = 'daemonset', deployment = 'deployment', pod = 'pod', @@ -56,7 +58,7 @@ class ResourceEndpoint { child(resourceName: string, relatedResource: Resource, resourceNamespace?: string): string { if (!resourceNamespace) { - resourceNamespace = '/:namespace'; + resourceNamespace = ':namespace'; } return `${baseHref}/${this.resource_}${ diff --git a/src/app/frontend/common/services/resource/resource.ts b/src/app/frontend/common/services/resource/resource.ts index 777081ebc892..97ebc2b317d8 100644 --- a/src/app/frontend/common/services/resource/resource.ts +++ b/src/app/frontend/common/services/resource/resource.ts @@ -63,7 +63,7 @@ export class NamespacedResourceService extends ResourceBase { private getNamespace_(): string { const currentNamespace = this.namespace_.current(); - return this.namespace_.isMultiNamespace(currentNamespace) ? '' : currentNamespace; + return this.namespace_.isMultiNamespace(currentNamespace) ? ' ' : currentNamespace; } get(endpoint: string, name?: string, namespace?: string, params?: HttpParams): Observable { diff --git a/src/app/frontend/crd/crdobject/component.ts b/src/app/frontend/crd/crdobject/component.ts new file mode 100644 index 000000000000..ad275ab49888 --- /dev/null +++ b/src/app/frontend/crd/crdobject/component.ts @@ -0,0 +1,105 @@ +// Copyright 2017 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. + +import {Component, ElementRef, OnDestroy, OnInit, Renderer2, ViewChild} from '@angular/core'; +import {ActivatedRoute} from '@angular/router'; +import {MatButtonToggleGroup} from '@angular/material'; +import {HttpClient} from '@angular/common/http'; +import {dump as toYaml} from 'js-yaml'; +import {Subscription} from 'rxjs'; +import {CRDObjectDetail} from '@api/backendapi'; +import {highlightAuto} from 'highlight.js'; +import {ActionbarService, ResourceMeta} from '../../common/services/global/actionbar'; +import {NamespacedResourceService} from '../../common/services/resource/resource'; +import {EndpointManager, Resource} from '../../common/services/resource/endpoint'; +import {NotificationsService} from '../../common/services/global/notifications'; +import {RawResource} from '../../common/resources/rawresource'; + +enum Modes { + JSON = 'json', + YAML = 'yaml', +} + +@Component({selector: 'kd-crd-object-detail', templateUrl: './template.html'}) +export class CRDObjectDetailComponent implements OnInit, OnDestroy { + @ViewChild('group', {static: true}) buttonToggleGroup: MatButtonToggleGroup; + @ViewChild('code', {static: true}) codeRef: ElementRef; + + private objectSubscription_: Subscription; + private readonly endpoint_ = EndpointManager.resource(Resource.crd, true); + object: CRDObjectDetail; + modes = Modes; + isInitialized = false; + selectedMode = Modes.YAML; + objectRaw: {[s: string]: string} = {}; + + constructor( + private readonly object_: NamespacedResourceService, + private readonly actionbar_: ActionbarService, + private readonly activatedRoute_: ActivatedRoute, + private readonly notifications_: NotificationsService, + private readonly http_: HttpClient, + private readonly renderer_: Renderer2, + ) {} + + ngOnInit(): void { + const {crdName, namespace, objectName} = this.activatedRoute_.snapshot.params; + this.objectSubscription_ = this.object_ + .get(this.endpoint_.child(crdName, objectName, namespace)) + .subscribe((d: CRDObjectDetail) => { + this.object = d; + this.notifications_.pushErrors(d.errors); + this.actionbar_.onInit.emit(new ResourceMeta(d.typeMeta.kind, d.objectMeta, d.typeMeta)); + this.isInitialized = true; + + // Get raw resource + const url = RawResource.getUrl(this.object.typeMeta, this.object.objectMeta); + this.http_ + .get(url) + .toPromise() + .then(response => { + this.objectRaw = { + json: highlightAuto(`${this.toRawJSON(response)}`, ['json']).value, + yaml: highlightAuto(`${toYaml(response)}`, ['yaml']).value, + }; + this.updateText(); + }); + }); + + this.buttonToggleGroup.valueChange.subscribe((selectedMode: Modes) => { + this.selectedMode = selectedMode; + + if (Object.keys(this.objectRaw).length > 0) { + this.updateText(); + } + }); + } + + ngOnDestroy(): void { + this.objectSubscription_.unsubscribe(); + this.actionbar_.onDetailsLeave.emit(); + } + + private updateText(): void { + this.renderer_.setProperty( + this.codeRef.nativeElement, + 'innerHTML', + this.objectRaw[this.selectedMode], + ); + } + + private toRawJSON(object: {}): string { + return JSON.stringify(object, null, 2); + } +} diff --git a/src/app/frontend/crd/crdobject/template.html b/src/app/frontend/crd/crdobject/template.html new file mode 100644 index 000000000000..ea6fc9cacaab --- /dev/null +++ b/src/app/frontend/crd/crdobject/template.html @@ -0,0 +1,34 @@ + + + + + +
Data
+ +
+ + YAML + JSON + + +
+
+
diff --git a/src/app/frontend/crd/detail/component.ts b/src/app/frontend/crd/detail/component.ts new file mode 100644 index 000000000000..c0d832dbc2c8 --- /dev/null +++ b/src/app/frontend/crd/detail/component.ts @@ -0,0 +1,63 @@ +// Copyright 2017 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. + +import {Component, OnDestroy, OnInit} from '@angular/core'; +import {ActivatedRoute} from '@angular/router'; +import {CRDDetail} from '@api/backendapi'; +import {Subscription} from 'rxjs'; + +import {ActionbarService, ResourceMeta} from '../../common/services/global/actionbar'; +import {NotificationsService} from '../../common/services/global/notifications'; +import {ResourceService} from '../../common/services/resource/resource'; +import {EndpointManager, Resource} from '../../common/services/resource/endpoint'; + +@Component({selector: 'kd-crd-detail', templateUrl: './template.html'}) +export class CRDDetailComponent implements OnInit, OnDestroy { + private crdSubscription_: Subscription; + private readonly endpoint_ = EndpointManager.resource(Resource.crd); + crd: CRDDetail; + crdObjectEndpoint: string; + isInitialized = false; + + constructor( + private readonly crd_: ResourceService, + private readonly actionbar_: ActionbarService, + private readonly activatedRoute_: ActivatedRoute, + private readonly notifications_: NotificationsService, + ) {} + + ngOnInit(): void { + const {crdName} = this.activatedRoute_.snapshot.params; + this.crdObjectEndpoint = EndpointManager.resource(Resource.crd, true).child( + crdName, + Resource.crdObject, + ); + + this.crdSubscription_ = this.crd_ + .get(this.endpoint_.detail(), crdName) + .subscribe((d: CRDDetail) => { + this.crd = d; + this.notifications_.pushErrors(d.errors); + this.actionbar_.onInit.emit( + new ResourceMeta('Custom Resource Definition', d.objectMeta, d.typeMeta), + ); + this.isInitialized = true; + }); + } + + ngOnDestroy(): void { + this.crdSubscription_.unsubscribe(); + this.actionbar_.onDetailsLeave.emit(); + } +} diff --git a/src/app/frontend/crd/detail/template.html b/src/app/frontend/crd/detail/template.html new file mode 100644 index 000000000000..3530c2654986 --- /dev/null +++ b/src/app/frontend/crd/detail/template.html @@ -0,0 +1,91 @@ + + + + + +
Resource Information
+
+ +
Version
+
{{crd?.version}}
+
+ +
Scope
+
{{crd?.scope}}
+
+ +
Group
+
{{crd?.group}}
+
+
+
+ + +
Accepted Names
+
+ +
Plural
+
{{crd?.names.plural}}
+
+ +
Singular
+
{{crd?.names.singular}}
+
+ +
Kind
+
{{crd?.names.kind}}
+
+ +
List Kind
+
{{crd?.names.listKind}}
+
+ +
Short Names
+
{{crd?.names.shortNames.join(", ")}}
+
+ +
Categories
+
{{crd?.names.categories.join(", ")}}
+
+
+
+ + + + + + diff --git a/src/app/frontend/crd/list/component.ts b/src/app/frontend/crd/list/component.ts new file mode 100644 index 000000000000..5f4f5b2567cb --- /dev/null +++ b/src/app/frontend/crd/list/component.ts @@ -0,0 +1,18 @@ +// Copyright 2017 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. + +import {Component} from '@angular/core'; + +@Component({selector: 'kd-crd-list-state', template: ''}) +export class CRDListComponent {} diff --git a/src/app/frontend/crd/module.ts b/src/app/frontend/crd/module.ts new file mode 100644 index 000000000000..aee1d38554e0 --- /dev/null +++ b/src/app/frontend/crd/module.ts @@ -0,0 +1,29 @@ +// Copyright 2017 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. + +import {NgModule} from '@angular/core'; + +import {ComponentsModule} from '../common/components/module'; +import {SharedModule} from '../shared.module'; + +import {CRDRoutingModule} from './routing'; +import {CRDDetailComponent} from './detail/component'; +import {CRDListComponent} from './list/component'; +import {CRDObjectDetailComponent} from './crdobject/component'; + +@NgModule({ + imports: [SharedModule, ComponentsModule, CRDRoutingModule], + declarations: [CRDListComponent, CRDDetailComponent, CRDObjectDetailComponent], +}) +export class CrdModule {} diff --git a/src/app/frontend/crd/routing.ts b/src/app/frontend/crd/routing.ts new file mode 100644 index 000000000000..e23d2b15152f --- /dev/null +++ b/src/app/frontend/crd/routing.ts @@ -0,0 +1,51 @@ +// Copyright 2017 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. + +import {NgModule} from '@angular/core'; +import {Route, RouterModule} from '@angular/router'; + +import {CRDDetailComponent} from './detail/component'; +import {CRDListComponent} from './list/component'; +import {DEFAULT_ACTIONBAR} from '../common/components/actionbars/routing'; +import {CRDObjectDetailComponent} from './crdobject/component'; + +const CRD_LIST_ROUTE: Route = { + path: '', + component: CRDListComponent, + data: {breadcrumb: 'Custom Resource Definitions'}, +}; + +const CRD_DETAIL_ROUTE: Route = { + path: ':crdName', + component: CRDDetailComponent, + data: {breadcrumb: '{{ crdName }}', parent: CRD_LIST_ROUTE}, +}; + +const CRD_OBJECT_DETAIL_ROUTE: Route = { + path: ':crdName/:namespace/:objectName', + component: CRDObjectDetailComponent, + data: {breadcrumb: '{{ objectName }}', routeParamsCount: 2, parent: CRD_DETAIL_ROUTE}, +}; + +@NgModule({ + imports: [ + RouterModule.forChild([ + CRD_LIST_ROUTE, + CRD_DETAIL_ROUTE, + CRD_OBJECT_DETAIL_ROUTE, + DEFAULT_ACTIONBAR, + ]), + ], +}) +export class CRDRoutingModule {} diff --git a/src/app/frontend/typings/backendapi.d.ts b/src/app/frontend/typings/backendapi.d.ts index 410439c37316..7d8131ffc9ab 100644 --- a/src/app/frontend/typings/backendapi.d.ts +++ b/src/app/frontend/typings/backendapi.d.ts @@ -78,6 +78,15 @@ export interface CronJobList extends ResourceList { status: Status; } +export interface CRDList extends ResourceList { + items: CRD[]; +} + +export interface CRDObjectList extends ResourceList { + typeMeta: TypeMeta; + items: CRDObject[]; +} + export interface DaemonSetList extends ResourceList { daemonSets: DaemonSet[]; status: Status; @@ -133,7 +142,7 @@ export interface PodList extends ResourceList { pods: Pod[]; status: Status; podInfo?: PodInfo; - cumulativeMetrics: Metric[]|null; + cumulativeMetrics: Metric[] | null; } export interface ReplicaSetList extends ResourceList { @@ -185,6 +194,15 @@ export interface CronJob extends Resource { lastSchedule: string; } +export interface CRD extends Resource { + group: string; + scope: string; + nameKind: string; + established: string; +} + +export interface CRDObject extends Resource {} + export interface DaemonSet extends Resource { podInfo: PodInfo; containerImages: string[]; @@ -433,6 +451,18 @@ export interface ConfigMapDetail extends ResourceDetail { data: StringMap; } +export interface CRDDetail extends ResourceDetail { + version?: string; + group: string; + scope: string; + names: CRDNames; + versions: CRDVersion[]; + objects: CRDObjectList; + conditions: Condition[]; +} + +export interface CRDObjectDetail extends ResourceDetail {} + export interface JobDetail extends ResourceDetail { podInfo: PodInfo; podList: PodList; @@ -706,6 +736,21 @@ export interface Container { args: string[]; } +export interface CRDNames { + plural: string; + singular?: string; + shortNames?: string[]; + kind: string; + listKind?: string; + categories?: string[]; +} + +export interface CRDVersion { + name: string; + served: boolean; + storage: boolean; +} + export interface PodMetrics { cpuUsage: number; memoryUsage: number; @@ -778,9 +823,9 @@ export interface NodeTaint { } export interface PortMapping { - port: number|null; + port: number | null; protocol: string; - targetPort: number|null; + targetPort: number | null; } export interface EnvironmentVariable { @@ -880,8 +925,8 @@ export interface FlockerVolumeSource { } export interface RollingUpdateStrategy { - maxSurge: (number|string); - maxUnavailable: (number|string); + maxSurge: number | string; + maxUnavailable: number | string; } export interface DeploymentInfo {