PT-1865 Clean-up

This commit is contained in:
Max Dudin
2020-07-06 12:19:32 +03:00
parent 36e9a2f07f
commit dbb1982321
3 changed files with 77 additions and 45 deletions

View File

@@ -1,6 +1,42 @@
#Debug collector tool #Debug collector tool
Collects all necessary data for debugging from a cluster and creates a tar file with it Collects data for debugging from a k8s/opeshift cluster and creates a tar.gz archive with it.
Archive name will be "cluster-dump.tar.gz" and it will be saved in the same location where you will run tool
Installed and configured kubectl is needed for work ###Data that will be collected
"pods",
"replicasets",
"deployments",
"statefulsets",
"replicationcontrollers",
"events",
"configmaps",
"secrets",
"cronjobs",
"jobs",
"podsecuritypolicies",
"poddisruptionbudgets",
"perconaxtradbbackups",
"perconaxtradbclusterbackups",
"perconaxtradbclusterrestores",
"perconaxtradbclusters",
"clusterrolebindings",
"clusterroles",
"rolebindings",
"roles",
"storageclasses",
"persistentvolumeclaims",
"persistentvolumes",
"modes",
"pxc/psmdb" (depend on 'resource' flag)
###Usage
Tool accept 3 flags:
1) resource: name of required custom resource ("pxc" is default)
2) namespace: namespace from where you need to collect data. If empty data will be collected from all namespaces
3) cluster: name for exact pxc/psmdb cluster. If empty tool will collect all "resource"
###Requirements
Installed and configured 'kubectl' is needed for work

View File

