diff --git a/docs/pt-k8s-debug-collector.rst b/docs/pt-k8s-debug-collector.rst index bc69497e..467cc4da 100644 --- a/docs/pt-k8s-debug-collector.rst +++ b/docs/pt-k8s-debug-collector.rst @@ -46,6 +46,13 @@ Data, collected for PXC "perconaxtradbclusterrestores", "perconaxtradbclusters" +Summary, collected for PXC (available in file summary.txt) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: bash + + "pt-mysql-summary" + Individual files, collected for PXC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -60,6 +67,22 @@ Individual files, collected for PXC "var/lib/mysql/mysqld.post.processing.log", "var/lib/mysql/auto.cnf" +Data, collected for MySQL +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: bash + + "perconaservermysqlbackups", + "perconaservermysqlrestores", + "perconaservermysqls" + +Summary, collected for MySQL (available in file summary.txt) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: bash + + "pt-mysql-summary" + Data, collected for MongoDB ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -69,22 +92,90 @@ Data, collected for MongoDB "perconaservermongodbrestores", "perconaservermongodbs" +Summary, collected for MongoDB (available in file summary.txt) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: bash + + "pt-mongodb-summary" + +Data, collected for PostgreSQL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: bash + + "perconapgclusters", + "pgclusters", + "pgpolicies", + "pgreplicas", + "pgtasks" + +Summary, collected for PostgreSQL (available in file summary.txt) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: bash + + "pg_gather" + Usage ===== ``pt-k8s-debug-collector `` -Flags: +Supported Flags: +================ -``--resource` targeted custom resource name (default "pxc")`` +``--resource`` -``--namespace` targeted namespace. By default data will be collected from all namespaces`` +Targeted custom resource name. Supported values: -``--cluster` targeted pxc/psmdb cluster. By default data from all available clusters to be collected`` +* ``pxc`` - PXC + +* ``psmdb`` - MongoDB + +* ``pg`` - PostgreSQL + +* ``ps`` - MySQL + +* ``none`` - Collect only general Kubernetes data, do not collect anything specific to the particular operator). + +Default: ``none`` + +``--namespace`` + +Targeted namespace. By default data will be collected from all namespaces + +``--cluster`` + +Targeted cluster. By default data from all available clusters to be collected + +``--kubeconfig`` + +Path to kubeconfig. Default configuration be used if none specified + +``--forwardport`` + +Port to use when collecting database-specific summaries. By default, 3306 will be used for PXC and MySQL, 27017 for MongoDB, and 5432 for PostgreSQL Requirements ============ -- Installed and configured ``kubectl`` -- Installed and configured ``pt-mysql-summary`` -- Installed and configured ``pt-mongodb-summary`` +- Installed, configured, and available in PATH ``kubectl`` +- Installed, configured, and available in PATH ``pt-mysql-summary`` for PXC and MySQL +- Installed, configured, and available in PATH ``pt-mongodb-summary`` for MongoDB + +Known Issues +============ + +On Kubernetes 1.21 - 1.24 warning is printed: + +.. code-block:: bash + + 2022/12/15 17:43:16 Error: get resource podsecuritypolicies in namespace default: error: , stderr: Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+ + , stdout: apiVersion: v1 + items: [] + kind: List + metadata: + resourceVersion: "" + +This warning is harmless and does not affect data collection. We will remove podsecuritypolicies once everyone upgrade to Kubernetes 1.25 or newer. Before that we advise to ignore this warning. \ No newline at end of file diff --git a/src/go/pt-k8s-debug-collector/README.md b/src/go/pt-k8s-debug-collector/README.md deleted file mode 100644 index 0a957fdd..00000000 --- a/src/go/pt-k8s-debug-collector/README.md +++ /dev/null @@ -1,84 +0,0 @@ -# Debug collector tool - -Collects debug data (logs, resource statuses etc.) from a k8s/OpenShift cluster. Data is packed into the `cluster-dump.tar.gz` archive in the current working directory. - -## Data that will be collected - -### Data, collected for all resources - -``` -"pods", -"replicasets", -"deployments", -"statefulsets", -"replicationcontrollers", -"events", -"configmaps", -"cronjobs", -"jobs", -"podsecuritypolicies", -"poddisruptionbudgets", -"perconaxtradbbackups", -"perconaxtradbclusterbackups", -"perconaxtradbclusterrestores", -"perconaxtradbclusters", -"clusterrolebindings", -"clusterroles", -"rolebindings", -"roles", -"storageclasses", -"persistentvolumeclaims", -"persistentvolumes", -"modes", -"your-custom-resource" (depends on 'resource' flag) - -``` - -### Data, collected for PXC - -``` -"perconaxtradbbackups", -"perconaxtradbclusterbackups", -"perconaxtradbclusterrestores", -"perconaxtradbclusters" -``` - -### Individual files, collected for PXC - -``` -"var/lib/mysql/mysqld-error.log", -"var/lib/mysql/innobackup.backup.log", -"var/lib/mysql/innobackup.move.log", -"var/lib/mysql/innobackup.prepare.log", -"var/lib/mysql/grastate.dat", -"var/lib/mysql/gvwstate.dat", -"var/lib/mysql/mysqld.post.processing.log", -"var/lib/mysql/auto.cnf" -``` - -### Data, collected for MongoDB - -``` -"perconaservermongodbbackups", -"perconaservermongodbrestores", -"perconaservermongodbs" -``` - -## Usage - -`pt-k8s-debug-collector ` - -Flags: - -`--resource` targeted custom resource name (default "pxc") - -`--namespace` targeted namespace. By default, data will be collected from all namespaces - -`--cluster` targeted pxc/psmdb cluster. By default, data from all available clusters to be collected - -## Requirements - -- Installed and configured 'kubectl' -- Installed and configured 'pt-mysql-summary' -- Installed and configured 'pt-mongodb-summary' - diff --git a/src/go/pt-k8s-debug-collector/README.rst b/src/go/pt-k8s-debug-collector/README.rst new file mode 100644 index 00000000..467cc4da --- /dev/null +++ b/src/go/pt-k8s-debug-collector/README.rst @@ -0,0 +1,181 @@ +.. _pt-k8s-debug-collector: + +================================== +:program:`pt-k8s-debug-collector` +================================== + +Collects debug data (logs, resource statuses etc.) from a k8s/OpenShift cluster. Data is packed into the ``cluster-dump.tar.gz`` archive in the current working directory. + +Data that will be collected +=========================== + +.. code-block:: bash + + "pods", + "replicasets", + "deployments", + "statefulsets", + "replicationcontrollers", + "events", + "configmaps", + "cronjobs", + "jobs", + "podsecuritypolicies", + "poddisruptionbudgets", + "perconaxtradbbackups", + "perconaxtradbclusterbackups", + "perconaxtradbclusterrestores", + "perconaxtradbclusters", + "clusterrolebindings", + "clusterroles", + "rolebindings", + "roles", + "storageclasses", + "persistentvolumeclaims", + "persistentvolumes", + "modes", + "your-custom-resource" (depends on 'resource' flag) + +Data, collected for PXC +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: bash + + "perconaxtradbbackups", + "perconaxtradbclusterbackups", + "perconaxtradbclusterrestores", + "perconaxtradbclusters" + +Summary, collected for PXC (available in file summary.txt) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: bash + + "pt-mysql-summary" + +Individual files, collected for PXC +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: bash + + "var/lib/mysql/mysqld-error.log", + "var/lib/mysql/innobackup.backup.log", + "var/lib/mysql/innobackup.move.log", + "var/lib/mysql/innobackup.prepare.log", + "var/lib/mysql/grastate.dat", + "var/lib/mysql/gvwstate.dat", + "var/lib/mysql/mysqld.post.processing.log", + "var/lib/mysql/auto.cnf" + +Data, collected for MySQL +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: bash + + "perconaservermysqlbackups", + "perconaservermysqlrestores", + "perconaservermysqls" + +Summary, collected for MySQL (available in file summary.txt) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: bash + + "pt-mysql-summary" + +Data, collected for MongoDB +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: bash + + "perconaservermongodbbackups", + "perconaservermongodbrestores", + "perconaservermongodbs" + +Summary, collected for MongoDB (available in file summary.txt) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: bash + + "pt-mongodb-summary" + +Data, collected for PostgreSQL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: bash + + "perconapgclusters", + "pgclusters", + "pgpolicies", + "pgreplicas", + "pgtasks" + +Summary, collected for PostgreSQL (available in file summary.txt) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: bash + + "pg_gather" + +Usage +===== + +``pt-k8s-debug-collector `` + +Supported Flags: +================ + +``--resource`` + +Targeted custom resource name. Supported values: + +* ``pxc`` - PXC + +* ``psmdb`` - MongoDB + +* ``pg`` - PostgreSQL + +* ``ps`` - MySQL + +* ``none`` - Collect only general Kubernetes data, do not collect anything specific to the particular operator). + +Default: ``none`` + +``--namespace`` + +Targeted namespace. By default data will be collected from all namespaces + +``--cluster`` + +Targeted cluster. By default data from all available clusters to be collected + +``--kubeconfig`` + +Path to kubeconfig. Default configuration be used if none specified + +``--forwardport`` + +Port to use when collecting database-specific summaries. By default, 3306 will be used for PXC and MySQL, 27017 for MongoDB, and 5432 for PostgreSQL + +Requirements +============ + +- Installed, configured, and available in PATH ``kubectl`` +- Installed, configured, and available in PATH ``pt-mysql-summary`` for PXC and MySQL +- Installed, configured, and available in PATH ``pt-mongodb-summary`` for MongoDB + +Known Issues +============ + +On Kubernetes 1.21 - 1.24 warning is printed: + +.. code-block:: bash + + 2022/12/15 17:43:16 Error: get resource podsecuritypolicies in namespace default: error: , stderr: Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+ + , stdout: apiVersion: v1 + items: [] + kind: List + metadata: + resourceVersion: "" + +This warning is harmless and does not affect data collection. We will remove podsecuritypolicies once everyone upgrade to Kubernetes 1.25 or newer. Before that we advise to ignore this warning. \ No newline at end of file diff --git a/src/go/pt-k8s-debug-collector/dumper/dumper.go b/src/go/pt-k8s-debug-collector/dumper/dumper.go index 29c99fa2..3a469f7c 100644 --- a/src/go/pt-k8s-debug-collector/dumper/dumper.go +++ b/src/go/pt-k8s-debug-collector/dumper/dumper.go @@ -20,18 +20,20 @@ import ( // Dumper struct is for dumping cluster type Dumper struct { - cmd string - resources []string - filePaths []string - namespace string - location string - errors string - mode int64 - crType string + cmd string + kubeconfig string + resources []string + filePaths []string + namespace string + location string + errors string + mode int64 + crType string + forwardport string } // New return new Dumper object -func New(location, namespace, resource string) Dumper { +func New(location, namespace, resource string, kubeconfig string, forwardport string) Dumper { resources := []string{ "pods", "replicasets", @@ -54,11 +56,8 @@ func New(location, namespace, resource string) Dumper { } filePaths := make([]string, 0) if len(resource) > 0 { - resources = append(resources, resource) - if resourceType(resource) == "pxc" { resources = append(resources, - "perconaxtradbbackups", "perconaxtradbclusterbackups", "perconaxtradbclusterrestores", "perconaxtradbclusters") @@ -78,16 +77,32 @@ func New(location, namespace, resource string) Dumper { "perconaservermongodbrestores", "perconaservermongodbs", ) + } else if resourceType(resource) == "pg" { + resources = append(resources, + "perconapgclusters", + "pgclusters", + "pgpolicies", + "pgreplicas", + "pgtasks", + ) + } else if resourceType(resource) == "ps" { + resources = append(resources, + "perconaservermysqlbackups", + "perconaservermysqlrestores", + "perconaservermysqls", + ) } } return Dumper{ - cmd: "kubectl", - resources: resources, - filePaths: filePaths, - location: "cluster-dump", - mode: int64(0o777), - namespace: namespace, - crType: resource, + cmd: "kubectl", + kubeconfig: kubeconfig, + resources: resources, + filePaths: filePaths, + location: "cluster-dump", + mode: int64(0o777), + namespace: namespace, + crType: resource, + forwardport: forwardport, } } @@ -187,11 +202,14 @@ func (d *Dumper) DumpCluster() error { if len(pod.Labels) == 0 { continue } - location = filepath.Join(d.location, ns.Name, pod.Name, "/pt-summary.txt") + location = filepath.Join(d.location, ns.Name, pod.Name, "/summary.txt") component := resourceType(d.crType) if component == "psmdb" { component = "mongod" } + if component == "ps" { + component = "mysql" + } if pod.Labels["app.kubernetes.io/instance"] != "" && pod.Labels["app.kubernetes.io/component"] != "" { resource := "secret/" + pod.Labels["app.kubernetes.io/instance"] + "-" + pod.Labels["app.kubernetes.io/component"] err = d.getResource(resource, ns.Name, true, tw) @@ -199,20 +217,27 @@ func (d *Dumper) DumpCluster() error { log.Printf("Error: get %s resource: %v", resource, err) } } - if pod.Labels["app.kubernetes.io/component"] == component { + if pod.Labels["app.kubernetes.io/component"] == component || + (component == "pg" && pod.Labels["pgo-pg-database"] == "true") { + var crName string + if component == "pg" { + crName = pod.Labels["pg-cluster"] + } else { + crName = pod.Labels["app.kubernetes.io/instance"] + } // Get summary - output, err = d.getPodSummary(resourceType(d.crType), pod.Name, pod.Labels["app.kubernetes.io/instance"], tw) + output, err = d.getPodSummary(resourceType(d.crType), pod.Name, crName, ns.Name, tw) if err != nil { d.logError(err.Error(), d.crType, pod.Name) err = addToArchive(location, d.mode, []byte(err.Error()), tw) if err != nil { - log.Printf("Error: create pt-summary errors archive for pod %s in namespace %s: %v", pod.Name, ns.Name, err) + log.Printf("Error: create summary errors archive for pod %s in namespace %s: %v", pod.Name, ns.Name, err) } } else { err = addToArchive(location, d.mode, output, tw) if err != nil { - d.logError(err.Error(), "create pt-summary archive for pod "+pod.Name) - log.Printf("Error: create pt-summary archive for pod %s: %v", pod.Name, err) + d.logError(err.Error(), "create summary archive for pod "+pod.Name) + log.Printf("Error: create summary archive for pod %s: %v", pod.Name, err) } } @@ -247,6 +272,7 @@ func (d *Dumper) DumpCluster() error { // runCmd run command (Dumper.cmd) with given args, return it output func (d *Dumper) runCmd(args ...string) ([]byte, error) { var outb, errb bytes.Buffer + args = append(args, "--kubeconfig", d.kubeconfig) cmd := exec.Command(d.cmd, args...) cmd.Stdout = &outb cmd.Stderr = &errb @@ -328,7 +354,7 @@ func (d *Dumper) getIndividualFiles(resource, namespace string, podName, path, l return addToArchive(location+"/"+path, d.mode, output, tw) } -func (d *Dumper) getPodSummary(resource, podName, crName string, tw *tar.Writer) ([]byte, error) { +func (d *Dumper) getPodSummary(resource, podName, crName string, namespace string, tw *tar.Writer) ([]byte, error) { var ( summCmdName string ports string @@ -336,33 +362,86 @@ func (d *Dumper) getPodSummary(resource, podName, crName string, tw *tar.Writer) ) switch resource { + case "ps": + fallthrough case "pxc": - cr, err := d.getCR("pxc/" + crName) + var pass, port string + if d.forwardport != "" { + port = d.forwardport + } else { + port = "3306" + } + cr, err := d.getCR(resource+"/"+crName, namespace) if err != nil { return nil, errors.Wrap(err, "get cr") } - pass, err := d.getDataFromSecret(cr.Spec.SecretName, "root") + if cr.Spec.SecretName != "" { + pass, err = d.getDataFromSecret(cr.Spec.SecretName, "root", namespace) + } else { + pass, err = d.getDataFromSecret(crName+"-secrets", "root", namespace) + } if err != nil { return nil, errors.Wrap(err, "get password from pxc users secret") } - ports = "3306:3306" + ports = port + ":3306" summCmdName = "pt-mysql-summary" - summCmdArgs = []string{"--host=127.0.0.1", "--port=3306", "--user=root", "--password=" + string(pass)} - case "psmdb": - cr, err := d.getCR("psmdb/" + crName) + summCmdArgs = []string{"--host=127.0.0.1", "--port=" + port, "--user=root", "--password=" + string(pass)} + case "pg": + var user, pass, port string + if d.forwardport != "" { + port = d.forwardport + } else { + port = "5432" + } + cr, err := d.getCR("pgclusters", namespace) if err != nil { return nil, errors.Wrap(err, "get cr") } - pass, err := d.getDataFromSecret(cr.Spec.Secrets.Users, "MONGODB_CLUSTER_ADMIN_PASSWORD") + if cr.Spec.SecretName != "" { + user, err = d.getDataFromSecret(cr.Spec.SecretName, "username", namespace) + } else { + user, err = d.getDataFromSecret(crName+"-postgres-secret", "username", namespace) + } + if err != nil { + return nil, errors.Wrap(err, "get user from PostgreSQL users secret") + } + if cr.Spec.SecretName != "" { + pass, err = d.getDataFromSecret(cr.Spec.SecretName, "password", namespace) + } else { + pass, err = d.getDataFromSecret(crName+"-postgres-secret", "password", namespace) + } + if err != nil { + return nil, errors.Wrap(err, "get password from PostgreSQL users secret") + } + ports = port + ":5432" + summCmdName = "sh" + summCmdArgs = []string{"-c", "curl https://raw.githubusercontent.com/percona/support-snippets/master/postgresql/pg_gather/gather.sql" + + " 2>/dev/null | PGPASSWORD=" + string(pass) + " psql -X --host=127.0.0.1 --port=" + port + " --user=" + user} + case "psmdb": + var port string + if d.forwardport != "" { + port = d.forwardport + } else { + port = "27017" + } + cr, err := d.getCR("psmdb/"+crName, namespace) + if err != nil { + return nil, errors.Wrap(err, "get cr") + } + user, err := d.getDataFromSecret(cr.Spec.Secrets.Users, "MONGODB_DATABASE_ADMIN_USER", namespace) if err != nil { return nil, errors.Wrap(err, "get password from psmdb users secret") } - ports = "27017:27017" + pass, err := d.getDataFromSecret(cr.Spec.Secrets.Users, "MONGODB_DATABASE_ADMIN_PASSWORD", namespace) + if err != nil { + return nil, errors.Wrap(err, "get password from psmdb users secret") + } + ports = port + ":27017" summCmdName = "pt-mongodb-summary" - summCmdArgs = []string{"--username=clusterAdmin", "--password=" + pass, "--authenticationDatabase=admin", "127.0.0.1:27017"} + summCmdArgs = []string{"--username=" + user, "--password=" + pass, "--authenticationDatabase=admin", "127.0.0.1:" + port} } - cmdPortFwd := exec.Command(d.cmd, "port-forward", "pod/"+podName, ports) + cmdPortFwd := exec.Command(d.cmd, "port-forward", "pod/"+podName, ports, "-n", namespace, "--kubeconfig", d.kubeconfig) go func() { err := cmdPortFwd.Run() if err != nil { @@ -390,22 +469,22 @@ func (d *Dumper) getPodSummary(resource, podName, crName string, tw *tar.Writer) return []byte(fmt.Sprintf("stderr: %s, stdout: %s", errb.String(), outb.String())), nil } -func (d *Dumper) getCR(crName string) (crSecrets, error) { +func (d *Dumper) getCR(crName string, namespace string) (crSecrets, error) { var cr crSecrets - output, err := d.runCmd("get", crName, "-o", "json") + output, err := d.runCmd("get", crName, "-o", "json", "-n", namespace) if err != nil { return cr, errors.Wrap(err, "get "+crName) } err = json.Unmarshal(output, &cr) if err != nil { - return cr, errors.Wrap(err, "unmarshal psmdb cr") + return cr, errors.Wrap(err, "unmarshal "+crName+" cr") } return cr, nil } -func (d *Dumper) getDataFromSecret(secretName, dataName string) (string, error) { - passEncoded, err := d.runCmd("get", "secrets/"+secretName, "--template={{.data."+dataName+"}}") +func (d *Dumper) getDataFromSecret(secretName, dataName string, namespace string) (string, error) { + passEncoded, err := d.runCmd("get", "secrets/"+secretName, "--template={{.data."+dataName+"}}", "-n", namespace) if err != nil { return "", errors.Wrap(err, "run get secret cmd") } @@ -422,6 +501,10 @@ func resourceType(s string) string { return "pxc" } else if s == "psmdb" || strings.HasPrefix(s, "psmdb/") { return "psmdb" + } else if s == "pg" || strings.HasPrefix(s, "pg/") { + return "pg" + } else if s == "ps" || strings.HasPrefix(s, "ps/") { + return "ps" } return s } diff --git a/src/go/pt-k8s-debug-collector/main.go b/src/go/pt-k8s-debug-collector/main.go index 39e692f8..685cb019 100644 --- a/src/go/pt-k8s-debug-collector/main.go +++ b/src/go/pt-k8s-debug-collector/main.go @@ -12,17 +12,21 @@ func main() { namespace := "" resource := "" clusterName := "" + kubeconfig := "" + forwardport := "" 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", "none", "Collect data, specific to the resource. Supported values: pxc, psmdb, pg, ps, none") flag.StringVar(&clusterName, "cluster", "", "Cluster name") + flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to kubeconfig") + flag.StringVar(&forwardport, "forwardport", "", "Port to use for port forwarding") flag.Parse() if len(clusterName) > 0 { resource += "/" + clusterName } - d := dumper.New("", namespace, resource) + d := dumper.New("", namespace, resource, kubeconfig, forwardport) log.Println("Start collecting cluster data") err := d.DumpCluster() diff --git a/src/go/pt-k8s-debug-collector/main_test.go b/src/go/pt-k8s-debug-collector/main_test.go index e03e6edf..c6c0b784 100644 --- a/src/go/pt-k8s-debug-collector/main_test.go +++ b/src/go/pt-k8s-debug-collector/main_test.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "os" "os/exec" "path" "strings" @@ -10,11 +11,33 @@ import ( "golang.org/x/exp/slices" ) +/* +This test requires: +- Running K8 Operator installation +- kubectl configuration files, one for each supported operator +-- KUBECONFIG_PXC for K8SPXC +-- KUBECONFIG_PS for K8SPS +-- KUBECONFIG_PSMDB for K8SPSMDB +-- KUBECONFIG_PG for K8SPG + +You can additionally set option FORWARDPORT if you want to use custom port when testing summaries. + +pt-mysql-summary and pt-mongodb-summary must be in the PATH. + +Since running pt-k8s-debug-collector may take long time run go test with increase timeout: +go test -timeout 6000s + +We do not explicitly test --kubeconfig and --forwardport options, because they are used in other tests. +*/ + /* Tests collection of the individual files by pt-k8s-debug-collector. Requires running K8SPXC instance and kubectl, configured to access that instance by default. */ func TestIndividualFiles(t *testing.T) { + if os.Getenv("KUBECONFIG_PXC") == "" { + t.Skip("TestIndividualFiles requires K8SPXC") + } tests := []struct { name string cmd []string @@ -56,7 +79,7 @@ func TestIndividualFiles(t *testing.T) { }, } - cmd := exec.Command("../../../bin/pt-k8s-debug-collector") + cmd := exec.Command("../../../bin/pt-k8s-debug-collector", "--kubeconfig", os.Getenv("KUBECONFIG_PXC"), "--forwardport", os.Getenv("FORWARDPORT"), "--resource", "pxc") if err := cmd.Run(); err != nil { t.Errorf("error executing pt-k8s-debug-collector: %s", err.Error()) } @@ -77,3 +100,61 @@ func TestIndividualFiles(t *testing.T) { } } } + +/* +Tests for supported values of the --resource option +*/ +func TestResourceOption(t *testing.T) { + testcmd := []string{"sh", "-c", "tar -tf cluster-dump.tar.gz --wildcards '*/summary.txt' 2>/dev/null | wc -l"} + tests := []struct { + name string + want string + kubeconfig string + }{ + { + name: "none", + want: "0", + kubeconfig: "", + }, + { + name: "pxc", + want: "3", + kubeconfig: os.Getenv("KUBECONFIG_PXC"), + }, + { + name: "ps", + want: "3", + kubeconfig: os.Getenv("KUBECONFIG_PS"), + }, + { + name: "psmdb", + want: "3", + kubeconfig: os.Getenv("KUBECONFIG_PSMDB"), + }, + { + name: "pg", + want: "3", + kubeconfig: os.Getenv("KUBECONFIG_PG"), + }, + } + + for _, test := range tests { + cmd := exec.Command("../../../bin/pt-k8s-debug-collector", "--kubeconfig", test.kubeconfig, "--forwardport", os.Getenv("FORWARDPORT"), "--resource", test.name) + if err := cmd.Run(); err != nil { + t.Errorf("error executing pt-k8s-debug-collector: %s", err.Error()) + } + defer func() { + cmd = exec.Command("rm", "-f", "cluster-dump.tar.gz") + if err := cmd.Run(); err != nil { + t.Errorf("error cleaning up test data: %s", err.Error()) + } + }() + out, err := exec.Command(testcmd[0], testcmd[1:]...).Output() + if err != nil { + t.Errorf("test %s, error running command %s:\n%s\n\nCommand output:\n%s", test.name, testcmd, err.Error(), out) + } + if strings.TrimRight(bytes.NewBuffer(out).String(), "\n") != test.want { + t.Errorf("test %s, output is not as expected\nOutput: %s\nWanted: %s", test.name, out, test.want) + } + } +}