diff --git a/go.mod b/go.mod index dc55f92723..1a3f3301a1 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,6 @@ require ( github.com/dexidp/dex v0.0.0-20210802203454-3fac2ab6bc3b github.com/docker/distribution v2.7.1+incompatible github.com/docker/go-units v0.4.0 - github.com/dsnet/compress v0.0.1 // indirect github.com/fatih/color v1.12.0 github.com/frankban/quicktest v1.13.0 // indirect github.com/garyburd/redigo v1.6.0 // indirect @@ -51,20 +50,18 @@ require ( github.com/mattn/go-sqlite3 v1.14.8 github.com/mholt/archiver v3.1.1+incompatible github.com/mitchellh/hashstructure v1.1.0 - github.com/nwaples/rardecode v1.0.0 // indirect github.com/open-policy-agent/opa v0.24.0 github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 github.com/openshift/api v0.0.0-20210513192832-efee9960e6fd // indirect github.com/openshift/client-go v0.0.0-20210503124028-ac0910aac9fa github.com/otiai10/copy v1.0.2 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 - github.com/pierrec/lz4 v2.2.6+incompatible // indirect github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.0 github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/rancher/wrangler v0.8.3 github.com/replicatedhq/kurl v0.0.0-20210414162418-8d6211901244 - github.com/replicatedhq/troubleshoot v0.18.0 + github.com/replicatedhq/troubleshoot v0.19.0 github.com/replicatedhq/yaml/v3 v3.0.0-beta5-replicatedhq github.com/robfig/cron v1.2.0 github.com/robfig/cron/v3 v3.0.1 @@ -77,7 +74,6 @@ require ( github.com/stretchr/testify v1.7.0 github.com/tj/go-spin v1.1.0 github.com/vmware-tanzu/velero v1.5.4 - github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940 // indirect github.com/yvasiyarov/gorelic v0.0.7 // indirect github.com/yvasiyarov/newrelic_platform_go v0.0.0-20160601141957-9c099fbc30e9 // indirect; indirect= diff --git a/go.sum b/go.sum index c97f17e2be..2e7937e918 100644 --- a/go.sum +++ b/go.sum @@ -820,8 +820,9 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0= @@ -1416,8 +1417,8 @@ github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJE github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= -github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs= -github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= +github.com/nwaples/rardecode v1.1.2 h1:Cj0yZY6T1Zx1R7AhTbyGSALm44/Mmq+BAPc4B/p/d3M= +github.com/nwaples/rardecode v1.1.2/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -1543,8 +1544,8 @@ github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoU github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4 v2.2.6+incompatible h1:6aCX4/YZ9v8q69hTyiR7dNLnTA3fgtKHVVW5BCd5Znw= -github.com/pierrec/lz4 v2.2.6+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= +github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.0.0-20181023235946-059132a15dd0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -1631,8 +1632,8 @@ github.com/replicatedhq/kurl v0.0.0-20210414162418-8d6211901244 h1:aSORttMXeqRXg github.com/replicatedhq/kurl v0.0.0-20210414162418-8d6211901244/go.mod h1:SoROyLOtkt95R1COPVzdCi5FZbMmATPHohQQzW9V9ds= github.com/replicatedhq/termui/v3 v3.1.1-0.20200811145416-f40076d26851/go.mod h1:JDxG6+uubnk9/BZ2yUsyAJJwlptjrnmB2MPF5d2Xe/8= github.com/replicatedhq/troubleshoot v0.10.18/go.mod h1:8oFRnMJlFjzJ490eq72iLEN7DGJjkgLx22Z1vX6WwU0= -github.com/replicatedhq/troubleshoot v0.18.0 h1:Gug+67PrIk6BLkjsT4AW+U6S/oMuqL/C0OMz1tzbu5Q= -github.com/replicatedhq/troubleshoot v0.18.0/go.mod h1:RbTswOcNaUURaDWdUYvKj8SmuJEh+dYWlaEalgC4tUU= +github.com/replicatedhq/troubleshoot v0.19.0 h1:9aIIr1Nnqf7UAyNjjkq3/YbQJKgK4sJbxZEjJ8xnNzc= +github.com/replicatedhq/troubleshoot v0.19.0/go.mod h1:aWhykK6xiTpfbLMjPpqnonih34avLwEag52Y9CYzb/Y= github.com/replicatedhq/yaml/v3 v3.0.0-beta5-replicatedhq h1:PwPggruelq2336c1Ayg5STFqgbn/QB1tWLQwrVlU7ZQ= github.com/replicatedhq/yaml/v3 v3.0.0-beta5-replicatedhq/go.mod h1:Txa7LopbYCU8aRgmNe0n+y/EPMz50NbCPcVVJBquwag= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= diff --git a/pkg/handlers/handlers.go b/pkg/handlers/handlers.go index 8b67234902..838d9a1985 100644 --- a/pkg/handlers/handlers.go +++ b/pkg/handlers/handlers.go @@ -57,6 +57,8 @@ func RegisterSessionAuthRoutes(r *mux.Router, kotsStore store.Store, handler KOT HandlerFunc(middleware.EnforceAccess(policy.AppSupportbundleWrite, handler.CollectSupportBundle)) r.Name("ShareSupportBundle").Path("/api/v1/troubleshoot/app/{appSlug}/supportbundle/{bundleId}/share").Methods("POST"). HandlerFunc(middleware.EnforceAccess(policy.AppSupportbundleWrite, handler.ShareSupportBundle)) + r.Name("GetPodDetailsFromSupportBundle").Path("/api/v1/troubleshoot/app/{appSlug}/supportbundle/{bundleId}/pod").Methods("GET"). + HandlerFunc(middleware.EnforceAccess(policy.AppSupportbundleRead, handler.GetPodDetailsFromSupportBundle)) // redactor routes r.Name("UpdateRedact").Path("/api/v1/redact/set").Methods("PUT"). diff --git a/pkg/handlers/handlers_test.go b/pkg/handlers/handlers_test.go index 1430f4e0e6..052171bdcd 100644 --- a/pkg/handlers/handlers_test.go +++ b/pkg/handlers/handlers_test.go @@ -165,6 +165,17 @@ var HandlerPolicyTests = map[string][]HandlerPolicyTest{ ExpectStatus: http.StatusOK, }, }, + "GetPodDetailsFromSupportBundle": { + { + Vars: map[string]string{"appSlug": "my-app", "bundleId": "234"}, + Roles: []rbactypes.Role{rbac.ClusterAdminRole}, + SessionRoles: []string{rbac.ClusterAdminRoleID}, + Calls: func(storeRecorder *mock_store.MockStoreMockRecorder, handlerRecorder *mock_handlers.MockKOTSHandlerMockRecorder) { + handlerRecorder.GetPodDetailsFromSupportBundle(gomock.Any(), gomock.Any()) + }, + ExpectStatus: http.StatusOK, + }, + }, // redactor routes "UpdateRedact": { diff --git a/pkg/handlers/interface.go b/pkg/handlers/interface.go index 031a106294..74debaaf96 100644 --- a/pkg/handlers/interface.go +++ b/pkg/handlers/interface.go @@ -19,6 +19,7 @@ type KOTSHandler interface { DownloadSupportBundle(w http.ResponseWriter, r *http.Request) // TODO: appSlug CollectSupportBundle(w http.ResponseWriter, r *http.Request) ShareSupportBundle(w http.ResponseWriter, r *http.Request) + GetPodDetailsFromSupportBundle(w http.ResponseWriter, r *http.Request) // redactor routes UpdateRedact(w http.ResponseWriter, r *http.Request) diff --git a/pkg/handlers/mock/mock.go b/pkg/handlers/mock/mock.go index 93316b5ab6..42bb3e4a64 100644 --- a/pkg/handlers/mock/mock.go +++ b/pkg/handlers/mock/mock.go @@ -658,6 +658,18 @@ func (mr *MockKOTSHandlerMockRecorder) GetPendingApp(w, r interface{}) *gomock.C return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPendingApp", reflect.TypeOf((*MockKOTSHandler)(nil).GetPendingApp), w, r) } +// GetPodDetailsFromSupportBundle mocks base method. +func (m *MockKOTSHandler) GetPodDetailsFromSupportBundle(w http.ResponseWriter, r *http.Request) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "GetPodDetailsFromSupportBundle", w, r) +} + +// GetPodDetailsFromSupportBundle indicates an expected call of GetPodDetailsFromSupportBundle. +func (mr *MockKOTSHandlerMockRecorder) GetPodDetailsFromSupportBundle(w, r interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPodDetailsFromSupportBundle", reflect.TypeOf((*MockKOTSHandler)(nil).GetPodDetailsFromSupportBundle), w, r) +} + // GetPreflightCommand mocks base method. func (m *MockKOTSHandler) GetPreflightCommand(w http.ResponseWriter, r *http.Request) { m.ctrl.T.Helper() diff --git a/pkg/handlers/troubleshoot.go b/pkg/handlers/troubleshoot.go index d4df43af6b..2c9d558e35 100644 --- a/pkg/handlers/troubleshoot.go +++ b/pkg/handlers/troubleshoot.go @@ -18,6 +18,8 @@ import ( "github.com/replicatedhq/kots/pkg/supportbundle/types" "github.com/replicatedhq/kots/pkg/util" redact2 "github.com/replicatedhq/troubleshoot/pkg/redact" + tsupportbundle "github.com/replicatedhq/troubleshoot/pkg/supportbundle" + tsupportbundletypes "github.com/replicatedhq/troubleshoot/pkg/supportbundle/types" ) type GetSupportBundleResponse struct { @@ -81,6 +83,19 @@ type GetSupportBundleRedactionsResponse struct { Error string `json:"error,omitempty"` } +type GetPodDetailsFromSupportBundleResponse struct { + tsupportbundletypes.PodDetails `json:",inline"` + + Success bool `json:"success"` + Error string `json:"error,omitempty"` +} + +type PodContainer struct { + Name string `json:"name"` + LogsFilePath string `json:"logsFilePath"` + IsInitContainer bool `json:"isInitContainer"` +} + type PutSupportBundleRedactions struct { Redactions redact2.RedactionList `json:"redactions"` } @@ -128,11 +143,20 @@ func (h *Handler) GetSupportBundleFiles(w http.ResponseWriter, r *http.Request) bundleID := mux.Vars(r)["bundleId"] filenames := r.URL.Query()["filename"] - files, err := supportbundle.GetFilesContents(bundleID, filenames) + bundleArchive, err := store.GetStore().GetSupportBundleArchive(bundleID) if err != nil { - logger.Error(err) + logger.Error(errors.Wrap(err, "failed to get support bundle archive")) + getSupportBundleFilesResponse.Error = "failed to get support bundle archive" + JSON(w, http.StatusInternalServerError, nil) + return + } + defer os.RemoveAll(bundleArchive) + + files, err := tsupportbundle.GetFilesContents(bundleArchive, filenames) + if err != nil { + logger.Error(errors.Wrap(err, "failed to get files")) getSupportBundleFilesResponse.Error = "failed to get files" - JSON(w, 500, getSupportBundleFilesResponse) + JSON(w, http.StatusInternalServerError, getSupportBundleFilesResponse) return } @@ -482,6 +506,37 @@ func (h *Handler) GetSupportBundleRedactions(w http.ResponseWriter, r *http.Requ JSON(w, http.StatusOK, getSupportBundleRedactionsResponse) } +func (h *Handler) GetPodDetailsFromSupportBundle(w http.ResponseWriter, r *http.Request) { + getPodDetailsFromSupportBundleResponse := GetPodDetailsFromSupportBundleResponse{ + Success: false, + } + + bundleID := mux.Vars(r)["bundleId"] + podName := r.URL.Query().Get("podName") + podNamespace := r.URL.Query().Get("podNamespace") + + bundleArchive, err := store.GetStore().GetSupportBundleArchive(bundleID) + if err != nil { + logger.Error(errors.Wrap(err, "failed to get support bundle archive")) + JSON(w, http.StatusInternalServerError, nil) + return + } + defer os.RemoveAll(bundleArchive) + + podDetails, err := tsupportbundle.GetPodDetails(bundleArchive, podNamespace, podName) + if err != nil { + logger.Error(errors.Wrap(err, "failed to get pod details")) + getPodDetailsFromSupportBundleResponse.Error = "failed to get pod details" + JSON(w, http.StatusInternalServerError, getPodDetailsFromSupportBundleResponse) + return + } + + getPodDetailsFromSupportBundleResponse.PodDetails = *podDetails + getPodDetailsFromSupportBundleResponse.Success = true + + JSON(w, http.StatusOK, getPodDetailsFromSupportBundleResponse) +} + // SetSupportBundleRedactions route is UNAUTHENTICATED // This request comes from the `kubectl support-bundle` command. func (h *Handler) SetSupportBundleRedactions(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/store/kotsstore/supportbundle_store.go b/pkg/store/kotsstore/supportbundle_store.go index 2b0c9591ef..cf03b02d4e 100644 --- a/pkg/store/kotsstore/supportbundle_store.go +++ b/pkg/store/kotsstore/supportbundle_store.go @@ -587,10 +587,11 @@ func insightsFromResults(results []byte) ([]types.SupportBundleInsight, error) { DesiredPosition string `json:"desiredPosition"` } type DBInsight struct { - Name string `json:"name"` - Severity string `json:"severity"` - Insight Insight `json:"insight"` - Labels Labels `json:"labels"` + Name string `json:"name"` + Severity string `json:"severity"` + Insight Insight `json:"insight"` + Labels Labels `json:"labels"` + InvolvedObject *corev1.ObjectReference `json:"involvedObject,omitempty"` } dbInsights := []DBInsight{} @@ -610,6 +611,7 @@ func insightsFromResults(results []byte) ([]types.SupportBundleInsight, error) { Icon: dbInsight.Labels.IconUri, IconKey: dbInsight.Labels.IconKey, DesiredPosition: desiredPosition, + InvolvedObject: dbInsight.InvolvedObject, } insights = append(insights, insight) } diff --git a/pkg/supportbundle/supportbundle.go b/pkg/supportbundle/supportbundle.go index 92838d3911..c2cb1f6ef0 100644 --- a/pkg/supportbundle/supportbundle.go +++ b/pkg/supportbundle/supportbundle.go @@ -85,86 +85,6 @@ func CreateBundle(requestedID string, appID string, archivePath string) (*types. return store.GetStore().CreateSupportBundle(id, appID, archivePath, marshalledTree) } -// GetFilesContents will return the file contents for filenames matching the filenames -// parameter. -func GetFilesContents(bundleID string, filenames []string) (map[string][]byte, error) { - bundleArchive, err := store.GetStore().GetSupportBundleArchive(bundleID) - if err != nil { - return nil, errors.Wrap(err, "failed to get bundle") - } - defer os.RemoveAll(bundleArchive) - - bundleDir, err := ioutil.TempDir("", "kots") - if err != nil { - return nil, errors.Wrap(err, "failed to create tmp dir") - } - defer os.RemoveAll(bundleDir) - - tarGz := archiver.TarGz{ - Tar: &archiver.Tar{ - ImplicitTopLevelFolder: false, - }, - } - if err := tarGz.Unarchive(bundleArchive, bundleDir); err != nil { - return nil, errors.Wrap(err, "failed to unarchive") - } - - files := map[string][]byte{} - err = filepath.Walk(bundleDir, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - - if len(path) <= len(bundleDir) { - return nil - } - - if info.IsDir() { - return nil - } - - // the following tries to find the actual file path of the desired files in the support bundle - // this is needed to handle old and new support bundle formats - // where old support bundles don't include a top level subdirectory and the new ones do - // this basically compares file paths after trimming the subdirectory path from both (if exists) - // for example: "support-bundle-2021-09-10T18_50_35/support-bundle-2021-09-10T18_50_35/path/to/file" - relPath, err := filepath.Rel(bundleDir, path) // becomes: "support-bundle-2021-09-10T18_50_35/path/to/file" - if err != nil { - return errors.Wrap(err, "failed to get relative path") - } - - trimmedRelPath := SupportBundleNameRegex.ReplaceAllString(relPath, "") // becomes: "path/to/file" - trimmedRelPath = strings.TrimPrefix(trimmedRelPath, string(os.PathSeparator)) // extra measure to ensure no leading slashes. for example: "/path/to/file" - if trimmedRelPath == "" { - return nil - } - - for _, filename := range filenames { - trimmedFileName := SupportBundleNameRegex.ReplaceAllString(filename, "") - trimmedFileName = strings.TrimPrefix(trimmedFileName, string(os.PathSeparator)) - if trimmedFileName == "" { - continue - } - if trimmedRelPath == trimmedFileName { - content, err := ioutil.ReadFile(path) - if err != nil { - return errors.Wrap(err, "failed to read file") - } - - files[filename] = content - return nil - } - } - - return nil - }) - if err != nil { - return nil, errors.Wrap(err, "failed to walk") - } - - return files, nil -} - func GetSpecSecretName(appSlug string) string { return fmt.Sprintf("kotsadm-%s-supportbundle", appSlug) } diff --git a/pkg/supportbundle/types/types.go b/pkg/supportbundle/types/types.go index e5bf671bdd..0e37e9d964 100644 --- a/pkg/supportbundle/types/types.go +++ b/pkg/supportbundle/types/types.go @@ -2,6 +2,8 @@ package types import ( "time" + + corev1 "k8s.io/api/core/v1" ) type ByCreated []*SupportBundle @@ -49,13 +51,14 @@ type SupportBundleAnalysis struct { } type SupportBundleInsight struct { - Key string `json:"key"` - Severity string `json:"severity"` - Primary string `json:"primary"` - Detail string `json:"detail"` - Icon string `json:"icon"` - IconKey string `json:"iconKey"` - DesiredPosition float64 `json:"desiredPosition"` + Key string `json:"key"` + Severity string `json:"severity"` + Primary string `json:"primary"` + Detail string `json:"detail"` + Icon string `json:"icon"` + IconKey string `json:"iconKey"` + DesiredPosition float64 `json:"desiredPosition"` + InvolvedObject *corev1.ObjectReference `json:"involvedObject"` } type FileTree struct { diff --git a/web/src/components/troubleshoot/AnalyzerInsights.jsx b/web/src/components/troubleshoot/AnalyzerInsights.jsx index 36ce54c373..f6646dfd66 100644 --- a/web/src/components/troubleshoot/AnalyzerInsights.jsx +++ b/web/src/components/troubleshoot/AnalyzerInsights.jsx @@ -3,7 +3,7 @@ import Loader from "../shared/Loader"; import isEmpty from "lodash/isEmpty"; import filter from "lodash/filter"; import MarkdownRenderer from "@src/components/shared/MarkdownRenderer"; -import { sortAnalyzers, parseIconUri } from "../../utilities/utilities"; +import { sortAnalyzers, parseIconUri, Utilities } from "../../utilities/utilities"; export class AnalyzerInsights extends React.Component { constructor(props) { @@ -35,6 +35,7 @@ export class AnalyzerInsights extends React.Component { } componentDidMount() { + this.testApi(); let isError, isWarn; if (this.props.insights) { isError = this.props.insights.some(i => i.severity === "error"); @@ -51,6 +52,22 @@ export class AnalyzerInsights extends React.Component { this.checkBundleStatus(); } + testApi = async () => { + this.setState({ sendingBundle: true, sendingBundleErrMsg: "", downloadBundleErrMsg: "" }); + fetch(`${window.env.API_ENDPOINT}/troubleshoot/app/qakots/supportbundle/2041y5f3xzi5ewauoaiqogyccme/pod?podNamespace=default&podName=sqs-7449b544fc-mw4dx`, { + method: "GET", + headers: { + "Authorization": Utilities.getToken(), + } + }) + .then(async (result) => { + console.log(result) + }) + .catch(err => { + console.log(err); + }) + } + checkBundleStatus = () => { const { status, refetchSupportBundle, insights } = this.props; if (status === "uploaded" || status === "analyzing") { @@ -159,6 +176,7 @@ export class AnalyzerInsights extends React.Component { {tile.detail} + {tile?.involvedObject?.kind === "Pod" &&
this.props.openPodDetailsModal(tile?.involvedObject)}>See details
} ) diff --git a/web/src/components/troubleshoot/PodAnalyzerDetails.jsx b/web/src/components/troubleshoot/PodAnalyzerDetails.jsx new file mode 100644 index 0000000000..5bee13c304 --- /dev/null +++ b/web/src/components/troubleshoot/PodAnalyzerDetails.jsx @@ -0,0 +1,218 @@ +import * as React from "react"; +import { withRouter } from "react-router-dom"; +import AceEditor from "react-ace"; +import Select from "react-select"; +import Loader from "../shared/Loader"; +import yaml from "js-yaml"; +import { Utilities } from "../../utilities/utilities"; +import "brace/ext/searchbox"; + +export class PodAnalyzerDetails extends React.Component { + + state = { + activeTab: "podLogs", + podContainers: [], + podEvents: "", + podDefinition: "", + selectedContainer: {}, + selectedContainerLogs: "", + loading: false, + errMsg: "", + } + + togglePodDetailView = (active) => { + this.setState({ activeTab: active }); + } + + componentDidMount() { + this.getPodDetails(); + } + + componentDidUpdate(_, lastState) { + if (this.state.activeTab !== lastState.activeTab && this.aceEditor) { + this.aceEditor.editor.resize(true); + } + } + + getPodDetails = async () => { + const { pod } = this.props; + this.setState({ loading: true, errMsg: "" }); + + fetch(`${window.env.API_ENDPOINT}/troubleshoot/app/qakots/supportbundle/${this.props.bundleId}/pod?podNamespace=${pod.namespace}&podName=${pod.name}`, { + method: "GET", + headers: { + "Authorization": Utilities.getToken(), + } + }) + .then(async (result) => { + const data = await result.json(); + + let selectedContainer = {}; + let groupedPodOptions = []; + const initContainers = data.podContainers.filter(c => c.isInitContainer); + const regContainers = data.podContainers.filter(c => !c.isInitContainer); + if (initContainers.length > 0) { + groupedPodOptions.push({ + label: "Init containers", + options: initContainers + }); + } + if (regContainers.length > 0) { + groupedPodOptions.push({ + label: "Containers", + options: regContainers + }) + } + selectedContainer = groupedPodOptions[0].options[0]; + this.onSelectedContainerChange(selectedContainer); + this.setState({ loading: false, podContainers: groupedPodOptions, podDefinition: yaml.dump(data.podDefinition), podEvents: yaml.dump(data.podEvents) }); + }) + .catch(err => { + this.setState({ + loading: false, + errMsg: err, + }); + }); + } + + onSelectedContainerChange = (selectedContainer) => { + this.setState({ selectedContainer: selectedContainer }); + + this.setState({ + loading: true, + errMsg: "", + }); + + fetch(`${window.env.API_ENDPOINT}/troubleshoot/supportbundle/${this.props.bundleId}/files?filename=${encodeURIComponent(selectedContainer.logsFilePath)}`, { + method: "GET", + headers: { + "Authorization": Utilities.getToken(), + "Content-Type": "application/json", + }, + }) + .then(async (result) => { + const data = await result.json(); + const decodedLogs = Buffer.from(data.files[selectedContainer.logsFilePath], 'base64').toString(); + this.setState({ loading: false, selectedContainerLogs: decodedLogs }); + }) + .catch(err => { + this.setState({ + loading: false, + errMsg: err, + }); + }); + } + + renderPodDetailView = () => { + switch (this.state.activeTab) { + case "podDefinition": + return ( +
+ {this.renderEditor("definition", this.state.podDefinition, "yaml", "Definition not found")} +
+ ) + case "podLogs": + return ( +
+ {this.state.podContainers && this.state.podContainers.length > 1 ? +
+

Which container logs would you like to view?

+
+