@@ -20,16 +20,12 @@ type Dumper struct {
resources []string resources []string
namespace string namespace string
location string location string
Errors string errors string
mode int64 mode int64
} }
// New return new Dumper object // New return new Dumper object
func New(location, namespace, resource string) Dumper { func New(location, namespace, resource string) Dumper {
directory := "cluster-dump"
if len(location) > 0 {
directory = location + "/cluster-dump"
}
resources := []string{ resources := []string{
"pods", "pods",
"replicasets", "replicasets",
@@ -61,7 +57,7 @@ func New(location, namespace, resource string) Dumper {
return Dumper{ return Dumper{
cmd: "kubectl", cmd: "kubectl",
resources: resources, resources: resources,
location: directory, location: "cluster-dump",
mode: int64(0777), mode: int64(0777),
namespace: namespace, namespace: namespace,
} }
@@ -77,27 +73,32 @@ type namespaces struct {
// DumpCluster create dump of a cluster in Dumper.location // DumpCluster create dump of a cluster in Dumper.location
func (d *Dumper) DumpCluster() error { func (d *Dumper) DumpCluster() error {
tarFile, err := os.Create(d.location + ".tar.gz") file, err := os.Create(d.location + ".tar.gz")
if err != nil { if err != nil {
return errors.Wrap(err, "create tar file") return errors.Wrap(err, "create tar file")
} }
zr := gzip.NewWriter(tarFile) zr := gzip.NewWriter(file)
tw := tar.NewWriter(zr) tw := tar.NewWriter(zr)
defer func() { defer func() {
err = addToArchive(d.location+"/errors.txt", d.mode, []byte(d.errors), tw)
if err != nil {
log.Println("Error: add errors.txt to archive:", err)
}
err = tw.Close() err = tw.Close()
if err != nil { if err != nil {
log.Panicln("close tar writer", err) log.Println("close tar writer", err)
return return
} }
err = zr.Close() err = zr.Close()
if err != nil { if err != nil {
log.Panicln("close gzip writer", err) log.Println("close gzip writer", err)
return return
} }
err = tarFile.Close() err = file.Close()
if err != nil { if err != nil {
log.Panicln("close tar file", err) log.Println("close file", err)
return return
} }
}() }()
@@ -112,13 +113,13 @@ func (d *Dumper) DumpCluster() error {
args := []string{"get", "namespaces", "-o", "json"} args := []string{"get", "namespaces", "-o", "json"}
output, err := d.runCmd(args...) output, err := d.runCmd(args...)
if err != nil { if err != nil {
d.saveCommandError(err.Error(), args...) d.logError(err.Error(), args...)
return errors.Wrap(err, "get namespaces") return errors.Wrap(err, "get namespaces")
} }
err = json.Unmarshal(output, &nss) err = json.Unmarshal(output, &nss)
if err != nil { if err != nil {
d.saveCommandError(err.Error(), "unmarshal namespaces") d.logError(err.Error(), "unmarshal namespaces")
return errors.Wrap(err, "unmarshal namespaces") return errors.Wrap(err, "unmarshal namespaces")
} }
} }
@@ -127,15 +128,15 @@ func (d *Dumper) DumpCluster() error {
args := []string{"get", "pods", "-o", "json", "--namespace", ns.Name} args := []string{"get", "pods", "-o", "json", "--namespace", ns.Name}
output, err := d.runCmd(args...) output, err := d.runCmd(args...)
if err != nil { if err != nil {
d.saveCommandError(err.Error(), args...) d.logError(err.Error(), args...)
continue continue
} }
var pods k8sPods var pods k8sPods
err = json.Unmarshal(output, &pods) err = json.Unmarshal(output, &pods)
if err != nil { if err != nil {
d.saveCommandError(err.Error(), "unmarshal pods from namespace", ns.Name) d.logError(err.Error(), "unmarshal pods from namespace", ns.Name)
log.Println(errors.Wrap(err, "unmarshal pods")) log.Printf("Error: unmarshal pods in namespace %s: %v", ns.Name, err)
} }
for _, pod := range pods.Items { for _, pod := range pods.Items {
@@ -143,36 +144,31 @@ func (d *Dumper) DumpCluster() error {
args := []string{"logs", pod.Name, "--namespace", ns.Name, "--all-containers"} args := []string{"logs", pod.Name, "--namespace", ns.Name, "--all-containers"}
output, err = d.runCmd(args...) output, err = d.runCmd(args...)
if err != nil { if err != nil {
log.Printf("logs pod %s namespace %s: %v", pod.Name, ns.Name, err) d.logError(err.Error(), args...)
d.saveCommandError(err.Error(), args...) err = addToArchive(location, d.mode, []byte(err.Error()), tw)
err = createArchive(location, d.mode, []byte(err.Error()), tw)
if err != nil { if err != nil {
log.Printf("create archive with err: %v", err) log.Printf("Error: create archive with logs for pod %s in namespace %s: %v", pod.Name, ns.Name, err)
} }
continue continue
} }
err = createArchive(location, d.mode, output, tw) err = addToArchive(location, d.mode, output, tw)
if err != nil { if err != nil {
log.Printf("create archive for pod %s: %v", pod.Name, err) d.logError(err.Error(), "create archive for pod "+pod.Name)
log.Printf("Error: create archive for pod %s: %v", pod.Name, err)
} }
} }
for _, resource := range d.resources { for _, resource := range d.resources {
err = d.getResource(resource, ns.Name, tw) err = d.getResource(resource, ns.Name, tw)
if err != nil { if err != nil {
log.Println(errors.Wrapf(err, "get %s resource", resource)) log.Printf("Error: get %s resource: %v", resource, err)
} }
} }
err = createArchive(d.location+"/errors.txt", d.mode, []byte(d.Errors), tw)
if err != nil {
log.Println(errors.Wrap(err, "write errors"))
}
} }
err = d.getResource("nodes", "", tw) err = d.getResource("nodes", "", tw)
if err != nil { if err != nil {
log.Println(errors.Wrapf(err, "get nodes")) return errors.Wrapf(err, "get nodes")
} }
return nil return nil
@@ -202,26 +198,26 @@ func (d *Dumper) getResource(name, namespace string, tw *tar.Writer) error {
location += "/" + name + ".yaml" location += "/" + name + ".yaml"
output, err := d.runCmd(args...) output, err := d.runCmd(args...)
if err != nil { if err != nil {
d.saveCommandError(err.Error(), args...) d.logError(err.Error(), args...)
log.Printf("get resource %s namespace %s: %v", name, namespace, err) log.Printf("Error: get resource %s in namespace %s: %v", name, namespace, err)
return createArchive(location, d.mode, []byte(err.Error()), tw) return addToArchive(location, d.mode, []byte(err.Error()), tw)
} }
return createArchive(location, d.mode, output, tw) return addToArchive(location, d.mode, output, tw)
} }
func (d *Dumper) saveCommandError(err string, args ...string) { func (d *Dumper) logError(err string, args ...string) {
d.Errors += d.cmd + " " + strings.Join(args, " ") + ": " + err + "\n" d.errors += d.cmd + " " + strings.Join(args, " ") + ": " + err + "\n"
} }
func createArchive(location string, mode int64, content []byte, tw *tar.Writer) error { func addToArchive(location string, mode int64, content []byte, tw *tar.Writer) error {
hdr := &tar.Header{ hdr := &tar.Header{
Name: location, Name: location,
Mode: mode, Mode: mode,
Size: int64(len(content)), Size: int64(len(content)),
} }
if err := tw.WriteHeader(hdr); err != nil { if err := tw.WriteHeader(hdr); err != nil {
return errors.Wrap(err, "write header") return errors.Wrapf(err, "write header to %s", location)
} }
if _, err := tw.Write(content); err != nil { if _, err := tw.Write(content); err != nil {
return errors.Wrapf(err, "write content to %s", location) return errors.Wrapf(err, "write content to %s", location)

View File

@@ -13,23 +13,23 @@ func main() {
resource := "" resource := ""
clusterName := "" clusterName := ""
flag.StringVar(&namespace, "namespace", "", "Namespace for dumping. If empty will dump all namespaces") flag.StringVar(&namespace, "namespace", "", "Namespace for collecting data. If empty data will be collected from all namespaces")
flag.StringVar(&resource, "resource", "pxc", "Resource name. Default value - 'pxc'") flag.StringVar(&resource, "resource", "pxc", "Resource name. Default value - 'pxc'")
flag.StringVar(&clusterName, "cluster", "", "Cluster name") flag.StringVar(&clusterName, "cluster", "", "Cluster name")
flag.Parse() flag.Parse()
if len(clusterName) > 0 { if len(clusterName) > 0 {
resource = resource + "/" + clusterName resource += "/" + clusterName
} }
d := dumper.New("", namespace, resource) d := dumper.New("", namespace, resource)
log.Println("Start dump cluster") log.Println("Start collecting cluster data")
err := d.DumpCluster() err := d.DumpCluster()
if err != nil { if err != nil {
log.Println(err) log.Println("Error:", err)
os.Exit(1) os.Exit(1)
} }
log.Println("Cluster dump ready") log.Println("Done")
} }