From 635828e4108a6e60388eac68b1b1426ce989fa5b Mon Sep 17 00:00:00 2001 From: lynnemorrison Date: Tue, 10 Dec 2024 14:03:57 -0500 Subject: [PATCH] Add kube debug dump command --- go.mod | 43 +- go.sum | 89 ++-- internal/cmd/skupper/debug/debug.go | 20 +- internal/cmd/skupper/debug/debug_test.go | 49 ++ internal/cmd/skupper/debug/kube/debug.go | 492 +++++++++++++++++- internal/cmd/skupper/debug/kube/debug_test.go | 324 ++++++++++++ internal/kube/client/pods.go | 28 + 7 files changed, 973 insertions(+), 72 deletions(-) diff --git a/go.mod b/go.mod index 176863345..c0b080eb3 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module github.com/skupperproject/skupper -go 1.22.0 +go 1.23.0 -toolchain go1.22.8 +toolchain go1.23.5 require ( github.com/Azure/go-amqp v1.0.5 @@ -28,13 +28,13 @@ require ( golang.org/x/sync v0.10.0 golang.org/x/sys v0.28.0 golang.org/x/text v0.21.0 - golang.org/x/time v0.5.0 - gopkg.in/yaml.v3 v3.0.1 + golang.org/x/time v0.7.0 gotest.tools/v3 v3.5.1 - k8s.io/api v0.31.0 - k8s.io/apimachinery v0.31.0 - k8s.io/client-go v0.31.0 - k8s.io/code-generator v0.31.0 + k8s.io/api v0.32.1 + k8s.io/apiextensions-apiserver v0.32.1 + k8s.io/apimachinery v0.32.1 + k8s.io/client-go v0.32.1 + k8s.io/code-generator v0.32.1 sigs.k8s.io/yaml v1.4.0 ) @@ -59,17 +59,17 @@ require ( github.com/go-logr/logr v1.4.2 // indirect github.com/go-openapi/analysis v0.21.2 // indirect github.com/go-openapi/errors v0.20.3 // indirect - github.com/go-openapi/jsonpointer v0.20.2 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/loads v0.21.1 // indirect github.com/go-openapi/spec v0.20.4 // indirect - github.com/go-openapi/swag v0.22.8 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/go-openapi/validate v0.22.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/imdario/mergo v0.3.8 // indirect + github.com/gorilla/websocket v1.5.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/invopop/yaml v0.2.0 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -78,10 +78,12 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect + github.com/moby/spdystream v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect @@ -92,19 +94,20 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/x448/float16 v0.8.4 // indirect go.mongodb.org/mongo-driver v1.10.0 // indirect - golang.org/x/mod v0.17.0 // indirect + golang.org/x/mod v0.21.0 // indirect golang.org/x/net v0.33.0 // indirect - golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/term v0.27.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect - google.golang.org/protobuf v1.34.2 // indirect + golang.org/x/tools v0.26.0 // indirect + google.golang.org/protobuf v1.35.1 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect - k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect ) diff --git a/go.sum b/go.sum index 078e07a1e..ddec44bfa 100644 --- a/go.sum +++ b/go.sum @@ -52,6 +52,8 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdko github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= @@ -126,8 +128,8 @@ github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDB github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= -github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= @@ -150,8 +152,8 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw= -github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-openapi/validate v0.21.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= github.com/go-openapi/validate v0.22.0 h1:b0QecH6VslW/TxtpKgzpO1SNG7GU2FsaqKdP1E2T50Y= github.com/go-openapi/validate v0.22.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= @@ -191,8 +193,6 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -236,8 +236,8 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -251,6 +251,8 @@ github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyE github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -259,8 +261,6 @@ github.com/heimdalr/dag v1.5.0/go.mod h1:lthekrHl01dddmzqyBQ1YZbi7XcVGGzjFo0jIky github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= @@ -313,6 +313,8 @@ github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= +github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -326,6 +328,7 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oapi-codegen/oapi-codegen/v2 v2.3.0 h1:rICjNsHbPP1LttefanBPnwsSwl09SqhCO7Ee623qR84= @@ -338,12 +341,12 @@ github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= -github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= +github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= +github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= -github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= +github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/openshift/api v0.0.0-20210105115604-44119421ec6b/go.mod h1:aqU5Cq+kqKKPbDMqxo9FojgDeSpNJI7iuskjXjtojDg= github.com/openshift/api v0.0.0-20210428205234-a8389931bee7 h1:kYbp8I2qi3bAyHjTj50Lb1GC2ck7SnZX5M/ZYvF3eLI= github.com/openshift/api v0.0.0-20210428205234-a8389931bee7/go.mod h1:aqU5Cq+kqKKPbDMqxo9FojgDeSpNJI7iuskjXjtojDg= @@ -475,8 +478,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -510,8 +513,8 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -582,8 +585,8 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -623,8 +626,8 @@ golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -679,8 +682,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -716,40 +719,42 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.20.0/go.mod h1:HyLC5l5eoS/ygQYl1BXBgFzWNlkHiAuyNAbevIn+FKg= -k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= -k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE= +k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc= +k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k= +k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw= +k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto= k8s.io/apimachinery v0.20.0/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc= -k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs= +k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= k8s.io/client-go v0.20.0/go.mod h1:4KWh/g+Ocd8KkCwKF8vUNnmqgv+EVnQDK4MBF4oB5tY= -k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8= -k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU= +k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU= +k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg= k8s.io/code-generator v0.20.0/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg= -k8s.io/code-generator v0.31.0 h1:w607nrMi1KeDKB3/F/J4lIoOgAwc+gV9ZKew4XRfMp8= -k8s.io/code-generator v0.31.0/go.mod h1:84y4w3es8rOJOUUP1rLsIiGlO1JuEaPFXQPA9e/K6U0= +k8s.io/code-generator v0.32.1 h1:4lw1kFNDuFYXquTkB7Sl5EwPMUP2yyW9hh6BnFfRZFY= +k8s.io/code-generator v0.32.1/go.mod h1:zaILfm00CVyP/6/pJMJ3zxRepXkxyDfUV5SNG4CjZI4= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 h1:NGrVE502P0s0/1hudf8zjgwki1X/TByhmAoILTarmzo= -k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8= +k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9 h1:si3PfKm8dDYxgfbeA6orqrtLkvvIeH8UqffFJDl0bz4= +k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= diff --git a/internal/cmd/skupper/debug/debug.go b/internal/cmd/skupper/debug/debug.go index 51342d222..22ada941b 100644 --- a/internal/cmd/skupper/debug/debug.go +++ b/internal/cmd/skupper/debug/debug.go @@ -10,28 +10,34 @@ import ( ) func NewCmdDebug() *cobra.Command { + cmd := &cobra.Command{ + Use: "debug", + Short: "debug site details", + Long: "debug site details", + Example: "skupper debug dump ", + } platform := common.Platform(config.GetPlatform()) - cmd := CmdDebugFactory(platform) + cmd.AddCommand(CmdDebugDumpFactory(platform)) return cmd } -func CmdDebugFactory(configuredPlatform common.Platform) *cobra.Command { +func CmdDebugDumpFactory(configuredPlatform common.Platform) *cobra.Command { kubeCommand := kube.NewCmdDebug() nonKubeCommand := nonkube.NewCmdDebug() cmdDebugDesc := common.SkupperCmdDescription{ - Use: "debug", - Short: "", - Long: "", + Use: "dump ", + Short: "Create a tarball containing various files with the site details", + Long: `Create a tarball including site resources and status; component versions, config files, + and logs; and info about the environment where Skupper is running`, + Example: "skupper debug dump ", } cmd := common.ConfigureCobraCommand(configuredPlatform, cmdDebugDesc, kubeCommand, nonKubeCommand) cmdFlags := common.CommandDebugFlags{} - //Add flags if necessary - kubeCommand.CobraCmd = cmd kubeCommand.Flags = &cmdFlags nonKubeCommand.CobraCmd = cmd diff --git a/internal/cmd/skupper/debug/debug_test.go b/internal/cmd/skupper/debug/debug_test.go index 8dc17c684..198cc40a5 100644 --- a/internal/cmd/skupper/debug/debug_test.go +++ b/internal/cmd/skupper/debug/debug_test.go @@ -1 +1,50 @@ package debug + +import ( + "fmt" + "testing" + + "github.com/skupperproject/skupper/internal/cmd/skupper/common" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "gotest.tools/v3/assert" +) + +func TestCmdLinkFactory(t *testing.T) { + + type test struct { + name string + expectedFlagsWithDefaultValue map[string]interface{} + command *cobra.Command + } + + testTable := []test{ + { + name: "CmdDebugDumpFactory", + expectedFlagsWithDefaultValue: map[string]interface{}{}, + command: CmdDebugDumpFactory(common.PlatformKubernetes), + }, + } + + for _, test := range testTable { + + var flagList []string + t.Run(test.name, func(t *testing.T) { + + test.command.Flags().VisitAll(func(flag *pflag.Flag) { + flagList = append(flagList, flag.Name) + assert.Check(t, test.expectedFlagsWithDefaultValue[flag.Name] != nil, fmt.Sprintf("flag %q not expected", flag.Name)) + assert.Check(t, test.expectedFlagsWithDefaultValue[flag.Name] == flag.DefValue, fmt.Sprintf("default value %q for flag %q not expected", flag.DefValue, flag.Name)) + }) + + assert.Check(t, len(flagList) == len(test.expectedFlagsWithDefaultValue)) + + assert.Assert(t, test.command.PreRunE != nil) + assert.Assert(t, test.command.Run != nil) + assert.Assert(t, test.command.PostRun != nil) + assert.Assert(t, test.command.Use != "") + assert.Assert(t, test.command.Short != "") + assert.Assert(t, test.command.Long != "") + }) + } +} diff --git a/internal/cmd/skupper/debug/kube/debug.go b/internal/cmd/skupper/debug/kube/debug.go index 8357302d9..1577bdb7b 100644 --- a/internal/cmd/skupper/debug/kube/debug.go +++ b/internal/cmd/skupper/debug/kube/debug.go @@ -1,13 +1,36 @@ package kube import ( + "archive/tar" + "bytes" + "compress/gzip" + "context" + "errors" "fmt" + "os" + "os/exec" + "path/filepath" + "time" + + "sigs.k8s.io/yaml" "github.com/skupperproject/skupper/internal/cmd/skupper/common" "github.com/skupperproject/skupper/internal/kube/client" + internalclient "github.com/skupperproject/skupper/internal/kube/client" + "github.com/skupperproject/skupper/internal/utils/validator" skupperv2alpha1 "github.com/skupperproject/skupper/pkg/generated/client/clientset/versioned/typed/skupper/v2alpha1" "github.com/spf13/cobra" + v1 "k8s.io/api/core/v1" + crdClient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" + 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/apimachinery/pkg/runtime/serializer/json" "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/scheme" + restclient "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" ) type CmdDebug struct { @@ -16,6 +39,9 @@ type CmdDebug struct { CobraCmd *cobra.Command Flags *common.CommandDebugFlags Namespace string + fileName string + Rest *restclient.Config + crdClient *crdClient.Clientset } func NewCmdDebug() *CmdDebug { @@ -31,13 +57,473 @@ func (cmd *CmdDebug) NewClient(cobraCommand *cobra.Command, args []string) { cmd.Client = cli.GetSkupperClient().SkupperV2alpha1() cmd.KubeClient = cli.GetKubeClient() cmd.Namespace = cli.Namespace + + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + kubeConfigPath := cmd.CobraCmd.Flag("kubeconfig").Value.String() + if kubeConfigPath != "" { + loadingRules = &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeConfigPath} + } + kubeconfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + loadingRules, + &clientcmd.ConfigOverrides{ + CurrentContext: cmd.CobraCmd.Flag("context").Value.String(), + }, + ) + restconfig, err := kubeconfig.ClientConfig() + if err != nil { + return + } + restconfig.ContentConfig.GroupVersion = &schema.GroupVersion{Version: "v1"} + restconfig.APIPath = "/api" + restconfig.NegotiatedSerializer = serializer.WithoutConversionCodecFactory{CodecFactory: scheme.Codecs} + cmd.Rest = restconfig + + cmd.crdClient, err = crdClient.NewForConfig(cmd.Rest) + if err != nil { + return + } + } +} + +func (cmd *CmdDebug) ValidateInput(args []string) error { + var validationErrors []error + fileStringValidator := validator.NewFilePathStringValidator() + + // Validate dump file name + if len(args) < 1 { + cmd.fileName = "skupper-dump" + } else if len(args) > 1 { + validationErrors = append(validationErrors, fmt.Errorf("only one argument is allowed for this command")) + } else if args[0] == "" { + validationErrors = append(validationErrors, fmt.Errorf("filename must not be empty")) + } else { + ok, err := fileStringValidator.Evaluate(args[0]) + if !ok { + validationErrors = append(validationErrors, fmt.Errorf("filename is not valid: %s", err)) + } else { + cmd.fileName = args[0] + } + } + + if cmd.Rest == nil { + validationErrors = append(validationErrors, fmt.Errorf("failed setting up command")) + } + + return errors.Join(validationErrors...) +} + +func (cmd *CmdDebug) InputToOptions() { + datetime := time.Now().Format("20060102150405") + cmd.fileName = fmt.Sprintf("%s-%s-%s", cmd.fileName, cmd.Namespace, datetime) +} + +func (cmd *CmdDebug) Run() error { + configMaps := []string{"skupper-router", "skupper-network-status", "prometheus-server-config"} + routerServices := []string{"skupper", "skupper-router", "skupper-router-local", "network-observer", "network-observer-prometheus", "skupper-grant-server"} //, "skupper-prometheus"} + controllerServices := []string{"skupper-grant-server"} + //routes := []string{"claims", "skupper", "skupper-edge", "skupper-inter-router"} + + dumpFile := cmd.fileName + + // Add extension if not present + if filepath.Ext(dumpFile) == "" { + dumpFile = dumpFile + ".tar.gz" + } + + tarFile, err := os.Create(dumpFile) + if err != nil { + return fmt.Errorf("Unable to save skupper dump details: %w", err) + } + + // compress tar + gz := gzip.NewWriter(tarFile) + defer gz.Close() + tw := tar.NewWriter(gz) + defer tw.Close() + + kv, err := runCommand("kubectl", "version", "-o", "yaml") + if err == nil { + writeTar("/versions/kubernetes.yaml", kv, time.Now(), tw) + writeTar("/versions/kubernetes.yaml.txt", kv, time.Now(), tw) + } + + manifest, err := runCommand("skupper", "version", "-o", "yaml") + if err == nil { + writeTar("/versions/skupper.yaml", manifest, time.Now(), tw) + writeTar("/versions/skupper.yaml.txt", manifest, time.Now(), tw) + } + + // get resources for skupper-router + site, err := cmd.KubeClient.AppsV1().Deployments(cmd.Namespace).Get(context.TODO(), "skupper-router", metav1.GetOptions{}) + if site != nil && err == nil { + path := "/site-namespace/" + + events, err := runCommand("kubectl", "events") + if err == nil { + writeTar(path+"events.txt", events, time.Now(), tw) + } + + endpoints, err := runCommand("kubectl", "get", "endpoints", "-o", "yaml") + if err == nil { + writeTar(path+"endpoints.yaml", endpoints, time.Now(), tw) + writeTar(path+"endpoints.yaml.txt", endpoints, time.Now(), tw) + } + + err = getDeployments(cmd, path, "skupper-router", tw) + if err != nil { + return err + } + + // List all the existing installed CRs in the cluster + path = path + "resources/" + crdList, err := cmd.crdClient.ApiextensionsV1().CustomResourceDefinitions().List(context.TODO(), metav1.ListOptions{}) + if err == nil { + var encodedOutput []byte + var crds []string + for _, crd := range crdList.Items { + crds = append(crds, crd.Name) + } + encodedOutput, err = yaml.Marshal(crds) + if err == nil { + writeTar(path+"crds.txt", encodedOutput, time.Now(), tw) + } + } + + for i := range configMaps { + cm, err := cmd.KubeClient.CoreV1().ConfigMaps(cmd.Namespace).Get(context.TODO(), configMaps[i], metav1.GetOptions{}) + if err == nil { + err := writeObject(cm, path+"Configmap-"+cm.Name, tw) + if err != nil { + return err + } + } + } + + for _, service := range routerServices { + service, err := cmd.KubeClient.CoreV1().Services(cmd.Namespace).Get(context.TODO(), service, metav1.GetOptions{}) + if err == nil { + err := writeObject(service, path+"Services-"+service.Name, tw) + if err != nil { + return err + } + } + } + + accessGrantList, err := cmd.Client.AccessGrants(cmd.Namespace).List(context.TODO(), metav1.ListOptions{}) + if accessGrantList != nil && err == nil { + for _, grant := range accessGrantList.Items { + g := grant.DeepCopy() + err := writeObject(g, path+"Accessgrant-"+g.Name, tw) + if err != nil { + return err + } + } + } + + accessTokenList, err := cmd.Client.AccessTokens(cmd.Namespace).List(context.TODO(), metav1.ListOptions{}) + if accessTokenList != nil && err == nil { + for _, token := range accessTokenList.Items { + t := token.DeepCopy() + err := writeObject(t, path+"AccessTokens-"+t.Name, tw) + if err != nil { + return err + } + } + } + + attachedConnectorBindingList, err := cmd.Client.AttachedConnectorBindings(cmd.Namespace).List(context.TODO(), metav1.ListOptions{}) + if attachedConnectorBindingList != nil && err == nil { + for _, binding := range attachedConnectorBindingList.Items { + b := binding.DeepCopy() + err := writeObject(b, path+"AttachedConnectorBinding-"+b.Name, tw) + if err != nil { + return err + } + } + } + + attachedConnectorList, err := cmd.Client.AttachedConnectors(cmd.Namespace).List(context.TODO(), metav1.ListOptions{}) + if attachedConnectorList != nil && err == nil { + for _, attachedConnector := range attachedConnectorList.Items { + a := attachedConnector.DeepCopy() + err := writeObject(a, path+"AttachedConnector-"+a.Name, tw) + if err != nil { + return err + } + } + } + + certificateList, err := cmd.Client.Certificates(cmd.Namespace).List(context.TODO(), metav1.ListOptions{}) + if certificateList != nil && err == nil { + for _, certificate := range certificateList.Items { + c := certificate.DeepCopy() + err := writeObject(c, path+"Certificate-"+c.Name, tw) + if err != nil { + return err + } + } + } + + connectorList, err := cmd.Client.Connectors(cmd.Namespace).List(context.TODO(), metav1.ListOptions{}) + if connectorList != nil && err == nil { + for _, connector := range connectorList.Items { + c := connector.DeepCopy() + err := writeObject(c, path+"Connector-"+c.Name, tw) + if err != nil { + return err + } + } + } + + linkList, err := cmd.Client.Links(cmd.Namespace).List(context.TODO(), metav1.ListOptions{}) + if linkList != nil && err == nil { + for _, link := range linkList.Items { + l := link.DeepCopy() + err := writeObject(l, path+"Link-"+l.Name, tw) + if err != nil { + return err + } + } + } + + listenerList, err := cmd.Client.Listeners(cmd.Namespace).List(context.TODO(), metav1.ListOptions{}) + if listenerList != nil && err == nil { + for _, listener := range listenerList.Items { + l := listener.DeepCopy() + err := writeObject(l, path+"Listener-"+l.Name, tw) + if err != nil { + return err + } + } + } + + siteList, err := cmd.Client.Sites(cmd.Namespace).List(context.TODO(), metav1.ListOptions{}) + if siteList != nil && err == nil { + for _, site := range siteList.Items { + s := site.DeepCopy() + err := writeObject(s, path+"Site-"+s.Name, tw) + if err != nil { + return err + } + } + } + + routerAccessList, err := cmd.Client.RouterAccesses(cmd.Namespace).List(context.TODO(), metav1.ListOptions{}) + if routerAccessList != nil && err == nil { + for _, site := range routerAccessList.Items { + s := site.DeepCopy() + err := writeObject(s, path+"RouterAccess-"+s.Name, tw) + if err != nil { + return err + } + } + } + + securedAccessList, err := cmd.Client.SecuredAccesses(cmd.Namespace).List(context.TODO(), metav1.ListOptions{}) + if securedAccessList != nil && err == nil { + for _, site := range securedAccessList.Items { + s := site.DeepCopy() + err := writeObject(s, path+"SecuredAccess-"+s.Name, tw) + if err != nil { + return err + } + } + } + } + + // get resources for skupper-controller + controller, err := cmd.KubeClient.AppsV1().Deployments(cmd.Namespace).Get(context.TODO(), "skupper-controller", metav1.GetOptions{}) + if controller != nil && err == nil { + path := "/controller-namespace/" + + events, err := runCommand("kubectl", "events") + if err == nil { + writeTar(path+"events.txt", events, time.Now(), tw) + } + + endpoints, err := runCommand("kubectl", "get", "endpoints", "-o", "yaml") + if err == nil { + writeTar(path+"endpoints.yaml", endpoints, time.Now(), tw) + writeTar(path+"endpoints.yaml.txt", endpoints, time.Now(), tw) + } + + err = getDeployments(cmd, path, "skupper-controller", tw) + if err != nil { + return err + } + + path = path + "resources/" + for i := range controllerServices { + service, err := cmd.KubeClient.CoreV1().Services(cmd.Namespace).Get(context.TODO(), controllerServices[i], metav1.GetOptions{}) + if err == nil { + err := writeObject(service, path+"Services-"+service.Name, tw) + if err != nil { + return err + } + } + } + } + fmt.Println("Skupper dump details written to compressed archive: ", dumpFile) + return nil +} + +func runCommand(name string, args ...string) ([]byte, error) { + cmd := exec.Command(name, args...) + var out bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &out + err := cmd.Run() + if err != nil { + return nil, err + } + return out.Bytes(), nil +} + +// helper functions + +func writeTar(name string, data []byte, ts time.Time, tw *tar.Writer) error { + hdr := &tar.Header{ + Name: name, + Mode: 0600, + Size: int64(len(data)), + ModTime: ts, + } + err := tw.WriteHeader(hdr) + if err != nil { + return fmt.Errorf("Failed to write tar file header: %w", err) + } + _, err = tw.Write(data) + if err != nil { + return fmt.Errorf("Failed to write to tar archive: %w", err) } + return nil } -func (cmd *CmdDebug) ValidateInput(args []string) error { return nil } +func writeObject(rto runtime.Object, name string, tw *tar.Writer) error { + var b bytes.Buffer + s := json.NewYAMLSerializer(json.DefaultMetaFactory, scheme.Scheme, scheme.Scheme) + if err := s.Encode(rto, &b); err != nil { + return err + } + err := writeTar(name+".yaml", b.Bytes(), time.Now(), tw) + if err != nil { + return err + } + err = writeTar(name+".yaml.txt", b.Bytes(), time.Now(), tw) + if err != nil { + return err + } + return nil +} + +func hasRestartedContainer(pod v1.Pod) bool { + for _, containerStatus := range pod.Status.ContainerStatuses { + if containerStatus.RestartCount > 0 { + return true + } + } + return false +} + +func getDeployments(cmd *CmdDebug, path string, deploymentType string, tw *tar.Writer) error { + routerDeployments := []string{"skupper-router", "network-observer", "network-observer-prometheus"} + controllerDeployments := []string{"skupper-controller"} + flags := []string{"-g", "-c", "-l", "-n", "-e", "-a", "-m", "-p"} + + deployments := routerDeployments + labelSelector := "app.kubernetes.io/name=" + if deploymentType == "skupper-controller" { + deployments = controllerDeployments + labelSelector = "application=" + } -func (cmd *CmdDebug) InputToOptions() {} + rPath := path + "resources/" + for i := range deployments { + deployment, err := cmd.KubeClient.AppsV1().Deployments(cmd.Namespace).Get(context.TODO(), deployments[i], metav1.GetOptions{}) + if err != nil { + continue + } -func (cmd *CmdDebug) Run() error { fmt.Println("not yet implemented"); return nil } + err = writeObject(deployment, rPath+"Deployment-"+deployment.Name, tw) + if err != nil { + return err + } + + podList, err := cmd.KubeClient.CoreV1().Pods(cmd.Namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: labelSelector + deployments[i]}) + if err != nil { + continue + } + + for _, pod := range podList.Items { + pod, err := cmd.KubeClient.CoreV1().Pods(cmd.Namespace).Get(context.TODO(), pod.Name, metav1.GetOptions{}) + if err != nil { + continue + } else { + err := writeObject(pod, rPath+"Pod-"+pod.Name, tw) + if err != nil { + return err + } + } + top, err := runCommand("kubectl", "top", "pod", pod.Name) + if err == nil { + writeTar(rPath+pod.Name+"/top-pod.txt", top, time.Now(), tw) + } + + for container := range pod.Spec.Containers { + if pod.Spec.Containers[container].Name == "router" { + // while we are here collect qdstats, logs will show these operations + for x := range flags { + qdr, err := client.ExecCommandInContainer([]string{"skstat", flags[x]}, pod.Name, "router", cmd.Namespace, cmd.KubeClient, cmd.Rest) + if err == nil { + writeTar(rPath+"skstat/"+pod.Name+"-skstat"+flags[x]+".txt", qdr.Bytes(), time.Now(), tw) + } else { + continue + } + } + } + + log, err := internalclient.GetPodContainerLogs(pod.Name, pod.Spec.Containers[container].Name, cmd.Namespace, cmd.KubeClient) + if err == nil { + writeTar(path+"logs/"+pod.Name+"-"+pod.Spec.Containers[container].Name+".txt", []byte(log), time.Now(), tw) + } + + if hasRestartedContainer(*pod) { + prevLog, err := internalclient.GetPodContainerLogsWithOpts(pod.Name, pod.Spec.Containers[container].Name, cmd.Namespace, cmd.KubeClient, v1.PodLogOptions{Previous: true}) + if err == nil { + writeTar(path+"logs/"+pod.Name+"-"+pod.Spec.Containers[container].Name+"-previous.txt", []byte(prevLog), time.Now(), tw) + } + } + } + } + + role, err := cmd.KubeClient.RbacV1().Roles(cmd.Namespace).Get(context.TODO(), deployments[i], metav1.GetOptions{}) + if err == nil && role != nil { + err = writeObject(role, rPath+"Role-"+deployment.Name, tw) + if err != nil { + return err + } + } + + roleBinding, err := cmd.KubeClient.RbacV1().RoleBindings(cmd.Namespace).Get(context.TODO(), deployments[i], metav1.GetOptions{}) + if err == nil && roleBinding != nil { + err = writeObject(roleBinding, rPath+"RoleBinding-"+deployment.Name, tw) + if err != nil { + return err + } + } + + replicaSetList, err := cmd.KubeClient.AppsV1().ReplicaSets(cmd.Namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: labelSelector + deployments[i]}) + if err == nil && replicaSetList != nil { + for _, replicaSet := range replicaSetList.Items { + r := replicaSet.DeepCopy() + err = writeObject(r, rPath+"ReplicaSet-"+replicaSet.Name, tw) + if err != nil { + return err + } + } + } + } + return nil +} func (cmd *CmdDebug) WaitUntil() error { return nil } diff --git a/internal/cmd/skupper/debug/kube/debug_test.go b/internal/cmd/skupper/debug/kube/debug_test.go index f84e1e4e1..56bad0c83 100644 --- a/internal/cmd/skupper/debug/kube/debug_test.go +++ b/internal/cmd/skupper/debug/kube/debug_test.go @@ -1 +1,325 @@ package kube + +import ( + "fmt" + "os" + "testing" + "time" + + "github.com/skupperproject/skupper/internal/cmd/skupper/common" + "github.com/skupperproject/skupper/internal/cmd/skupper/common/testutils" + fakeclient "github.com/skupperproject/skupper/internal/kube/client/fake" + "github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1" + "github.com/spf13/cobra" + "gotest.tools/v3/assert" + appsv1 "k8s.io/api/apps/v1" + v12 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + restclient "k8s.io/client-go/rest" +) + +func TestCmdDebug_ValidateInput(t *testing.T) { + type test struct { + name string + args []string + flags common.CommandDebugFlags + k8sObjects []runtime.Object + skupperObjects []runtime.Object + skupperErrorMessage string + expectedError string + restconfig bool + } + + testTable := []test{ + { + name: "too many args", + flags: common.CommandDebugFlags{}, + args: []string{"test", "not-valid"}, + expectedError: "only one argument is allowed for this command", + restconfig: true, + }, + { + name: "too many args", + flags: common.CommandDebugFlags{}, + args: []string{""}, + expectedError: "filename must not be empty", + restconfig: true, + }, + { + name: "rest empty", + flags: common.CommandDebugFlags{}, + args: []string{"test"}, + expectedError: "failed setting up command", + restconfig: false, + }, + { + name: "ok", + flags: common.CommandDebugFlags{}, + args: []string{"test"}, + restconfig: true, + }, + { + name: "ok default name", + flags: common.CommandDebugFlags{}, + args: []string{}, + restconfig: true, + }, + } + + for _, test := range testTable { + t.Run(test.name, func(t *testing.T) { + var rest restclient.Config + cmd, err := newCmdDebugWithMocks("test", test.k8sObjects, test.skupperObjects, "") + assert.Assert(t, err) + if test.restconfig { + cmd.Rest = &rest + } + cmd.Flags = &test.flags + + testutils.CheckValidateInput(t, cmd, test.expectedError, test.args) + + }) + } +} + +func TestCmdDebug_InputToOptions(t *testing.T) { + type test struct { + name string + namespace string + filename string + args []string + flags common.CommandDebugFlags + k8sObjects []runtime.Object + skupperObjects []runtime.Object + skupperErrorMessage string + expectedName string + } + + testTable := []test{ + { + name: "default name", + namespace: "default", + filename: "skupper-dump", + args: []string{}, + flags: common.CommandDebugFlags{}, + }, + { + name: "name", + namespace: "test", + filename: "dump", + args: []string{}, + flags: common.CommandDebugFlags{}, + }, + } + + for _, test := range testTable { + t.Run(test.name, func(t *testing.T) { + cmd, err := newCmdDebugWithMocks("test", test.k8sObjects, test.skupperObjects, "") + assert.Assert(t, err) + cmd.Flags = &test.flags + cmd.Namespace = test.namespace + cmd.fileName = test.filename + name := fmt.Sprintf("%s-%s-%s", test.filename, cmd.Namespace, time.Now().Format("20060102150405")) + cmd.InputToOptions() + + assert.Check(t, cmd.fileName == name) + }) + } +} + +func TestCmdDebug_Run(t *testing.T) { + type test struct { + name string + DebugName string + flags common.CommandDebugFlags + k8sObjects []runtime.Object + skupperObjects []runtime.Object + skupperErrorMessage string + errorMessage string + } + + testTable := []test{ + { + name: "default", + flags: common.CommandDebugFlags{}, + k8sObjects: []runtime.Object{ + &appsv1.Deployment{ + TypeMeta: v1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "Deployment", + }, + ObjectMeta: v1.ObjectMeta{ + Name: "skupper-controller", + Namespace: "test", + Labels: map[string]string{ + "application": "skupper-controller", + }, + }, + Spec: appsv1.DeploymentSpec{ + Selector: &v1.LabelSelector{ + MatchLabels: map[string]string{ + "application": "skupper-controller", + }, + }, + Template: v12.PodTemplateSpec{ + ObjectMeta: v1.ObjectMeta{ + Name: "skupper-controller", + Namespace: "test", + Labels: map[string]string{ + "application": "skupper-controller", + }, + }, + }, + }, + }, + &v12.Pod{ + TypeMeta: v1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "Pod", + }, + ObjectMeta: v1.ObjectMeta{ + Name: "skupper-controller-cbbd7c69c-9dc55", + Namespace: "test", + Labels: map[string]string{ + "application": "skupper-controller", + }, + }, + Spec: v12.PodSpec{ + Containers: []v12.Container{ + { + Name: "controller", + }, + }, + }, + }, + &v12.Service{ + TypeMeta: v1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "Service", + }, + ObjectMeta: v1.ObjectMeta{ + Name: "skupper-router", + Namespace: "test", + }, + }, + &v12.ConfigMap{ + ObjectMeta: v1.ObjectMeta{ + Name: "skupper-router", + Namespace: "test", + }, + }, + &v12.Secret{ + TypeMeta: v1.TypeMeta{ + APIVersion: "v1", + Kind: "Secret", + }, + }, + }, + skupperObjects: []runtime.Object{ + &v2alpha1.AccessGrant{ + ObjectMeta: v1.ObjectMeta{ + Name: "my-token", + Namespace: "test", + }, + }, + &v2alpha1.AccessToken{ + ObjectMeta: v1.ObjectMeta{ + Name: "my-token", + Namespace: "test", + }, + }, + &v2alpha1.Connector{ + ObjectMeta: v1.ObjectMeta{ + Name: "my-connector", + Namespace: "test", + }, + }, + &v2alpha1.Listener{ + ObjectMeta: v1.ObjectMeta{ + Name: "my-listener", + Namespace: "test", + }, + }, + &v2alpha1.Site{ + ObjectMeta: v1.ObjectMeta{ + Name: "the-site", + Namespace: "test", + }, + }, + &v2alpha1.Certificate{ + ObjectMeta: v1.ObjectMeta{ + Name: "link-test", + Namespace: "test", + }, + }, + &v2alpha1.Link{ + ObjectMeta: v1.ObjectMeta{ + Name: "my-link", + Namespace: "test", + }, + }, + &v2alpha1.AttachedConnectorBinding{ + ObjectMeta: v1.ObjectMeta{ + Name: "my-attachedConnectorBinding", + Namespace: "test", + }, + }, + &v2alpha1.AttachedConnector{ + ObjectMeta: v1.ObjectMeta{ + Name: "my-attachedConnector", + Namespace: "test", + }, + }, + &v2alpha1.RouterAccess{ + ObjectMeta: v1.ObjectMeta{ + Name: "my-routerAccess", + Namespace: "test", + }, + }, + &v2alpha1.SecuredAccess{ + ObjectMeta: v1.ObjectMeta{ + Name: "my-securedAccess", + Namespace: "test", + }, + }, + }, + }, + } + + for _, test := range testTable { + cmd, err := newCmdDebugWithMocks("test", test.k8sObjects, test.skupperObjects, test.skupperErrorMessage) + assert.Assert(t, err) + cmd.Flags = &test.flags + cmd.Namespace = "test" + cmd.fileName = "/tmp/test" + cmd.CobraCmd = &cobra.Command{Use: "test"} + defer os.Remove("/tmp/test.tar.gz") //clean up + t.Run(test.name, func(t *testing.T) { + + err := cmd.Run() + if err != nil { + assert.Check(t, test.errorMessage == err.Error()) + } else { + assert.Check(t, err == nil) + } + }) + } +} + +// --- helper methods + +func newCmdDebugWithMocks(namespace string, k8sObjects []runtime.Object, skupperObjects []runtime.Object, fakeSkupperError string) (*CmdDebug, error) { + + client, err := fakeclient.NewFakeClient(namespace, k8sObjects, skupperObjects, fakeSkupperError) + if err != nil { + return nil, err + } + cmdDebug := &CmdDebug{ + Client: client.GetSkupperClient().SkupperV2alpha1(), + KubeClient: client.GetKubeClient(), + Namespace: namespace, + } + + return cmdDebug, nil +} diff --git a/internal/kube/client/pods.go b/internal/kube/client/pods.go index aa4db8cba..02effb757 100644 --- a/internal/kube/client/pods.go +++ b/internal/kube/client/pods.go @@ -15,7 +15,9 @@ limitations under the License. package client import ( + "bytes" "context" + "io" "time" corev1 "k8s.io/api/core/v1" @@ -70,3 +72,29 @@ func WaitForPodsSelectorStatus(namespace string, clientset kubernetes.Interface, return pods, err } + +func GetPodContainerLogs(podName string, containerName string, namespace string, clientset kubernetes.Interface) (string, error) { + podLogOpts := corev1.PodLogOptions{} + return GetPodContainerLogsWithOpts(podName, containerName, namespace, clientset, podLogOpts) +} + +func GetPodContainerLogsWithOpts(podName string, containerName string, namespace string, clientset kubernetes.Interface, podLogOpts corev1.PodLogOptions) (string, error) { + if containerName != "" { + podLogOpts.Container = containerName + } + req := clientset.CoreV1().Pods(namespace).GetLogs(podName, &podLogOpts) + podLogs, err := req.Stream(context.TODO()) + if err != nil { + return "", err + } + defer podLogs.Close() + + buf := new(bytes.Buffer) + _, err = io.Copy(buf, podLogs) + if err != nil { + return "", err + } + str := buf.String() + + return str, nil +}