This repository has been archived by the owner on Jul 11, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 276
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
bug-report: implement bug report tool
Implements a tool to generate a bug report archive. The tool collects info about app namespaces, pods, events etc. Signed-off-by: Shashank Ram <[email protected]>
- Loading branch information
1 parent
adfa0b9
commit ea27b73
Showing
13 changed files
with
585 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/spf13/cobra" | ||
"helm.sh/helm/v3/pkg/action" | ||
"k8s.io/apimachinery/pkg/types" | ||
"k8s.io/client-go/kubernetes" | ||
|
||
"github.com/openservicemesh/osm/pkg/bugreport" | ||
"github.com/openservicemesh/osm/pkg/k8s" | ||
) | ||
|
||
const bugReportDescription = ` | ||
Generate a bug report. | ||
*Note: | ||
- Both 'osm' and 'kubectl' CLI must reside in the evironment's lookup path. | ||
- If the environment includes sensitive information that should not be collected, | ||
please do not specify the associated resources. | ||
` | ||
|
||
const bugReportExample = ` | ||
# Generate a bug report for the given namespaces, deployments, and pods | ||
osm support bug-report --app-namespaces bookbuyer,bookstore \ | ||
--app-deployments bookbuyer/bookbuyer,bookstore/bookstore-v1 \ | ||
--app-pods bookthief/bookthief-7bb7f9b98c-qplq4 | ||
` | ||
|
||
type bugReportCmd struct { | ||
stdout io.Writer | ||
stderr io.Writer | ||
kubeClient kubernetes.Interface | ||
appNamespaces []string | ||
appDeployments []string | ||
appPods []string | ||
outFile string | ||
} | ||
|
||
func newSupportBugReportCmd(config *action.Configuration, stdout io.Writer, stderr io.Writer) *cobra.Command { | ||
bugReportCmd := &bugReportCmd{ | ||
stdout: stdout, | ||
stderr: stderr, | ||
} | ||
|
||
cmd := &cobra.Command{ | ||
Use: "bug-report", | ||
Short: "generate bug report", | ||
Long: bugReportDescription, | ||
Args: cobra.MaximumNArgs(1), | ||
RunE: func(_ *cobra.Command, args []string) error { | ||
config, err := settings.RESTClientGetter().ToRESTConfig() | ||
if err != nil { | ||
return errors.Errorf("Error fetching kubeconfig: %s", err) | ||
} | ||
bugReportCmd.kubeClient, err = kubernetes.NewForConfig(config) | ||
if err != nil { | ||
return errors.Errorf("Could not access Kubernetes cluster, check kubeconfig: %s", err) | ||
} | ||
return bugReportCmd.run() | ||
}, | ||
Example: bugReportExample, | ||
} | ||
|
||
f := cmd.Flags() | ||
f.StringSliceVar(&bugReportCmd.appNamespaces, "app-namespaces", nil, "Application namespaces") | ||
f.StringSliceVar(&bugReportCmd.appDeployments, "app-deployments", nil, "Application deployments: <namespace>/<deployment>") | ||
f.StringSliceVar(&bugReportCmd.appPods, "app-pods", nil, "Application pods: <namespace>/pod") | ||
f.StringVarP(&bugReportCmd.outFile, "out-file", "o", "", "Output file path") | ||
|
||
return cmd | ||
} | ||
|
||
func (cmd *bugReportCmd) run() error { | ||
var appPods, appDeployments []types.NamespacedName | ||
|
||
for _, pod := range cmd.appPods { | ||
p, err := k8s.NamespacedNameFrom(pod) | ||
if err != nil { | ||
fmt.Fprintf(cmd.stderr, "Pod name %s is not namespaced, skipping it", pod) | ||
continue | ||
} | ||
appPods = append(appPods, p) | ||
} | ||
|
||
for _, deployment := range cmd.appDeployments { | ||
d, err := k8s.NamespacedNameFrom(deployment) | ||
if err != nil { | ||
fmt.Fprintf(cmd.stderr, "Deployment name %s is not namespaced, skipping it", deployment) | ||
continue | ||
} | ||
appDeployments = append(appDeployments, d) | ||
} | ||
|
||
bugReportCfg := &bugreport.Config{ | ||
Stdout: cmd.stdout, | ||
Stderr: cmd.stderr, | ||
KubeClient: cmd.kubeClient, | ||
ControlPlaneNamepace: settings.Namespace(), | ||
AppNamespaces: cmd.appNamespaces, | ||
AppDeployments: appDeployments, | ||
AppPods: appPods, | ||
OutFile: cmd.outFile, | ||
} | ||
|
||
return bugReportCfg.Run() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package bugreport | ||
|
||
import ( | ||
"compress/flate" | ||
|
||
"github.com/mholt/archiver/v3" | ||
"github.com/pkg/errors" | ||
) | ||
|
||
func (c *Config) archive(sourcePath string, destinationPath string) error { | ||
z := archiver.Zip{ | ||
CompressionLevel: flate.DefaultCompression, | ||
MkdirAll: true, | ||
SelectiveCompression: true, | ||
ContinueOnError: true, | ||
OverwriteExisting: true, | ||
ImplicitTopLevelFolder: false, | ||
} | ||
if err := z.Archive([]string{sourcePath}, destinationPath); err != nil { | ||
c.completionFailure("Error archiving files for bug report") | ||
return errors.Wrap(err, "Error generating bug report") | ||
} | ||
|
||
c.completionSuccess("Bug report successfully archived to %s", destinationPath) | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package bugreport | ||
|
||
import ( | ||
"os" | ||
"path" | ||
"strings" | ||
|
||
"github.com/pkg/errors" | ||
|
||
"github.com/openservicemesh/osm/pkg/constants" | ||
) | ||
|
||
const ( | ||
rootNamespaceDirName = "namespaces" | ||
) | ||
|
||
var commonNamespaceCmds = [][]string{ | ||
{"osm", "namespace", "list"}, | ||
} | ||
|
||
func (c *Config) initRootNamespaceDir() error { | ||
rootNsDir := c.rootNamespaceDirPath() | ||
if err := os.Mkdir(rootNsDir, 0700); err != nil { | ||
return errors.Wrapf(err, "Error creating root dir %s for namespaces", rootNsDir) | ||
} | ||
return nil | ||
} | ||
|
||
func (c *Config) collectMeshedNamespaceReport() { | ||
for _, nsCmd := range commonNamespaceCmds { | ||
outPath := path.Join(c.rootNamespaceDirPath(), commandsDirName, strings.Join(nsCmd, "_")) | ||
if err := runCmdAndWriteToFile(nsCmd, outPath); err != nil { | ||
c.completionFailure("Error running command: %v", nsCmd) | ||
} | ||
} | ||
} | ||
|
||
func (c *Config) collectPerNamespaceReport() { | ||
for _, ns := range c.AppNamespaces { | ||
for _, nsCmd := range getPerNamespaceCommands(ns) { | ||
outPath := path.Join(c.rootNamespaceDirPath(), ns, commandsDirName, strings.Join(nsCmd, "_")) | ||
if err := runCmdAndWriteToFile(nsCmd, outPath); err != nil { | ||
c.completionFailure("Error running cmd: %v", nsCmd) | ||
} | ||
} | ||
c.completionSuccess("Collected report from Namespace %q", ns) | ||
} | ||
} | ||
|
||
func (c *Config) rootNamespaceDirPath() string { | ||
return path.Join(c.stagingDir, rootNamespaceDirName) | ||
} | ||
|
||
func getPerNamespaceCommands(namespace string) [][]string { | ||
return [][]string{ | ||
{"kubectl", "get", "events", "-n", namespace}, | ||
{"kubectl", "get", "pods", "-n", namespace, "-l", constants.EnvoyUniqueIDLabelName}, | ||
{"kubectl", "get", "svc", "-n", namespace}, | ||
} | ||
} |
Oops, something went wrong.