fix pod xray menu + layout
parent
8299129baf
commit
aa58d1063a
1
go.mod
1
go.mod
|
|
@ -28,6 +28,7 @@ replace (
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
fyne.io/fyne v1.2.2 // indirect
|
||||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
||||||
github.com/atotto/clipboard v0.1.2
|
github.com/atotto/clipboard v0.1.2
|
||||||
github.com/derailed/tview v0.3.3
|
github.com/derailed/tview v0.3.3
|
||||||
|
|
|
||||||
26
go.sum
26
go.sum
|
|
@ -3,6 +3,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
|
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
|
||||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||||
|
fyne.io/fyne v1.2.2 h1:mf7EseASp3CAC5vLWVPLnsoKxvp/ARdu3Seh0HvAQak=
|
||||||
|
fyne.io/fyne v1.2.2/go.mod h1:Ab+3DIB/FVteW0y4DXfmZv4N3JdnCBh2lHkINI02BOU=
|
||||||
github.com/Azure/azure-sdk-for-go v32.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
github.com/Azure/azure-sdk-for-go v32.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||||
|
|
@ -29,6 +31,7 @@ github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFD
|
||||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||||
github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14=
|
github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14=
|
||||||
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA=
|
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA=
|
||||||
|
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
|
||||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
|
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
|
||||||
github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e h1:eb0Pzkt15Bm7f2FFYv7sjY7NPFi3cPkS3tv1CcrFBWA=
|
github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e h1:eb0Pzkt15Bm7f2FFYv7sjY7NPFi3cPkS3tv1CcrFBWA=
|
||||||
github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
|
github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
|
||||||
|
|
@ -59,6 +62,7 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdko
|
||||||
github.com/Rican7/retry v0.1.0/go.mod h1:FgOROf8P5bebcC1DS0PdOQiqGUridaZvikzUmkFW6gg=
|
github.com/Rican7/retry v0.1.0/go.mod h1:FgOROf8P5bebcC1DS0PdOQiqGUridaZvikzUmkFW6gg=
|
||||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
|
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
|
||||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||||
|
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
|
@ -212,6 +216,10 @@ github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7a
|
||||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||||
github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M=
|
github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M=
|
||||||
github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
|
github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
|
||||||
|
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw=
|
||||||
|
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
|
||||||
|
github.com/go-gl/glfw v0.0.0-20181213070059-819e8ce5125f h1:7MsFMbSn8Lcw0blK4+NEOf8DuHoOBDhJsHz04yh13pM=
|
||||||
|
github.com/go-gl/glfw v0.0.0-20181213070059-819e8ce5125f/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
|
|
@ -283,6 +291,8 @@ github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09Vjb
|
||||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||||
|
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8=
|
||||||
|
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
|
@ -366,9 +376,11 @@ github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
||||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
|
||||||
github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8=
|
github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
github.com/josephspurrier/goversioninfo v0.0.0-20190124120936-8611f5a5ff3f/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE=
|
||||||
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
|
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
|
||||||
|
|
@ -460,6 +472,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
|
||||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||||
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
|
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
|
||||||
github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
|
github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
|
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
|
||||||
|
|
@ -568,6 +582,10 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||||
|
github.com/srwiley/oksvg v0.0.0-20190829233741-58e08c8fe40e h1:LJUrNHytcMXWKxnULIHPe5SCb1jDpO9o672VB1x2EuQ=
|
||||||
|
github.com/srwiley/oksvg v0.0.0-20190829233741-58e08c8fe40e/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
|
||||||
|
github.com/srwiley/rasterx v0.0.0-20181219215540-696f7edb7a7e h1:FFotfUvew9Eg02LYRl8YybAnm0HCwjjfY5JlOI1oB00=
|
||||||
|
github.com/srwiley/rasterx v0.0.0-20181219215540-696f7edb7a7e/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU=
|
||||||
github.com/storageos/go-api v0.0.0-20180912212459-343b3eff91fc/go.mod h1:ZrLn+e0ZuF3Y65PNF6dIwbJPZqfmtCXxFm9ckv0agOY=
|
github.com/storageos/go-api v0.0.0-20180912212459-343b3eff91fc/go.mod h1:ZrLn+e0ZuF3Y65PNF6dIwbJPZqfmtCXxFm9ckv0agOY=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
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/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
|
@ -577,6 +595,7 @@ github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRci
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/syndtr/gocapability v0.0.0-20160928074757-e7cb7fa329f4/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
github.com/syndtr/gocapability v0.0.0-20160928074757-e7cb7fa329f4/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||||
|
|
@ -630,8 +649,12 @@ golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152 h1:ZC1Xn5A1nlpSmQCIva4bZ3
|
||||||
golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20191028145041-f83a4685e152/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495 h1:I6A9Ag9FpEKOjcKrRNjQkPHawoXIhKyTGfvvjFAiiAk=
|
||||||
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
|
||||||
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
|
@ -639,6 +662,8 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=
|
||||||
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
|
@ -695,6 +720,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10=
|
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10=
|
||||||
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191028164358-195ce5e7f934 h1:u/E0NqCIWRDAo9WCFo6Ko49njPFDLSd3z+X1HgWDMpE=
|
golang.org/x/sys v0.0.0-20191028164358-195ce5e7f934 h1:u/E0NqCIWRDAo9WCFo6Ko49njPFDLSd3z+X1HgWDMpE=
|
||||||
golang.org/x/sys v0.0.0-20191028164358-195ce5e7f934/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191028164358-195ce5e7f934/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,12 @@ func makeCacheKey(ns, gvr string, vv []string) string {
|
||||||
return ns + ":" + gvr + "::" + strings.Join(vv, ",")
|
return ns + ":" + gvr + "::" + strings.Join(vv, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *APIClient) clearCache() {
|
||||||
|
for _, k := range a.cache.Keys() {
|
||||||
|
a.cache.Remove(k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CanI checks if user has access to a certain resource.
|
// CanI checks if user has access to a certain resource.
|
||||||
func (a *APIClient) CanI(ns, gvr string, verbs []string) (auth bool, err error) {
|
func (a *APIClient) CanI(ns, gvr string, verbs []string) (auth bool, err error) {
|
||||||
if IsClusterWide(ns) {
|
if IsClusterWide(ns) {
|
||||||
|
|
@ -131,6 +137,9 @@ func (a *APIClient) ValidNamespaces() ([]v1.Namespace, error) {
|
||||||
// BOZO!! No super sure about this approach either??
|
// BOZO!! No super sure about this approach either??
|
||||||
func (a *APIClient) CheckConnectivity() (status bool) {
|
func (a *APIClient) CheckConnectivity() (status bool) {
|
||||||
defer func() {
|
defer func() {
|
||||||
|
if !status {
|
||||||
|
a.clearCache()
|
||||||
|
}
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
status = false
|
status = false
|
||||||
}
|
}
|
||||||
|
|
@ -149,10 +158,10 @@ func (a *APIClient) CheckConnectivity() (status bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := a.checkClientSet.ServerVersion(); err != nil {
|
if _, err := a.checkClientSet.ServerVersion(); err == nil {
|
||||||
log.Error().Err(err).Msgf("K9s can't connect to cluster")
|
|
||||||
} else {
|
|
||||||
status = true
|
status = true
|
||||||
|
} else {
|
||||||
|
log.Error().Err(err).Msgf("K9s can't connect to cluster")
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
@ -258,21 +267,24 @@ func (a *APIClient) MXDial() (*versioned.Clientset, error) {
|
||||||
return a.mxsClient, err
|
return a.mxsClient, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SwitchContextOrDie handles kubeconfig context switches.
|
// SwitchContext handles kubeconfig context switches.
|
||||||
func (a *APIClient) SwitchContextOrDie(ctx string) {
|
func (a *APIClient) SwitchContext(ctx string) error {
|
||||||
currentCtx, err := a.config.CurrentContextName()
|
currentCtx, err := a.config.CurrentContextName()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("Fetching current context")
|
return err
|
||||||
|
}
|
||||||
|
if currentCtx == ctx {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentCtx != ctx {
|
|
||||||
a.cachedClient = nil
|
|
||||||
a.reset()
|
|
||||||
if err := a.config.SwitchContext(ctx); err != nil {
|
if err := a.config.SwitchContext(ctx); err != nil {
|
||||||
log.Fatal().Err(err).Msg("Switching context")
|
return err
|
||||||
}
|
}
|
||||||
|
a.clearCache()
|
||||||
|
a.reset()
|
||||||
_ = a.supportsMxServer()
|
_ = a.supportsMxServer()
|
||||||
}
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *APIClient) reset() {
|
func (a *APIClient) reset() {
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,10 @@ func (c *Config) SwitchContext(name string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := c.GetContext(name); err != nil {
|
||||||
|
return fmt.Errorf("context %s does not exist", name)
|
||||||
|
}
|
||||||
|
|
||||||
if currentCtx != name {
|
if currentCtx != name {
|
||||||
c.reset()
|
c.reset()
|
||||||
c.flags.Context, c.currentContext = &name, name
|
c.flags.Context, c.currentContext = &name, name
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ type Connection interface {
|
||||||
|
|
||||||
Config() *Config
|
Config() *Config
|
||||||
DialOrDie() kubernetes.Interface
|
DialOrDie() kubernetes.Interface
|
||||||
SwitchContextOrDie(ctx string)
|
SwitchContext(ctx string) error
|
||||||
CachedDiscoveryOrDie() *disk.CachedDiscoveryClient
|
CachedDiscoveryOrDie() *disk.CachedDiscoveryClient
|
||||||
RestConfigOrDie() *restclient.Config
|
RestConfigOrDie() *restclient.Config
|
||||||
MXDial() (*versioned.Clientset, error)
|
MXDial() (*versioned.Clientset, error)
|
||||||
|
|
|
||||||
|
|
@ -267,12 +267,14 @@ func (mock *MockConnection) SupportsResource(_param0 string) bool {
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mock *MockConnection) SwitchContextOrDie(_param0 string) {
|
func (mock *MockConnection) SwitchContext(_param0 string) error {
|
||||||
if mock == nil {
|
if mock == nil {
|
||||||
panic("mock must not be nil. Use myMock := NewMockConnection().")
|
panic("mock must not be nil. Use myMock := NewMockConnection().")
|
||||||
}
|
}
|
||||||
params := []pegomock.Param{_param0}
|
params := []pegomock.Param{_param0}
|
||||||
pegomock.GetGenericMockFrom(mock).Invoke("SwitchContextOrDie", params, []reflect.Type{})
|
pegomock.GetGenericMockFrom(mock).Invoke("SwitchContext", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mock *MockConnection) ValidNamespaces() ([]v1.Namespace, error) {
|
func (mock *MockConnection) ValidNamespaces() ([]v1.Namespace, error) {
|
||||||
|
|
|
||||||
|
|
@ -389,6 +389,9 @@ func (s *Styles) Update() {
|
||||||
|
|
||||||
// AsColor checks color index, if match return color otherwise pink it is.
|
// AsColor checks color index, if match return color otherwise pink it is.
|
||||||
func AsColor(c string) tcell.Color {
|
func AsColor(c string) tcell.Color {
|
||||||
|
if c == "default" {
|
||||||
|
return tcell.ColorDefault
|
||||||
|
}
|
||||||
if color, ok := tcell.ColorNames[c]; ok {
|
if color, ok := tcell.ColorNames[c]; ok {
|
||||||
return color
|
return color
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ func makeConn() *conn {
|
||||||
|
|
||||||
func (c *conn) Config() *client.Config { return nil }
|
func (c *conn) Config() *client.Config { return nil }
|
||||||
func (c *conn) DialOrDie() kubernetes.Interface { return nil }
|
func (c *conn) DialOrDie() kubernetes.Interface { return nil }
|
||||||
func (c *conn) SwitchContextOrDie(ctx string) {}
|
func (c *conn) SwitchContext(ctx string) error { return nil }
|
||||||
func (c *conn) CachedDiscoveryOrDie() *disk.CachedDiscoveryClient { return nil }
|
func (c *conn) CachedDiscoveryOrDie() *disk.CachedDiscoveryClient { return nil }
|
||||||
func (c *conn) RestConfigOrDie() *restclient.Config { return nil }
|
func (c *conn) RestConfigOrDie() *restclient.Config { return nil }
|
||||||
func (c *conn) MXDial() (*versioned.Clientset, error) { return nil, nil }
|
func (c *conn) MXDial() (*versioned.Clientset, error) { return nil, nil }
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,7 @@ func (c *Context) MustCurrentContextName() string {
|
||||||
|
|
||||||
// Switch to another context.
|
// Switch to another context.
|
||||||
func (c *Context) Switch(ctx string) error {
|
func (c *Context) Switch(ctx string) error {
|
||||||
c.Factory.Client().SwitchContextOrDie(ctx)
|
return c.Factory.Client().SwitchContext(ctx)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// KubeUpdate modifies kubeconfig default context.
|
// KubeUpdate modifies kubeconfig default context.
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,6 @@ type Node struct {
|
||||||
|
|
||||||
// List returns a collection of node resources.
|
// List returns a collection of node resources.
|
||||||
func (n *Node) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
func (n *Node) List(ctx context.Context, ns string) ([]runtime.Object, error) {
|
||||||
log.Debug().Msgf("NODE-LIST %q:%q", ns, n.gvr)
|
|
||||||
|
|
||||||
labels, ok := ctx.Value(internal.KeyLabels).(string)
|
labels, ok := ctx.Value(internal.KeyLabels).(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Warn().Msgf("No label selector found in context")
|
log.Warn().Msgf("No label selector found in context")
|
||||||
|
|
|
||||||
|
|
@ -332,12 +332,14 @@ func (mock *MockClusterMeta) SupportsResource(_param0 string) bool {
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mock *MockClusterMeta) SwitchContextOrDie(_param0 string) {
|
func (mock *MockClusterMeta) SwitchContext(_param0 string) error {
|
||||||
if mock == nil {
|
if mock == nil {
|
||||||
panic("mock must not be nil. Use myMock := NewMockClusterMeta().")
|
panic("mock must not be nil. Use myMock := NewMockClusterMeta().")
|
||||||
}
|
}
|
||||||
params := []pegomock.Param{_param0}
|
params := []pegomock.Param{_param0}
|
||||||
pegomock.GetGenericMockFrom(mock).Invoke("SwitchContextOrDie", params, []reflect.Type{})
|
pegomock.GetGenericMockFrom(mock).Invoke("SwitchContext", params, []reflect.Type{reflect.TypeOf((*error)(nil)).Elem()})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mock *MockClusterMeta) UserName() (string, error) {
|
func (mock *MockClusterMeta) UserName() (string, error) {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
@ -75,10 +76,15 @@ func (t *Table) RemoveListener(l TableListener) {
|
||||||
|
|
||||||
// Watch initiates model updates.
|
// Watch initiates model updates.
|
||||||
func (t *Table) Watch(ctx context.Context) {
|
func (t *Table) Watch(ctx context.Context) {
|
||||||
t.Refresh(ctx)
|
t.refresh(ctx)
|
||||||
go t.updater(ctx)
|
go t.updater(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Refresh updates the table content.
|
||||||
|
func (t *Table) Refresh(ctx context.Context) {
|
||||||
|
t.refresh(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
// Get returns a resource instance if found, else an error.
|
// Get returns a resource instance if found, else an error.
|
||||||
func (t *Table) Get(ctx context.Context, path string) (runtime.Object, error) {
|
func (t *Table) Get(ctx context.Context, path string) (runtime.Object, error) {
|
||||||
meta, err := t.getMeta(ctx)
|
meta, err := t.getMeta(ctx)
|
||||||
|
|
@ -134,11 +140,6 @@ func (t *Table) ToYAML(ctx context.Context, path string) (string, error) {
|
||||||
return desc.ToYAML(path)
|
return desc.ToYAML(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh update the model now.
|
|
||||||
func (t *Table) Refresh(ctx context.Context) {
|
|
||||||
t.refresh(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetNamespace returns the model namespace.
|
// GetNamespace returns the model namespace.
|
||||||
func (t *Table) GetNamespace() string {
|
func (t *Table) GetNamespace() string {
|
||||||
return t.namespace
|
return t.namespace
|
||||||
|
|
@ -185,6 +186,7 @@ func (t *Table) updater(ctx context.Context) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
t.fireTableLoadFailed(errors.New("operation canceled"))
|
||||||
return
|
return
|
||||||
case <-time.After(rate):
|
case <-time.After(rate):
|
||||||
rate = t.refreshRate
|
rate = t.refreshRate
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ package ui
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/config"
|
"github.com/derailed/k9s/internal/config"
|
||||||
"github.com/derailed/tview"
|
"github.com/derailed/tview"
|
||||||
)
|
)
|
||||||
|
|
@ -94,6 +96,7 @@ func (l *Logo) refreshLogo(c string) {
|
||||||
|
|
||||||
func logo() *tview.TextView {
|
func logo() *tview.TextView {
|
||||||
v := tview.NewTextView()
|
v := tview.NewTextView()
|
||||||
|
v.SetBackgroundColor(tcell.ColorDefault)
|
||||||
v.SetWordWrap(false)
|
v.SetWordWrap(false)
|
||||||
v.SetWrap(false)
|
v.SetWrap(false)
|
||||||
v.SetTextAlign(tview.AlignLeft)
|
v.SetTextAlign(tview.AlignLeft)
|
||||||
|
|
@ -104,6 +107,7 @@ func logo() *tview.TextView {
|
||||||
|
|
||||||
func status() *tview.TextView {
|
func status() *tview.TextView {
|
||||||
v := tview.NewTextView()
|
v := tview.NewTextView()
|
||||||
|
v.SetBackgroundColor(tcell.ColorDefault)
|
||||||
v.SetWordWrap(false)
|
v.SetWordWrap(false)
|
||||||
v.SetWrap(false)
|
v.SetWrap(false)
|
||||||
v.SetTextAlign(tview.AlignCenter)
|
v.SetTextAlign(tview.AlignCenter)
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ func NewTree() *Tree {
|
||||||
|
|
||||||
// Init initializes the view
|
// Init initializes the view
|
||||||
func (t *Tree) Init(ctx context.Context) error {
|
func (t *Tree) Init(ctx context.Context) error {
|
||||||
t.bindKeys()
|
t.BindKeys()
|
||||||
t.SetBorder(true)
|
t.SetBorder(true)
|
||||||
t.SetBorderAttributes(tcell.AttrBold)
|
t.SetBorderAttributes(tcell.AttrBold)
|
||||||
t.SetBorderPadding(0, 0, 1, 1)
|
t.SetBorderPadding(0, 0, 1, 1)
|
||||||
|
|
@ -86,7 +86,7 @@ func (t *Tree) ExtraHints() map[string]string {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tree) bindKeys() {
|
func (t *Tree) BindKeys() {
|
||||||
t.Actions().Add(KeyActions{
|
t.Actions().Add(KeyActions{
|
||||||
KeySpace: NewKeyAction("Expand/Collapse", t.noopCmd, true),
|
KeySpace: NewKeyAction("Expand/Collapse", t.noopCmd, true),
|
||||||
KeyX: NewKeyAction("Expand/Collapse All", t.toggleCollapseCmd, true),
|
KeyX: NewKeyAction("Expand/Collapse All", t.toggleCollapseCmd, true),
|
||||||
|
|
|
||||||
|
|
@ -118,12 +118,16 @@ func execCmd(r Runner, bin string, bg bool, args ...string) ui.ActionHandler {
|
||||||
|
|
||||||
ns, _ := client.Namespaced(path)
|
ns, _ := client.Namespaced(path)
|
||||||
var (
|
var (
|
||||||
env = r.EnvFn()()
|
|
||||||
aa = make([]string, len(args))
|
aa = make([]string, len(args))
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if r.EnvFn() == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
for i, a := range args {
|
for i, a := range args {
|
||||||
aa[i], err = env.envFor(ns, a)
|
aa[i], err = r.EnvFn()().envFor(ns, a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Plugin Args match failed")
|
log.Error().Err(err).Msg("Plugin Args match failed")
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal"
|
"github.com/derailed/k9s/internal"
|
||||||
|
|
@ -24,7 +24,7 @@ var ExitStatus = ""
|
||||||
const (
|
const (
|
||||||
splashDelay = 1 * time.Second
|
splashDelay = 1 * time.Second
|
||||||
clusterRefresh = 5 * time.Second
|
clusterRefresh = 5 * time.Second
|
||||||
maxConRetry = 5
|
maxConRetry = 10
|
||||||
clusterInfoWidth = 50
|
clusterInfoWidth = 50
|
||||||
clusterInfoPad = 15
|
clusterInfoPad = 15
|
||||||
)
|
)
|
||||||
|
|
@ -39,9 +39,8 @@ type App struct {
|
||||||
version string
|
version string
|
||||||
showHeader bool
|
showHeader bool
|
||||||
cancelFn context.CancelFunc
|
cancelFn context.CancelFunc
|
||||||
conRetry int
|
conRetry int32
|
||||||
clusterModel *model.ClusterInfo
|
clusterModel *model.ClusterInfo
|
||||||
mx sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewApp returns a K9s app instance.
|
// NewApp returns a K9s app instance.
|
||||||
|
|
@ -61,9 +60,7 @@ func NewApp(cfg *config.Config) *App {
|
||||||
|
|
||||||
// ConOK checks the connection is cool, returns false otherwise.
|
// ConOK checks the connection is cool, returns false otherwise.
|
||||||
func (a *App) ConOK() bool {
|
func (a *App) ConOK() bool {
|
||||||
a.mx.Lock()
|
return atomic.LoadInt32(&a.conRetry) == 0
|
||||||
defer a.mx.Unlock()
|
|
||||||
return a.conRetry == 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the application.
|
// Init initializes the application.
|
||||||
|
|
@ -198,32 +195,31 @@ func (a *App) clusterUpdater(ctx context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) refreshCluster() {
|
func (a *App) refreshCluster() {
|
||||||
a.mx.Lock()
|
|
||||||
defer a.mx.Unlock()
|
|
||||||
|
|
||||||
c := a.Content.Top()
|
c := a.Content.Top()
|
||||||
if ok := a.Conn().CheckConnectivity(); ok {
|
if ok := a.Conn().CheckConnectivity(); ok {
|
||||||
if a.conRetry > 0 {
|
if atomic.LoadInt32(&a.conRetry) > 0 {
|
||||||
|
atomic.StoreInt32(&a.conRetry, 0)
|
||||||
|
a.Status(ui.FlashInfo, "K8s connectivity OK")
|
||||||
if c != nil {
|
if c != nil {
|
||||||
c.Start()
|
c.Start()
|
||||||
}
|
}
|
||||||
a.Status(ui.FlashInfo, "K8s connectivity OK")
|
|
||||||
}
|
}
|
||||||
a.conRetry = 0
|
|
||||||
} else {
|
} else {
|
||||||
a.conRetry++
|
atomic.AddInt32(&a.conRetry, 1)
|
||||||
log.Warn().Msgf("Conn check failed (%d/%d)", a.conRetry, maxConRetry)
|
|
||||||
if c != nil {
|
if c != nil {
|
||||||
c.Stop()
|
c.Stop()
|
||||||
}
|
}
|
||||||
a.Status(ui.FlashWarn, fmt.Sprintf("Dial K8s failed (%d)", a.conRetry))
|
count := atomic.LoadInt32(&a.conRetry)
|
||||||
|
log.Warn().Msgf("Conn check failed (%d/%d)", count, maxConRetry)
|
||||||
|
a.Status(ui.FlashWarn, fmt.Sprintf("Dial K8s failed (%d)", count))
|
||||||
}
|
}
|
||||||
if a.conRetry >= maxConRetry {
|
|
||||||
ExitStatus = fmt.Sprintf("Lost K8s connection (%d). Bailing out!", a.conRetry)
|
count := atomic.LoadInt32(&a.conRetry)
|
||||||
|
if count >= maxConRetry {
|
||||||
|
ExitStatus = fmt.Sprintf("Lost K8s connection (%d). Bailing out!", count)
|
||||||
a.BailOut()
|
a.BailOut()
|
||||||
}
|
}
|
||||||
if a.conRetry > 0 {
|
if count > 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,6 @@ func (b *Browser) SetInstance(path string) {
|
||||||
func (b *Browser) Start() {
|
func (b *Browser) Start() {
|
||||||
b.Stop()
|
b.Stop()
|
||||||
|
|
||||||
log.Debug().Msgf("BROWSER started!")
|
|
||||||
b.Table.Start()
|
b.Table.Start()
|
||||||
ctx := b.defaultContext()
|
ctx := b.defaultContext()
|
||||||
ctx, b.cancelFn = context.WithCancel(ctx)
|
ctx, b.cancelFn = context.WithCancel(ctx)
|
||||||
|
|
@ -111,7 +110,6 @@ func (b *Browser) Stop() {
|
||||||
if b.cancelFn == nil {
|
if b.cancelFn == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Debug().Msgf("BROWSER Stopped!")
|
|
||||||
b.Table.Stop()
|
b.Table.Stop()
|
||||||
b.cancelFn()
|
b.cancelFn()
|
||||||
b.cancelFn = nil
|
b.cancelFn = nil
|
||||||
|
|
@ -373,7 +371,6 @@ func (b *Browser) refreshActions() {
|
||||||
|
|
||||||
if b.app.ConOK() {
|
if b.app.ConOK() {
|
||||||
b.namespaceActions(aa)
|
b.namespaceActions(aa)
|
||||||
|
|
||||||
if !b.app.Config.K9s.GetReadOnly() {
|
if !b.app.Config.K9s.GetReadOnly() {
|
||||||
if client.Can(b.meta.Verbs, "edit") {
|
if client.Can(b.meta.Verbs, "edit") {
|
||||||
aa[ui.KeyE] = ui.NewKeyAction("Edit", b.editCmd, true)
|
aa[ui.KeyE] = ui.NewKeyAction("Edit", b.editCmd, true)
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ func useContext(app *App, name string) error {
|
||||||
return errors.New("Expecting a switchable resource")
|
return errors.New("Expecting a switchable resource")
|
||||||
}
|
}
|
||||||
if err := switcher.Switch(name); err != nil {
|
if err := switcher.Switch(name); err != nil {
|
||||||
|
log.Error().Err(err).Msgf("Context switch failed")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := app.switchCtx(name, false); err != nil {
|
if err := app.switchCtx(name, false); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ func execute(opts shellOpts) error {
|
||||||
cancel()
|
cancel()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
log.Debug().Msgf("Running command > %s %s", opts.binary, strings.Join(opts.args, " "))
|
log.Debug().Msgf("Running command> %s %s", opts.binary, strings.Join(opts.args, " "))
|
||||||
|
|
||||||
cmd := exec.Command(opts.binary, opts.args...)
|
cmd := exec.Command(opts.binary, opts.args...)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,39 +16,44 @@ import (
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func defaultK9sEnv(app *App, sel string, row render.Row) K9sEnv {
|
func generalEnv(a *App) K9sEnv {
|
||||||
ns, n := client.Namespaced(sel)
|
ctx, err := a.Conn().Config().CurrentContextName()
|
||||||
ctx, err := app.Conn().Config().CurrentContextName()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx = render.NAValue
|
ctx = render.NAValue
|
||||||
}
|
}
|
||||||
cluster, err := app.Conn().Config().CurrentClusterName()
|
cluster, err := a.Conn().Config().CurrentClusterName()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cluster = render.NAValue
|
cluster = render.NAValue
|
||||||
}
|
}
|
||||||
user, err := app.Conn().Config().CurrentUserName()
|
user, err := a.Conn().Config().CurrentUserName()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
user = render.NAValue
|
user = render.NAValue
|
||||||
}
|
}
|
||||||
groups, err := app.Conn().Config().CurrentGroupNames()
|
groups, err := a.Conn().Config().CurrentGroupNames()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
groups = []string{render.NAValue}
|
groups = []string{render.NAValue}
|
||||||
}
|
}
|
||||||
|
|
||||||
var cfg string
|
var cfg string
|
||||||
kcfg := app.Conn().Config().Flags().KubeConfig
|
kcfg := a.Conn().Config().Flags().KubeConfig
|
||||||
if kcfg != nil && *kcfg != "" {
|
if kcfg != nil && *kcfg != "" {
|
||||||
cfg = *kcfg
|
cfg = *kcfg
|
||||||
}
|
}
|
||||||
|
|
||||||
env := K9sEnv{
|
return K9sEnv{
|
||||||
"NAMESPACE": ns,
|
|
||||||
"NAME": n,
|
|
||||||
"CONTEXT": ctx,
|
"CONTEXT": ctx,
|
||||||
"CLUSTER": cluster,
|
"CLUSTER": cluster,
|
||||||
"USER": user,
|
"USER": user,
|
||||||
"GROUPS": strings.Join(groups, ","),
|
"GROUPS": strings.Join(groups, ","),
|
||||||
"KUBECONFIG": cfg,
|
"KUBECONFIG": cfg,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultK9sEnv(a *App, sel string, row render.Row) K9sEnv {
|
||||||
|
ns, n := client.Namespaced(sel)
|
||||||
|
|
||||||
|
env := generalEnv(a)
|
||||||
|
env["NAMESPACE"], env["NAME"] = ns, n
|
||||||
|
|
||||||
for i, r := range row.Fields {
|
for i, r := range row.Fields {
|
||||||
env["COL"+strconv.Itoa(i)] = r
|
env["COL"+strconv.Itoa(i)] = r
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/derailed/k9s/internal"
|
"github.com/derailed/k9s/internal"
|
||||||
"github.com/derailed/k9s/internal/client"
|
"github.com/derailed/k9s/internal/client"
|
||||||
"github.com/derailed/k9s/internal/dao"
|
"github.com/derailed/k9s/internal/dao"
|
||||||
|
"github.com/derailed/k9s/internal/model"
|
||||||
"github.com/derailed/k9s/internal/render"
|
"github.com/derailed/k9s/internal/render"
|
||||||
"github.com/derailed/k9s/internal/ui"
|
"github.com/derailed/k9s/internal/ui"
|
||||||
"github.com/derailed/k9s/internal/watch"
|
"github.com/derailed/k9s/internal/watch"
|
||||||
|
|
@ -108,47 +109,86 @@ func (p *Pod) killCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pod) shellCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (p *Pod) shellCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
sel := p.GetTable().GetSelectedItem()
|
path := p.GetTable().GetSelectedItem()
|
||||||
if sel == "" {
|
if path == "" {
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
|
|
||||||
row := p.GetTable().GetSelectedRowIndex()
|
row := p.GetTable().GetSelectedRowIndex()
|
||||||
status := ui.TrimCell(p.GetTable().SelectTable, row, p.GetTable().NameColIndex()+2)
|
status := ui.TrimCell(p.GetTable().SelectTable, row, p.GetTable().NameColIndex()+2)
|
||||||
if status != render.Running {
|
if status != render.Running {
|
||||||
p.App().Flash().Errf("%s is not in a running state", sel)
|
p.App().Flash().Errf("%s is not in a running state", path)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
cc, err := fetchContainers(p.App().factory, sel, false)
|
|
||||||
if err != nil {
|
if err := containerShellin(p.App(), p, path, ""); err != nil {
|
||||||
p.App().Flash().Errf("Unable to retrieve containers %s", err)
|
|
||||||
return evt
|
|
||||||
}
|
|
||||||
if len(cc) == 1 {
|
|
||||||
p.shellIn(sel, cc[0])
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
picker := NewPicker()
|
|
||||||
picker.populate(cc)
|
|
||||||
picker.SetSelectedFunc(func(i int, t, d string, r rune) {
|
|
||||||
p.shellIn(sel, t)
|
|
||||||
})
|
|
||||||
if err := p.App().inject(picker); err != nil {
|
|
||||||
p.App().Flash().Err(err)
|
p.App().Flash().Err(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return evt
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Pod) shellIn(path, co string) {
|
|
||||||
p.Stop()
|
|
||||||
shellIn(p.App(), path, co)
|
|
||||||
p.Start()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Helpers...
|
// Helpers...
|
||||||
|
|
||||||
|
func containerShellin(a *App, comp model.Component, path, co string) error {
|
||||||
|
if co != "" {
|
||||||
|
resumeShellIn(a, comp, path, co)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cc, err := fetchContainers(a.factory, path, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(cc) == 1 {
|
||||||
|
resumeShellIn(a, comp, path, cc[0])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
picker := NewPicker()
|
||||||
|
picker.populate(cc)
|
||||||
|
picker.SetSelectedFunc(func(_ int, co, _ string, _ rune) {
|
||||||
|
resumeShellIn(a, comp, path, co)
|
||||||
|
})
|
||||||
|
if err := a.inject(picker); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resumeShellIn(a *App, c model.Component, path, co string) {
|
||||||
|
c.Stop()
|
||||||
|
defer c.Start()
|
||||||
|
shellIn(a, path, co)
|
||||||
|
}
|
||||||
|
|
||||||
|
func shellIn(a *App, path, co string) {
|
||||||
|
args := computeShellArgs(path, co, a.Config.K9s.CurrentContext, a.Conn().Config().Flags().KubeConfig)
|
||||||
|
|
||||||
|
c := color.New(color.BgGreen).Add(color.FgBlack).Add(color.Bold)
|
||||||
|
if !runK(a, shellOpts{clear: true, banner: c.Sprintf(bannerFmt, path, co), args: args}) {
|
||||||
|
a.Flash().Err(errors.New("Shell exec failed"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeShellArgs(path, co, context string, kcfg *string) []string {
|
||||||
|
args := make([]string, 0, 15)
|
||||||
|
args = append(args, "exec", "-it")
|
||||||
|
args = append(args, "--context", context)
|
||||||
|
ns, po := client.Namespaced(path)
|
||||||
|
args = append(args, "-n", ns)
|
||||||
|
args = append(args, po)
|
||||||
|
if kcfg != nil && *kcfg != "" {
|
||||||
|
args = append(args, "--kubeconfig", *kcfg)
|
||||||
|
}
|
||||||
|
if co != "" {
|
||||||
|
args = append(args, "-c", co)
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(args, "--", "sh", "-c", shellCheck)
|
||||||
|
}
|
||||||
|
|
||||||
func fetchContainers(f *watch.Factory, path string, includeInit bool) ([]string, error) {
|
func fetchContainers(f *watch.Factory, path string, includeInit bool) ([]string, error) {
|
||||||
o, err := f.Get("v1/pods", path, true, labels.Everything())
|
o, err := f.Get("v1/pods", path, true, labels.Everything())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -172,30 +212,3 @@ func fetchContainers(f *watch.Factory, path string, includeInit bool) ([]string,
|
||||||
}
|
}
|
||||||
return nn, nil
|
return nn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func shellIn(a *App, path, co string) {
|
|
||||||
args := computeShellArgs(path, co, a.Config.K9s.CurrentContext, a.Conn().Config().Flags().KubeConfig)
|
|
||||||
log.Debug().Msgf("Shell args %v", args)
|
|
||||||
|
|
||||||
c := color.New(color.BgGreen).Add(color.FgBlack).Add(color.Bold)
|
|
||||||
if !runK(a, shellOpts{clear: true, banner: c.Sprintf(bannerFmt, path, co), args: args}) {
|
|
||||||
a.Flash().Err(errors.New("Shell exec failed"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func computeShellArgs(path, co, context string, kcfg *string) []string {
|
|
||||||
args := make([]string, 0, 15)
|
|
||||||
args = append(args, "exec", "-it")
|
|
||||||
args = append(args, "--context", context)
|
|
||||||
ns, po := client.Namespaced(path)
|
|
||||||
args = append(args, "-n", ns)
|
|
||||||
args = append(args, po)
|
|
||||||
if kcfg != nil && *kcfg != "" {
|
|
||||||
args = append(args, "--kubeconfig", *kcfg)
|
|
||||||
}
|
|
||||||
if co != "" {
|
|
||||||
args = append(args, "-c", co)
|
|
||||||
}
|
|
||||||
|
|
||||||
return append(args, "--", "sh", "-c", shellCheck)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,8 @@ func NewXray(gvr client.GVR) ResourceViewer {
|
||||||
|
|
||||||
// Init initializes the view
|
// Init initializes the view
|
||||||
func (x *Xray) Init(ctx context.Context) error {
|
func (x *Xray) Init(ctx context.Context) error {
|
||||||
|
x.envFn = x.k9sEnv
|
||||||
|
|
||||||
if err := x.Tree.Init(ctx); err != nil {
|
if err := x.Tree.Init(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -77,12 +79,12 @@ func (x *Xray) Init(ctx context.Context) error {
|
||||||
x.model.AddListener(x)
|
x.model.AddListener(x)
|
||||||
|
|
||||||
x.SetChangedFunc(func(n *tview.TreeNode) {
|
x.SetChangedFunc(func(n *tview.TreeNode) {
|
||||||
ref, ok := n.GetReference().(xray.NodeSpec)
|
spec, ok := n.GetReference().(xray.NodeSpec)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Error().Msgf("No ref found on node %s", n.GetText())
|
log.Error().Msgf("No ref found on node %s", n.GetText())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
x.SetSelectedItem(ref.Path)
|
x.SetSelectedItem(spec.AsPath())
|
||||||
x.refreshActions()
|
x.refreshActions()
|
||||||
})
|
})
|
||||||
x.refreshActions()
|
x.refreshActions()
|
||||||
|
|
@ -131,16 +133,18 @@ func (x *Xray) refreshActions() {
|
||||||
|
|
||||||
x.Actions().Clear()
|
x.Actions().Clear()
|
||||||
x.bindKeys()
|
x.bindKeys()
|
||||||
|
x.Tree.BindKeys()
|
||||||
|
|
||||||
ref := x.selectedSpec()
|
spec := x.selectedSpec()
|
||||||
if ref == nil {
|
if spec == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gvr := spec.GVR()
|
||||||
var err error
|
var err error
|
||||||
x.meta, err = dao.MetaAccess.MetaFor(client.NewGVR(ref.GVR))
|
x.meta, err = dao.MetaAccess.MetaFor(client.NewGVR(gvr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn().Msgf("NO meta for %q -- %s", ref.GVR, err)
|
log.Warn().Msgf("NO meta for %q -- %s", gvr, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,22 +159,27 @@ func (x *Xray) refreshActions() {
|
||||||
aa[ui.KeyD] = ui.NewKeyAction("Describe", x.describeCmd, true)
|
aa[ui.KeyD] = ui.NewKeyAction("Describe", x.describeCmd, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ref.GVR == "containers" {
|
switch gvr {
|
||||||
|
case "containers":
|
||||||
|
x.Actions().Delete(tcell.KeyEnter)
|
||||||
|
aa[ui.KeyS] = ui.NewKeyAction("Shell", x.shellCmd, true)
|
||||||
|
aa[ui.KeyL] = ui.NewKeyAction("Logs", x.logsCmd(false), true)
|
||||||
|
aa[ui.KeyShiftL] = ui.NewKeyAction("Logs Previous", x.logsCmd(true), true)
|
||||||
|
case "v1/pods":
|
||||||
aa[ui.KeyS] = ui.NewKeyAction("Shell", x.shellCmd, true)
|
aa[ui.KeyS] = ui.NewKeyAction("Shell", x.shellCmd, true)
|
||||||
aa[ui.KeyL] = ui.NewKeyAction("Logs", x.logsCmd(false), true)
|
aa[ui.KeyL] = ui.NewKeyAction("Logs", x.logsCmd(false), true)
|
||||||
aa[ui.KeyShiftL] = ui.NewKeyAction("Logs Previous", x.logsCmd(true), true)
|
aa[ui.KeyShiftL] = ui.NewKeyAction("Logs Previous", x.logsCmd(true), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
x.Actions().Add(aa)
|
x.Actions().Add(aa)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSelectedPath returns the current selection as string.
|
// GetSelectedPath returns the current selection as string.
|
||||||
func (x *Xray) GetSelectedPath() string {
|
func (x *Xray) GetSelectedPath() string {
|
||||||
ref := x.selectedSpec()
|
spec := x.selectedSpec()
|
||||||
if ref == nil {
|
if spec == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return ref.Path
|
return spec.Path()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Xray) selectedSpec() *xray.NodeSpec {
|
func (x *Xray) selectedSpec() *xray.NodeSpec {
|
||||||
|
|
@ -193,6 +202,34 @@ func (x *Xray) EnvFn() EnvFunc {
|
||||||
return x.envFn
|
return x.envFn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Xray) k9sEnv() K9sEnv {
|
||||||
|
env := generalEnv(x.app)
|
||||||
|
|
||||||
|
spec := x.selectedSpec()
|
||||||
|
if spec == nil {
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
|
||||||
|
env["FILTER"] = x.CmdBuff().String()
|
||||||
|
if env["FILTER"] == "" {
|
||||||
|
ns, n := client.Namespaced(spec.Path())
|
||||||
|
env["NAMESPACE"], env["FILTER"] = ns, n
|
||||||
|
}
|
||||||
|
|
||||||
|
switch spec.GVR() {
|
||||||
|
case "containers":
|
||||||
|
_, co := client.Namespaced(spec.Path())
|
||||||
|
env["CONTAINER"] = co
|
||||||
|
ns, n := client.Namespaced(*spec.ParentPath())
|
||||||
|
env["NAMESPACE"], env["POD"], env["NAME"] = ns, n, co
|
||||||
|
default:
|
||||||
|
ns, n := client.Namespaced(spec.Path())
|
||||||
|
env["NAMESPACE"], env["NAME"] = ns, n
|
||||||
|
}
|
||||||
|
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
|
||||||
// Aliases returns all available aliases.
|
// Aliases returns all available aliases.
|
||||||
func (x *Xray) Aliases() []string {
|
func (x *Xray) Aliases() []string {
|
||||||
return append(x.meta.ShortNames, x.meta.SingularName, x.meta.Name)
|
return append(x.meta.ShortNames, x.meta.SingularName, x.meta.Name)
|
||||||
|
|
@ -200,100 +237,98 @@ func (x *Xray) Aliases() []string {
|
||||||
|
|
||||||
func (x *Xray) logsCmd(prev bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
func (x *Xray) logsCmd(prev bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return func(evt *tcell.EventKey) *tcell.EventKey {
|
return func(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
ref := x.selectedSpec()
|
spec := x.selectedSpec()
|
||||||
if ref == nil {
|
if spec == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if ref.Parent != nil {
|
x.showLogs(spec, prev)
|
||||||
x.showLogs(ref.Parent, ref, prev)
|
|
||||||
} else {
|
|
||||||
log.Error().Msgf("No parent found for container %q", ref.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Xray) showLogs(pod, co *xray.NodeSpec, prev bool) {
|
func (x *Xray) showLogs(spec *xray.NodeSpec, prev bool) {
|
||||||
// Need to load and wait for pods
|
// Need to load and wait for pods
|
||||||
ns, _ := client.Namespaced(pod.Path)
|
path, co := spec.Path(), ""
|
||||||
|
if spec.GVR() == "containers" {
|
||||||
|
_, coName := client.Namespaced(spec.Path())
|
||||||
|
path, co = *spec.ParentPath(), coName
|
||||||
|
}
|
||||||
|
|
||||||
|
ns, _ := client.Namespaced(path)
|
||||||
_, err := x.app.factory.CanForResource(ns, "v1/pods", client.MonitorAccess)
|
_, err := x.app.factory.CanForResource(ns, "v1/pods", client.MonitorAccess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
x.app.Flash().Err(err)
|
x.app.Flash().Err(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := x.app.inject(NewLog(client.NewGVR(co.GVR), pod.Path, co.Path, prev)); err != nil {
|
if err := x.app.inject(NewLog(client.NewGVR("v1/pods"), path, co, prev)); err != nil {
|
||||||
x.app.Flash().Err(err)
|
x.app.Flash().Err(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Xray) shellCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (x *Xray) shellCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
ref := x.selectedSpec()
|
spec := x.selectedSpec()
|
||||||
if ref == nil {
|
if spec == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if ref.Status != "" {
|
if spec.Status() != "ok" {
|
||||||
x.app.Flash().Errf("%s is not in a running state", ref.Path)
|
x.app.Flash().Errf("%s is not in a running state", spec.Path())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if ref.Parent != nil {
|
path, co := spec.Path(), ""
|
||||||
_, co := client.Namespaced(ref.Path)
|
if spec.GVR() == "containers" {
|
||||||
x.shellIn(ref.Parent.Path, co)
|
_, co = client.Namespaced(spec.Path())
|
||||||
} else {
|
path = *spec.ParentPath()
|
||||||
log.Error().Msgf("No parent found on container node %q", ref.Path)
|
}
|
||||||
|
|
||||||
|
if err := containerShellin(x.app, x, path, co); err != nil {
|
||||||
|
x.app.Flash().Err(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Xray) shellIn(path, co string) {
|
|
||||||
x.Stop()
|
|
||||||
shellIn(x.app, path, co)
|
|
||||||
x.Start()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Xray) viewCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (x *Xray) viewCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
ref := x.selectedSpec()
|
spec := x.selectedSpec()
|
||||||
if ref == nil {
|
if spec == nil {
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := x.defaultContext()
|
ctx := x.defaultContext()
|
||||||
raw, err := x.model.ToYAML(ctx, ref.GVR, ref.Path)
|
raw, err := x.model.ToYAML(ctx, spec.GVR(), spec.Path())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
x.App().Flash().Errf("unable to get resource %q -- %s", ref.GVR, err)
|
x.App().Flash().Errf("unable to get resource %q -- %s", spec.GVR(), err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
details := NewDetails(x.app, "YAML", ref.Path, true).Update(raw)
|
details := NewDetails(x.app, "YAML", spec.Path(), true).Update(raw)
|
||||||
if err := x.app.inject(details); err != nil {
|
if err := x.app.inject(details); err != nil {
|
||||||
x.app.Flash().Err(err)
|
x.app.Flash().Err(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Xray) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (x *Xray) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
ref := x.selectedSpec()
|
spec := x.selectedSpec()
|
||||||
if ref == nil {
|
if spec == nil {
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
|
|
||||||
x.Stop()
|
x.Stop()
|
||||||
defer x.Start()
|
defer x.Start()
|
||||||
{
|
{
|
||||||
gvr := client.NewGVR(ref.GVR)
|
gvr := client.NewGVR(spec.GVR())
|
||||||
meta, err := dao.MetaAccess.MetaFor(gvr)
|
meta, err := dao.MetaAccess.MetaFor(gvr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn().Msgf("NO meta for %q -- %s", ref.GVR, err)
|
log.Warn().Msgf("NO meta for %q -- %s", spec.GVR(), err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
x.resourceDelete(gvr, ref, fmt.Sprintf("Delete %s %s?", meta.SingularName, ref.Path))
|
x.resourceDelete(gvr, spec, fmt.Sprintf("Delete %s %s?", meta.SingularName, spec.Path()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -301,12 +336,12 @@ func (x *Xray) deleteCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Xray) describeCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (x *Xray) describeCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
ref := x.selectedSpec()
|
spec := x.selectedSpec()
|
||||||
if ref == nil {
|
if spec == nil {
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
|
|
||||||
x.describe(ref.GVR, ref.Path)
|
x.describe(spec.GVR(), spec.Path())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -328,18 +363,18 @@ func (x *Xray) describe(gvr, path string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Xray) editCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (x *Xray) editCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
ref := x.selectedSpec()
|
spec := x.selectedSpec()
|
||||||
if ref == nil {
|
if spec == nil {
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
|
|
||||||
x.Stop()
|
x.Stop()
|
||||||
defer x.Start()
|
defer x.Start()
|
||||||
{
|
{
|
||||||
ns, n := client.Namespaced(ref.Path)
|
ns, n := client.Namespaced(spec.Path())
|
||||||
args := make([]string, 0, 10)
|
args := make([]string, 0, 10)
|
||||||
args = append(args, "edit")
|
args = append(args, "edit")
|
||||||
args = append(args, client.NewGVR(ref.GVR).R())
|
args = append(args, client.NewGVR(spec.GVR()).R())
|
||||||
args = append(args, "-n", ns)
|
args = append(args, "-n", ns)
|
||||||
args = append(args, "--context", x.app.Config.K9s.CurrentContext)
|
args = append(args, "--context", x.app.Config.K9s.CurrentContext)
|
||||||
if cfg := x.app.Conn().Config().Flags().KubeConfig; cfg != nil && *cfg != "" {
|
if cfg := x.app.Conn().Config().Flags().KubeConfig; cfg != nil && *cfg != "" {
|
||||||
|
|
@ -408,14 +443,15 @@ func (x *Xray) gotoCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ref := x.selectedSpec()
|
spec := x.selectedSpec()
|
||||||
if ref == nil {
|
if spec == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if len(strings.Split(ref.Path, "/")) == 1 {
|
log.Debug().Msgf("SELECTED REF %#v", spec)
|
||||||
|
if len(strings.Split(spec.Path(), "/")) == 1 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := x.app.viewResource(client.NewGVR(ref.GVR).R(), ref.Path, false); err != nil {
|
if err := x.app.viewResource(client.NewGVR(spec.GVR()).R(), spec.Path(), false); err != nil {
|
||||||
x.app.Flash().Err(err)
|
x.app.Flash().Err(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -464,13 +500,13 @@ func (x *Xray) update(node *xray.TreeNode) {
|
||||||
x.hydrate(root, c)
|
x.hydrate(root, c)
|
||||||
}
|
}
|
||||||
if x.GetSelectedItem() == "" {
|
if x.GetSelectedItem() == "" {
|
||||||
x.SetSelectedItem(node.ID)
|
x.SetSelectedItem(node.Spec().Path())
|
||||||
}
|
}
|
||||||
|
|
||||||
x.app.QueueUpdateDraw(func() {
|
x.app.QueueUpdateDraw(func() {
|
||||||
x.SetRoot(root)
|
x.SetRoot(root)
|
||||||
root.Walk(func(node, parent *tview.TreeNode) bool {
|
root.Walk(func(node, parent *tview.TreeNode) bool {
|
||||||
ref, ok := node.GetReference().(xray.NodeSpec)
|
spec, ok := node.GetReference().(xray.NodeSpec)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Error().Msgf("Expecting a NodeSpec but got %T", node.GetReference())
|
log.Error().Msgf("Expecting a NodeSpec but got %T", node.GetReference())
|
||||||
return false
|
return false
|
||||||
|
|
@ -482,7 +518,8 @@ func (x *Xray) update(node *xray.TreeNode) {
|
||||||
node.SetExpanded(true)
|
node.SetExpanded(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ref.Path == x.GetSelectedItem() {
|
if spec.AsPath() == x.GetSelectedItem() {
|
||||||
|
log.Debug().Msgf("SEL %q--%q", spec.Path(), x.GetSelectedItem())
|
||||||
node.SetExpanded(true).SetSelectable(true)
|
node.SetExpanded(true).SetSelectable(true)
|
||||||
x.SetCurrentNode(node)
|
x.SetCurrentNode(node)
|
||||||
}
|
}
|
||||||
|
|
@ -611,9 +648,9 @@ func (x *Xray) styleTitle() string {
|
||||||
return title + ui.SkinTitle(fmt.Sprintf(ui.SearchFmt, buff), x.app.Styles.Frame())
|
return title + ui.SkinTitle(fmt.Sprintf(ui.SearchFmt, buff), x.app.Styles.Frame())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Xray) resourceDelete(gvr client.GVR, ref *xray.NodeSpec, msg string) {
|
func (x *Xray) resourceDelete(gvr client.GVR, spec *xray.NodeSpec, msg string) {
|
||||||
dialog.ShowDelete(x.app.Content.Pages, msg, func(cascade, force bool) {
|
dialog.ShowDelete(x.app.Content.Pages, msg, func(cascade, force bool) {
|
||||||
x.app.Flash().Infof("Delete resource %s %s", ref.GVR, ref.Path)
|
x.app.Flash().Infof("Delete resource %s %s", spec.GVR(), spec.Path())
|
||||||
accessor, err := dao.AccessorFor(x.app.factory, gvr)
|
accessor, err := dao.AccessorFor(x.app.factory, gvr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("No accessor")
|
log.Error().Err(err).Msgf("No accessor")
|
||||||
|
|
@ -625,11 +662,11 @@ func (x *Xray) resourceDelete(gvr client.GVR, ref *xray.NodeSpec, msg string) {
|
||||||
x.app.Flash().Errf("Invalid nuker %T", accessor)
|
x.app.Flash().Errf("Invalid nuker %T", accessor)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := nuker.Delete(ref.Path, true, true); err != nil {
|
if err := nuker.Delete(spec.Path(), true, true); err != nil {
|
||||||
x.app.Flash().Errf("Delete failed with `%s", err)
|
x.app.Flash().Errf("Delete failed with `%s", err)
|
||||||
} else {
|
} else {
|
||||||
x.app.Flash().Infof("%s `%s deleted successfully", x.GVR(), ref.Path)
|
x.app.Flash().Infof("%s `%s deleted successfully", x.GVR(), spec.Path())
|
||||||
x.app.factory.DeleteForwarder(ref.Path)
|
x.app.factory.DeleteForwarder(spec.Path())
|
||||||
}
|
}
|
||||||
x.Refresh()
|
x.Refresh()
|
||||||
}, func() {})
|
}, func() {})
|
||||||
|
|
@ -661,15 +698,7 @@ func makeTreeNode(node *xray.TreeNode, expanded bool, styles *config.Styles) *tv
|
||||||
n := tview.NewTreeNode("No data...")
|
n := tview.NewTreeNode("No data...")
|
||||||
if node != nil {
|
if node != nil {
|
||||||
n.SetText(node.Title(styles.Xray()))
|
n.SetText(node.Title(styles.Xray()))
|
||||||
spec := xray.NodeSpec{}
|
n.SetReference(node.Spec())
|
||||||
if p := node.Parent; p != nil {
|
|
||||||
spec.GVR, spec.Path = p.GVR, p.ID
|
|
||||||
}
|
|
||||||
n.SetReference(xray.NodeSpec{
|
|
||||||
GVR: node.GVR,
|
|
||||||
Path: node.ID,
|
|
||||||
Parent: &spec,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
n.SetSelectable(true)
|
n.SetSelectable(true)
|
||||||
n.SetExpanded(expanded)
|
n.SetExpanded(expanded)
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,9 @@ func (c *Container) Render(ctx context.Context, ns string, o interface{}) error
|
||||||
}
|
}
|
||||||
pns, _ := client.Namespaced(parent.ID)
|
pns, _ := client.Namespaced(parent.ID)
|
||||||
c.envRefs(f, root, pns, co.Container)
|
c.envRefs(f, root, pns, co.Container)
|
||||||
if !root.IsLeaf() {
|
// if !root.IsLeaf() {
|
||||||
parent.Add(root)
|
parent.Add(root)
|
||||||
}
|
// }
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -235,7 +235,7 @@ func makeDoubleCMKeysContainer(n string, optional bool) *v1.Container {
|
||||||
}
|
}
|
||||||
|
|
||||||
func load(t *testing.T, n string) *unstructured.Unstructured {
|
func load(t *testing.T, n string) *unstructured.Unstructured {
|
||||||
raw, err := ioutil.ReadFile(fmt.Sprintf("test_assets/%s.json", n))
|
raw, err := ioutil.ReadFile(fmt.Sprintf("testdata/%s.json", n))
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
var o unstructured.Unstructured
|
var o unstructured.Unstructured
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,6 @@ func (p *Pod) Render(ctx context.Context, ns string, o interface{}) error {
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("Expecting a TreeNode but got %T", ctx.Value(KeyParent))
|
return fmt.Errorf("Expecting a TreeNode but got %T", ctx.Value(KeyParent))
|
||||||
}
|
}
|
||||||
parent.Add(node)
|
|
||||||
|
|
||||||
if err := p.containerRefs(ctx, node, po.Namespace, po.Spec); err != nil {
|
if err := p.containerRefs(ctx, node, po.Namespace, po.Spec); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -50,6 +49,14 @@ func (p *Pod) Render(ctx context.Context, ns string, o interface{}) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gvr, nsID := "v1/namespaces", client.FQN(client.ClusterScope, po.Namespace)
|
||||||
|
nsn := parent.Find(gvr, nsID)
|
||||||
|
if nsn == nil {
|
||||||
|
nsn = NewTreeNode(gvr, nsID)
|
||||||
|
parent.Add(nsn)
|
||||||
|
}
|
||||||
|
nsn.Add(node)
|
||||||
|
|
||||||
return p.validate(node, po)
|
return p.validate(node, po)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,25 +13,25 @@ import (
|
||||||
func TestPodRender(t *testing.T) {
|
func TestPodRender(t *testing.T) {
|
||||||
uu := map[string]struct {
|
uu := map[string]struct {
|
||||||
file string
|
file string
|
||||||
level1, level2 int
|
count, children int
|
||||||
status string
|
status string
|
||||||
}{
|
}{
|
||||||
"plain": {
|
"plain": {
|
||||||
file: "po",
|
file: "po",
|
||||||
level1: 1,
|
children: 1,
|
||||||
level2: 3,
|
count: 7,
|
||||||
status: xray.OkStatus,
|
status: xray.OkStatus,
|
||||||
},
|
},
|
||||||
"withInit": {
|
"withInit": {
|
||||||
file: "init",
|
file: "init",
|
||||||
level1: 1,
|
children: 1,
|
||||||
level2: 2,
|
count: 7,
|
||||||
status: xray.OkStatus,
|
status: xray.OkStatus,
|
||||||
},
|
},
|
||||||
"cilium": {
|
"cilium": {
|
||||||
file: "cilium",
|
file: "cilium",
|
||||||
level1: 1,
|
children: 1,
|
||||||
level2: 3,
|
count: 8,
|
||||||
status: xray.OkStatus,
|
status: xray.OkStatus,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -46,8 +46,8 @@ func TestPodRender(t *testing.T) {
|
||||||
ctx = context.WithValue(ctx, internal.KeyFactory, makeFactory())
|
ctx = context.WithValue(ctx, internal.KeyFactory, makeFactory())
|
||||||
|
|
||||||
assert.Nil(t, re.Render(ctx, "", &render.PodWithMetrics{Raw: o}))
|
assert.Nil(t, re.Render(ctx, "", &render.PodWithMetrics{Raw: o}))
|
||||||
assert.Equal(t, u.level1, root.CountChildren())
|
assert.Equal(t, u.children, root.CountChildren())
|
||||||
assert.Equal(t, u.level2, root.Children[0].CountChildren())
|
assert.Equal(t, u.count, root.Count(""))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,51 @@ type TreeRef string
|
||||||
|
|
||||||
// NodeSpec represents a node resource specification.
|
// NodeSpec represents a node resource specification.
|
||||||
type NodeSpec struct {
|
type NodeSpec struct {
|
||||||
GVR, Path, Status string
|
GVRs, Paths, Statuses []string
|
||||||
Parent *NodeSpec
|
}
|
||||||
|
|
||||||
|
func (s NodeSpec) ParentGVR() *string {
|
||||||
|
if len(s.GVRs) > 1 {
|
||||||
|
return &s.GVRs[1]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s NodeSpec) ParentPath() *string {
|
||||||
|
if len(s.Paths) > 1 {
|
||||||
|
return &s.Paths[1]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GVR returns the current GVR.
|
||||||
|
func (s NodeSpec) GVR() string {
|
||||||
|
return s.GVRs[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path returns the current path.
|
||||||
|
func (s NodeSpec) Path() string {
|
||||||
|
return s.Paths[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status returns the current status.
|
||||||
|
func (s NodeSpec) Status() string {
|
||||||
|
return s.Statuses[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsPath returns path hierarchy as string.
|
||||||
|
func (s NodeSpec) AsPath() string {
|
||||||
|
return strings.Join(s.Paths, PathSeparator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsGVR returns a gvr hierarchy as string.
|
||||||
|
func (s NodeSpec) AsGVR() string {
|
||||||
|
return strings.Join(s.GVRs, PathSeparator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsStatus returns a status hierarchy as string.
|
||||||
|
func (s NodeSpec) AsStatus() string {
|
||||||
|
return strings.Join(s.Statuses, PathSeparator)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
@ -145,19 +188,17 @@ func (t *TreeNode) Sort() {
|
||||||
|
|
||||||
// Spec returns this node specification.
|
// Spec returns this node specification.
|
||||||
func (t *TreeNode) Spec() NodeSpec {
|
func (t *TreeNode) Spec() NodeSpec {
|
||||||
parent := t
|
var GVRs, Paths, Statuses []string
|
||||||
var gvr, path, status []string
|
for parent := t; parent != nil; parent = parent.Parent {
|
||||||
for parent != nil {
|
GVRs = append(GVRs, parent.GVR)
|
||||||
gvr = append(gvr, parent.GVR)
|
Paths = append(Paths, parent.ID)
|
||||||
path = append(path, parent.ID)
|
Statuses = append(Statuses, parent.Extras[StatusKey])
|
||||||
status = append(status, parent.Extras[StatusKey])
|
|
||||||
parent = parent.Parent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NodeSpec{
|
return NodeSpec{
|
||||||
GVR: strings.Join(gvr, PathSeparator),
|
GVRs: GVRs,
|
||||||
Path: strings.Join(path, PathSeparator),
|
Paths: Paths,
|
||||||
Status: strings.Join(status, PathSeparator),
|
Statuses: Statuses,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,21 +221,18 @@ func (t *TreeNode) Blank() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hydrate hydrates a full tree bases on a collection of specifications.
|
// Hydrate hydrates a full tree bases on a collection of specifications.
|
||||||
func Hydrate(refs []NodeSpec) *TreeNode {
|
func Hydrate(specs []NodeSpec) *TreeNode {
|
||||||
root := NewTreeNode("", "")
|
root := NewTreeNode("", "")
|
||||||
nav := root
|
nav := root
|
||||||
for _, ref := range refs {
|
for _, spec := range specs {
|
||||||
gvrs := strings.Split(ref.GVR, PathSeparator)
|
for i := len(spec.Paths) - 1; i >= 0; i-- {
|
||||||
paths := strings.Split(ref.Path, PathSeparator)
|
|
||||||
statuses := strings.Split(ref.Status, PathSeparator)
|
|
||||||
for i := len(paths) - 1; i >= 0; i-- {
|
|
||||||
if nav.Blank() {
|
if nav.Blank() {
|
||||||
nav.GVR, nav.ID, nav.Extras[StatusKey] = gvrs[i], paths[i], statuses[i]
|
nav.GVR, nav.ID, nav.Extras[StatusKey] = spec.GVRs[i], spec.Paths[i], spec.Statuses[i]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c := NewTreeNode(gvrs[i], paths[i])
|
c := NewTreeNode(spec.GVRs[i], spec.Paths[i])
|
||||||
c.Extras[StatusKey] = statuses[i]
|
c.Extras[StatusKey] = spec.Statuses[i]
|
||||||
if n := nav.Find(gvrs[i], paths[i]); n == nil {
|
if n := nav.Find(spec.GVRs[i], spec.Paths[i]); n == nil {
|
||||||
nav.Add(c)
|
nav.Add(c)
|
||||||
nav = c
|
nav = c
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -260,7 +298,7 @@ func (t *TreeNode) Filter(q string, filter func(q, path string) bool) *TreeNode
|
||||||
specs := t.Flatten()
|
specs := t.Flatten()
|
||||||
matches := make([]NodeSpec, 0, len(specs))
|
matches := make([]NodeSpec, 0, len(specs))
|
||||||
for _, s := range specs {
|
for _, s := range specs {
|
||||||
if filter(q, s.Path+s.Status) {
|
if filter(q, s.AsPath()+s.AsStatus()) {
|
||||||
matches = append(matches, s)
|
matches = append(matches, s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -461,7 +499,7 @@ func toEmoji(gvr string) string {
|
||||||
|
|
||||||
// EmojiInfo returns emoji help.
|
// EmojiInfo returns emoji help.
|
||||||
func EmojiInfo() map[string]string {
|
func EmojiInfo() map[string]string {
|
||||||
gvrs := []string{
|
GVRs := []string{
|
||||||
"containers",
|
"containers",
|
||||||
"v1/namespaces",
|
"v1/namespaces",
|
||||||
"v1/pods",
|
"v1/pods",
|
||||||
|
|
@ -476,8 +514,8 @@ func EmojiInfo() map[string]string {
|
||||||
"apps/v1/daemonsets",
|
"apps/v1/daemonsets",
|
||||||
}
|
}
|
||||||
|
|
||||||
m := make(map[string]string, len(gvrs))
|
m := make(map[string]string, len(GVRs))
|
||||||
for _, g := range gvrs {
|
for _, g := range GVRs {
|
||||||
m[client.NewGVR(g).R()] = toEmoji(g)
|
m[client.NewGVR(g).R()] = toEmoji(g)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,8 +86,8 @@ func TestTreeNodeFilter(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTreeNodeHydrate(t *testing.T) {
|
func TestTreeNodeHydrate(t *testing.T) {
|
||||||
threeOK := strings.Join([]string{"ok", "ok", "ok"}, xray.PathSeparator)
|
threeOK := []string{"ok", "ok", "ok"}
|
||||||
fiveOK := strings.Join([]string{"ok", "ok", "ok", "ok", "ok"}, xray.PathSeparator)
|
fiveOK := append(threeOK, "ok", "ok")
|
||||||
|
|
||||||
uu := map[string]struct {
|
uu := map[string]struct {
|
||||||
spec []xray.NodeSpec
|
spec []xray.NodeSpec
|
||||||
|
|
@ -96,14 +96,14 @@ func TestTreeNodeHydrate(t *testing.T) {
|
||||||
"flat_simple": {
|
"flat_simple": {
|
||||||
spec: []xray.NodeSpec{
|
spec: []xray.NodeSpec{
|
||||||
{
|
{
|
||||||
GVR: "containers::v1/pods",
|
GVRs: []string{"containers", "v1/pods"},
|
||||||
Path: "c1::default/p1",
|
Paths: []string{"c1", "default/p1"},
|
||||||
Status: threeOK,
|
Statuses: threeOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
GVR: "containers::v1/pods",
|
GVRs: []string{"containers", "v1/pods"},
|
||||||
Path: "c2::default/p1",
|
Paths: []string{"c2", "default/p1"},
|
||||||
Status: threeOK,
|
Statuses: threeOK,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
e: root1(),
|
e: root1(),
|
||||||
|
|
@ -111,14 +111,14 @@ func TestTreeNodeHydrate(t *testing.T) {
|
||||||
"flat_complex": {
|
"flat_complex": {
|
||||||
spec: []xray.NodeSpec{
|
spec: []xray.NodeSpec{
|
||||||
{
|
{
|
||||||
GVR: "v1/secrets::containers::v1/pods",
|
GVRs: []string{"v1/secrets", "containers", "v1/pods"},
|
||||||
Path: "s1::c1::default/p1",
|
Paths: []string{"s1", "c1", "default/p1"},
|
||||||
Status: threeOK,
|
Statuses: threeOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
GVR: "v1/secrets::containers::v1/pods",
|
GVRs: []string{"v1/secrets", "containers", "v1/pods"},
|
||||||
Path: "s2::c2::default/p1",
|
Paths: []string{"s2", "c2", "default/p1"},
|
||||||
Status: threeOK,
|
Statuses: threeOK,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
e: root2(),
|
e: root2(),
|
||||||
|
|
@ -126,49 +126,49 @@ func TestTreeNodeHydrate(t *testing.T) {
|
||||||
"complex1": {
|
"complex1": {
|
||||||
spec: []xray.NodeSpec{
|
spec: []xray.NodeSpec{
|
||||||
{
|
{
|
||||||
GVR: "v1/secrets::v1/pods::apps/v1/deployments::v1/namespaces::apps/v1/deployments",
|
GVRs: []string{"v1/secrets", "v1/pods", "apps/v1/deployments", "v1/namespaces", "apps/v1/deployments"},
|
||||||
Path: "default/default-token-rr22g::default/nginx-6b866d578b-c6tcn::default/nginx::-/default::deployments",
|
Paths: []string{"default/default-token-rr22g", "default/nginx-6b866d578b-c6tcn", "default/nginx", "-/default", "deployments"},
|
||||||
Status: fiveOK,
|
Statuses: fiveOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
GVR: "v1/configmaps::v1/pods::apps/v1/deployments::v1/namespaces::apps/v1/deployments",
|
GVRs: []string{"v1/configmaps", "v1/pods", "apps/v1/deployments", "v1/namespaces", "apps/v1/deployments"},
|
||||||
Path: "kube-system/coredns::kube-system/coredns-6955765f44-89q2p::kube-system/coredns::-/kube-system::deployments",
|
Paths: []string{"kube-system/coredns", "kube-system/coredns-6955765f44-89q2p", "kube-system/coredns", "-/kube-system", "deployments"},
|
||||||
Status: fiveOK,
|
Statuses: fiveOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
GVR: "v1/secrets::v1/pods::apps/v1/deployments::v1/namespaces::apps/v1/deployments",
|
GVRs: []string{"v1/secrets", "v1/pods", "apps/v1/deployments", "v1/namespaces", "apps/v1/deployments"},
|
||||||
Path: "kube-system/coredns-token-5cq9j::kube-system/coredns-6955765f44-89q2p::kube-system/coredns::-/kube-system::deployments",
|
Paths: []string{"kube-system/coredns-token-5cq9j", "kube-system/coredns-6955765f44-89q2p", "kube-system/coredns", "-/kube-system", "deployments"},
|
||||||
Status: fiveOK,
|
Statuses: fiveOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
GVR: "v1/configmaps::v1/pods::apps/v1/deployments::v1/namespaces::apps/v1/deployments",
|
GVRs: []string{"v1/configmaps", "v1/pods", "apps/v1/deployments", "v1/namespaces", "apps/v1/deployments"},
|
||||||
Path: "kube-system/coredns::kube-system/coredns-6955765f44-r9j9t::kube-system/coredns::-/kube-system::deployments",
|
Paths: []string{"kube-system/coredns", "kube-system/coredns-6955765f44-r9j9t", "kube-system/coredns", "-/kube-system", "deployments"},
|
||||||
Status: fiveOK,
|
Statuses: fiveOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
GVR: "v1/secrets::v1/pods::apps/v1/deployments::v1/namespaces::apps/v1/deployments",
|
GVRs: []string{"v1/secrets", "v1/pods", "apps/v1/deployments", "v1/namespaces", "apps/v1/deployments"},
|
||||||
Path: "kube-system/coredns-token-5cq9j::kube-system/coredns-6955765f44-r9j9t::kube-system/coredns::-/kube-system::deployments",
|
Paths: []string{"kube-system/coredns-token-5cq9j", "kube-system/coredns-6955765f44-r9j9t", "kube-system/coredns", "-/kube-system", "deployments"},
|
||||||
Status: fiveOK,
|
Statuses: fiveOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
GVR: "v1/secrets::v1/pods::apps/v1/deployments::v1/namespaces::apps/v1/deployments",
|
GVRs: []string{"v1/secrets", "v1/pods", "apps/v1/deployments", "v1/namespaces", "apps/v1/deployments"},
|
||||||
Path: "kube-system/default-token-thzt8::kube-system/metrics-server-6754dbc9df-88bk4::kube-system/metrics-server::-/kube-system::deployments",
|
Paths: []string{"kube-system/default-token-thzt8", "kube-system/metrics-server-6754dbc9df-88bk4", "kube-system/metrics-server", "-/kube-system", "deployments"},
|
||||||
Status: fiveOK,
|
Statuses: fiveOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
GVR: "v1/secrets::v1/pods::apps/v1/deployments::v1/namespaces::apps/v1/deployments",
|
GVRs: []string{"v1/secrets", "v1/pods", "apps/v1/deployments", "v1/namespaces", "apps/v1/deployments"},
|
||||||
Path: "kube-system/nginx-ingress-token-kff5q::kube-system/nginx-ingress-controller-6fc5bcc8c9-cwp55::kube-system/nginx-ingress-controller::-/kube-system::deployments",
|
Paths: []string{"kube-system/nginx-ingress-token-kff5q", "kube-system/nginx-ingress-controller-6fc5bcc8c9-cwp55", "kube-system/nginx-ingress-controller", "-/kube-system", "deployments"},
|
||||||
Status: fiveOK,
|
Statuses: fiveOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
GVR: "v1/secrets::v1/pods::apps/v1/deployments::v1/namespaces::apps/v1/deployments",
|
GVRs: []string{"v1/secrets", "v1/pods", "apps/v1/deployments", "v1/namespaces", "apps/v1/deployments"},
|
||||||
Path: "kubernetes-dashboard/kubernetes-dashboard-token-d6rt4::kubernetes-dashboard/dashboard-metrics-scraper-7b64584c5c-c7b56::kubernetes-dashboard/dashboard-metrics-scraper::-/kubernetes-dashboard::deployments",
|
Paths: []string{"kubernetes-dashboard/kubernetes-dashboard-token-d6rt4", "kubernetes-dashboard/dashboard-metrics-scraper-7b64584c5c-c7b56", "kubernetes-dashboard/dashboard-metrics-scraper", "-/kubernetes-dashboard", "deployments"},
|
||||||
Status: fiveOK,
|
Statuses: fiveOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
GVR: "v1/secrets::v1/pods::apps/v1/deployments::v1/namespaces::apps/v1/deployments",
|
GVRs: []string{"v1/secrets", "v1/pods", "apps/v1/deployments", "v1/namespaces", "apps/v1/deployments"},
|
||||||
Path: "kubernetes-dashboard/kubernetes-dashboard-token-d6rt4::kubernetes-dashboard/kubernetes-dashboard-79d9cd965-b4c7d::kubernetes-dashboard/kubernetes-dashboard::-/kubernetes-dashboard::deployments",
|
Paths: []string{"kubernetes-dashboard/kubernetes-dashboard-token-d6rt4", "kubernetes-dashboard/kubernetes-dashboard-79d9cd965-b4c7d", "kubernetes-dashboard/kubernetes-dashboard", "-/kubernetes-dashboard", "deployments"},
|
||||||
Status: fiveOK,
|
Statuses: fiveOK,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
e: root3(),
|
e: root3(),
|
||||||
|
|
@ -193,14 +193,14 @@ func TestTreeNodeFlatten(t *testing.T) {
|
||||||
root: root1(),
|
root: root1(),
|
||||||
e: []xray.NodeSpec{
|
e: []xray.NodeSpec{
|
||||||
{
|
{
|
||||||
GVR: "containers::v1/pods",
|
GVRs: []string{"containers", "v1/pods"},
|
||||||
Path: "c1::default/p1",
|
Paths: []string{"c1", "default/p1"},
|
||||||
Status: strings.Join([]string{"ok", "ok"}, xray.PathSeparator),
|
Statuses: []string{"ok", "ok"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
GVR: "containers::v1/pods",
|
GVRs: []string{"containers", "v1/pods"},
|
||||||
Path: "c2::default/p1",
|
Paths: []string{"c2", "default/p1"},
|
||||||
Status: strings.Join([]string{"ok", "ok"}, xray.PathSeparator),
|
Statuses: []string{"ok", "ok"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -208,14 +208,14 @@ func TestTreeNodeFlatten(t *testing.T) {
|
||||||
root: root2(),
|
root: root2(),
|
||||||
e: []xray.NodeSpec{
|
e: []xray.NodeSpec{
|
||||||
{
|
{
|
||||||
GVR: "v1/secrets::containers::v1/pods",
|
GVRs: []string{"v1/secrets", "containers", "v1/pods"},
|
||||||
Path: "s1::c1::default/p1",
|
Paths: []string{"s1", "c1", "default/p1"},
|
||||||
Status: strings.Join([]string{"ok", "ok", "ok"}, xray.PathSeparator),
|
Statuses: []string{"ok", "ok", "ok"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
GVR: "v1/secrets::containers::v1/pods",
|
GVRs: []string{"v1/secrets", "containers", "v1/pods"},
|
||||||
Path: "s2::c2::default/p1",
|
Paths: []string{"s2", "c2", "default/p1"},
|
||||||
Status: strings.Join([]string{"ok", "ok", "ok"}, xray.PathSeparator),
|
Statuses: []string{"ok", "ok", "ok"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -323,18 +323,18 @@ func diff1() *xray.TreeNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
func root2() *xray.TreeNode {
|
func root2() *xray.TreeNode {
|
||||||
n := xray.NewTreeNode("v1/pods", "default/p1")
|
|
||||||
c1 := xray.NewTreeNode("containers", "c1")
|
c1 := xray.NewTreeNode("containers", "c1")
|
||||||
c2 := xray.NewTreeNode("containers", "c2")
|
|
||||||
n.Add(c1)
|
|
||||||
n.Add(c2)
|
|
||||||
|
|
||||||
s1 := xray.NewTreeNode("v1/secrets", "s1")
|
s1 := xray.NewTreeNode("v1/secrets", "s1")
|
||||||
c1.Add(s1)
|
c1.Add(s1)
|
||||||
|
|
||||||
|
c2 := xray.NewTreeNode("containers", "c2")
|
||||||
s2 := xray.NewTreeNode("v1/secrets", "s2")
|
s2 := xray.NewTreeNode("v1/secrets", "s2")
|
||||||
c2.Add(s2)
|
c2.Add(s2)
|
||||||
|
|
||||||
|
n := xray.NewTreeNode("v1/pods", "default/p1")
|
||||||
|
n.Add(c1)
|
||||||
|
n.Add(c2)
|
||||||
|
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue