mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-20 02:44:58 +00:00
161 lines
3.6 KiB
Go
161 lines
3.6 KiB
Go
package dumper
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"log"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
corev1 "k8s.io/api/core/v1"
|
|
)
|
|
|
|
// Dumper struct is for dumping cluster
|
|
type Dumper struct {
|
|
cmd string
|
|
resources []string
|
|
location string
|
|
Errors map[string]string
|
|
Files map[string][]byte
|
|
}
|
|
|
|
// New return new Dumper object
|
|
func New(location string) Dumper {
|
|
directory := "cluster-dump"
|
|
if len(location) > 0 {
|
|
directory = location + "/cluster-dump"
|
|
}
|
|
return Dumper{
|
|
cmd: "kubectl",
|
|
resources: []string{
|
|
"pods",
|
|
"replicasets",
|
|
"deployments",
|
|
"daemonsets",
|
|
"replicationcontrollers",
|
|
"events",
|
|
},
|
|
location: directory,
|
|
Errors: make(map[string]string),
|
|
Files: make(map[string][]byte),
|
|
}
|
|
}
|
|
|
|
type k8sPods struct {
|
|
Items []corev1.Pod `json:"items"`
|
|
}
|
|
|
|
type namespaces struct {
|
|
Items []corev1.Namespace `json:"items"`
|
|
}
|
|
|
|
// DumpCluster create dump of a cluster in Dumper.location
|
|
func (d *Dumper) DumpCluster() error {
|
|
output, err := d.runCmd("get", "namespaces", "-o", "json")
|
|
if err != nil {
|
|
return errors.Wrap(err, "get namespaces")
|
|
}
|
|
var nss namespaces
|
|
err = json.Unmarshal(output, &nss)
|
|
if err != nil {
|
|
return errors.Wrap(err, "unmarshal namespaces")
|
|
}
|
|
|
|
for _, ns := range nss.Items {
|
|
output, err = d.runCmd("get", "pods", "-o", "json", "--namespace", ns.Name)
|
|
if err != nil {
|
|
continue // runCmd already stored this error in Dumper.Errors
|
|
}
|
|
var pods k8sPods
|
|
err = json.Unmarshal(output, &pods)
|
|
if err != nil {
|
|
log.Println(errors.Wrap(err, "unmarshal pods"))
|
|
}
|
|
|
|
for _, pod := range pods.Items {
|
|
output, err = d.runCmd("logs", pod.Name, "--namespace", ns.Name, "--all-containers")
|
|
if err != nil {
|
|
continue // runCmd already stored this error in Dumper.Errors
|
|
}
|
|
d.Files[d.location+"/"+ns.Name+"/"+pod.Name+"/logs.txt"] = output
|
|
}
|
|
|
|
for _, resource := range d.resources {
|
|
err = d.getResource(resource, ns.Name)
|
|
if err != nil {
|
|
log.Println(errors.Wrapf(err, "get %s resource", resource))
|
|
}
|
|
}
|
|
}
|
|
|
|
err = d.getResource("nodes", "")
|
|
if err != nil {
|
|
log.Println(errors.Wrapf(err, "get nodes"))
|
|
}
|
|
|
|
err = d.writeErrorsToFile()
|
|
if err != nil {
|
|
log.Println(errors.Wrap(err, "write errors"))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// runCmd run command (Dumper.cmd) with given args, return it output and save all errors in Dumper.errors
|
|
func (d *Dumper) runCmd(args ...string) ([]byte, error) {
|
|
var outb, errb bytes.Buffer
|
|
cmd := exec.Command(d.cmd, args...)
|
|
cmd.Stdout = &outb
|
|
cmd.Stderr = &errb
|
|
err := cmd.Run()
|
|
if err != nil {
|
|
d.saveCommandError(err.Error()+" "+outb.String(), args...)
|
|
return outb.Bytes(), err
|
|
}
|
|
if errb.Len() > 0 {
|
|
d.saveCommandError(errb.String()+" "+outb.String(), args...)
|
|
return outb.Bytes(), err
|
|
}
|
|
|
|
return outb.Bytes(), nil
|
|
}
|
|
|
|
func (d *Dumper) getResource(name, namespace string) error {
|
|
location := d.location
|
|
args := []string{"get", name, "-o", "yaml"}
|
|
if len(namespace) > 0 {
|
|
args = append(args, "--namespace", namespace)
|
|
location = d.location + "/" + namespace
|
|
}
|
|
output, err := d.runCmd(args...)
|
|
if err != nil {
|
|
return nil // runCmd already stored this error in Dumper.Errors
|
|
}
|
|
|
|
d.Files[location+"/"+name+".yaml"] = output
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *Dumper) saveCommandError(err string, args ...string) {
|
|
command := d.cmd + " " + strings.Join(args, " ")
|
|
|
|
d.Errors[command] = err
|
|
}
|
|
|
|
func (d *Dumper) writeErrorsToFile() error {
|
|
var errStr string
|
|
for cmd, errS := range d.Errors {
|
|
errStr += cmd + ": " + errS + "\n"
|
|
}
|
|
d.Files[d.location+"/errors.txt"] = []byte(errStr)
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetLocation return Dumper.location
|
|
func (d *Dumper) GetLocation() string {
|
|
return d.location
|
|
}
|