@@ -27,6 +27,8 @@ import (
27
27
networkingv1 "k8s.io/api/networking/v1"
28
28
policyv1 "k8s.io/api/policy/v1"
29
29
policyv1beta1 "k8s.io/api/policy/v1beta1"
30
+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
31
+ apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
30
32
apierrors "k8s.io/apimachinery/pkg/api/errors"
31
33
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32
34
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -341,6 +343,11 @@ Collecting PGO CLI logs...
341
343
return err
342
344
}
343
345
346
+ apiExtensionClientSet , err := apiextensionsclientset .NewForConfig (restConfig )
347
+ if err != nil {
348
+ return err
349
+ }
350
+
344
351
discoveryClient , err := discovery .NewDiscoveryClientForConfig (restConfig )
345
352
if err != nil {
346
353
return err
@@ -456,6 +463,12 @@ Collecting PGO CLI logs...
456
463
writeInfo (cmd , fmt .Sprintf ("Error gathering Namespaced API Resources: %s" , err ))
457
464
}
458
465
466
+ // Gather CRDs
467
+ err = gatherCrds (ctx , apiExtensionClientSet , clusterName , tw , cmd )
468
+ if err != nil {
469
+ writeInfo (cmd , fmt .Sprintf ("Error gathering CRDs: %s" , err ))
470
+ }
471
+
459
472
// Gather Events
460
473
err = gatherEvents (ctx , clientset , namespace , clusterName , tw , cmd )
461
474
if err != nil {
@@ -581,6 +594,60 @@ Collecting PGO CLI logs...
581
594
writeInfo (cmd , fmt .Sprintf ("There is no PGUpgrade object associated with cluster '%s'" , clusterName ))
582
595
}
583
596
597
+ // Run kubectl describe and similar commands
598
+ writeInfo (cmd , "Running kubectl describe nodes..." )
599
+ err = runKubectlCommand (tw , cmd , clusterName + "/describe/nodes" , "describe" , "nodes" )
600
+ if err != nil {
601
+ writeInfo (cmd , fmt .Sprintf ("Error running kubectl describe nodes: %s" , err ))
602
+ }
603
+
604
+ writeInfo (cmd , "Running kubectl describe postgrescluster..." )
605
+ err = runKubectlCommand (tw , cmd , clusterName + "/describe/postgrescluster" , "describe" , "postgrescluster" , clusterName , "-n" , namespace )
606
+ if err != nil {
607
+ writeInfo (cmd , fmt .Sprintf ("Error running kubectl describe postgrescluster: %s" , err ))
608
+ }
609
+
610
+ // Resource name is generally 'postgres-operator' but in some environments
611
+ // like Openshift it could be 'postgresoperator'
612
+ writeInfo (cmd , "Running kubectl describe clusterrole..." )
613
+ err = runKubectlCommand (tw , cmd , clusterName + "/describe/clusterrole" , "describe" , "clusterrole" , "postgres-operator" )
614
+ if err != nil {
615
+ writeInfo (cmd , fmt .Sprintf ("Error running kubectl describe clusterrole: %s" , err ))
616
+ writeInfo (cmd , "Could not find clusterrole 'postgres-operator'. Looking for 'postgresoperator'..." )
617
+
618
+ // Check for the alternative spelling with 'postgresoperator'
619
+ err = runKubectlCommand (tw , cmd , clusterName + "/describe/clusterrole" , "describe" , "clusterrole" , "postgresoperator" )
620
+ if err != nil {
621
+ writeInfo (cmd , fmt .Sprintf ("Error running kubectl describe clusterrole: %s" , err ))
622
+ }
623
+ }
624
+
625
+ // Resource name is generally 'postgres-operator' but in some environments
626
+ // like Openshift it could be 'postgresoperator'
627
+ writeInfo (cmd , "Running kubectl describe clusterrolebinding..." )
628
+ err = runKubectlCommand (tw , cmd , clusterName + "/describe/clusterrolebinding" , "describe" , "clusterrolebinding" , "postgres-operator" )
629
+ if err != nil {
630
+ writeInfo (cmd , fmt .Sprintf ("Error running kubectl describe clusterrolebinding: %s" , err ))
631
+
632
+ // Check for the alternative spelling with 'postgresoperator'
633
+ writeInfo (cmd , "Could not find clusterrolebinding 'postgres-operator'. Looking for 'postgresoperator'..." )
634
+ err = runKubectlCommand (tw , cmd , clusterName + "/describe/clusterrolebinding" , "describe" , "clusterrolebinding" , "postgresoperator" )
635
+ if err != nil {
636
+ writeInfo (cmd , fmt .Sprintf ("Error running kubectl describe clusterrolebinding: %s" , err ))
637
+ }
638
+ }
639
+
640
+ writeInfo (cmd , "Running kubectl describe lease..." )
641
+ err = runKubectlCommand (tw , cmd , "operator/describe/lease" , "describe" , "lease" , "-n" , operatorNamespace )
642
+ if err != nil {
643
+ writeInfo (cmd , fmt .Sprintf ("Error running kubectl describe lease: %s" , err ))
644
+ }
645
+
646
+ err = gatherPgadminResources (config , clientset , ctx , namespace , tw , cmd )
647
+ if err != nil {
648
+ writeInfo (cmd , fmt .Sprintf ("Error gathering PGAdmin Resources: %s" , err ))
649
+ }
650
+
584
651
// Print cli output
585
652
writeInfo (cmd , "Collecting PGO CLI logs..." )
586
653
path := clusterName + "/cli.log"
@@ -598,6 +665,74 @@ Collecting PGO CLI logs...
598
665
return cmd
599
666
}
600
667
668
+ func gatherPgadminResources (config * internal.Config ,
669
+ clientset * kubernetes.Clientset ,
670
+ ctx context.Context ,
671
+ namespace string ,
672
+ tw * tar.Writer , cmd * cobra.Command ) error {
673
+
674
+ _ , pgadminClient , err := v1beta1 .NewPgadminClient (config )
675
+
676
+ if err != nil {
677
+ return err
678
+ }
679
+
680
+ pgadmins , err := pgadminClient .Namespace (namespace ).List (ctx , metav1.ListOptions {})
681
+ if err != nil {
682
+ if apierrors .IsForbidden (err ) {
683
+ writeInfo (cmd , err .Error ())
684
+ return nil
685
+ }
686
+ return err
687
+ }
688
+
689
+ if len (pgadmins .Items ) == 0 {
690
+ // If we didn't find any resources, skip
691
+ writeInfo (cmd , "Resource PGAdmin not found, skipping" )
692
+ return nil
693
+ }
694
+
695
+ // Create a buffer to generate string with the table formatted list
696
+ var buf bytes.Buffer
697
+ if err := printers .NewTablePrinter (printers.PrintOptions {}).
698
+ PrintObj (pgadmins , & buf ); err != nil {
699
+ return err
700
+ }
701
+
702
+ // Define the file name/path where the list file will be created and
703
+ // write to the tar
704
+ path := "pgadmin" + "/list"
705
+ if err := writeTar (tw , buf .Bytes (), path , cmd ); err != nil {
706
+ return err
707
+ }
708
+
709
+ for _ , obj := range pgadmins .Items {
710
+ b , err := yaml .Marshal (obj )
711
+ if err != nil {
712
+ return err
713
+ }
714
+
715
+ path := "pgadmin" + "/" + obj .GetName () + ".yaml"
716
+ if err := writeTar (tw , b , path , cmd ); err != nil {
717
+ return err
718
+ }
719
+
720
+ writeInfo (cmd , "Collecting PGAdmin pod logs..." )
721
+ err = gatherPodLogs (ctx , clientset , namespace , fmt .Sprintf ("%s=%s" , util .LabelPgadmin , obj .GetName ()), "pgadmin" , tw , cmd )
722
+ if err != nil {
723
+ writeInfo (cmd , fmt .Sprintf ("Error gathering PGAdmin pod logs: %s" , err ))
724
+ }
725
+
726
+ writeInfo (cmd , "Running kubectl describe pgadmin" )
727
+ err = runKubectlCommand (tw , cmd , "pgadmin/describe/" + obj .GetName (), "describe" , "pgadmin" , obj .GetName (), "-n" , namespace )
728
+ if err != nil {
729
+ writeInfo (cmd , fmt .Sprintf ("Error running kubectl describe pgadmin: %s" , err ))
730
+ }
731
+ }
732
+
733
+ return nil
734
+ }
735
+
601
736
func gatherPluginList (clusterName string , tw * tar.Writer , cmd * cobra.Command ) error {
602
737
ctx , cancel := context .WithTimeout (context .Background (), 30 * time .Second )
603
738
defer cancel () // Ensure the context is canceled to avoid leaks
@@ -640,6 +775,29 @@ There was an error running 'kubectl get pgupgrade'. Verify permissions and that
640
775
return nil
641
776
}
642
777
778
+ func runKubectlCommand (tw * tar.Writer , cmd * cobra.Command , path string , cmdArgs ... string ) error {
779
+ ctx , cancel := context .WithTimeout (context .Background (), 30 * time .Second )
780
+ defer cancel () // Ensure the context is canceled to avoid leaks
781
+
782
+ ex := exec .CommandContext (ctx , "kubectl" , cmdArgs ... )
783
+ msg , err := ex .Output ()
784
+
785
+ if err != nil {
786
+ msg = append (msg , err .Error ()... )
787
+ msg = append (msg , []byte (`
788
+ There was an error running the command. Verify permissions and that the resource exists.` )... )
789
+
790
+ writeInfo (cmd , fmt .Sprintf ("Error: '%s'" , msg ))
791
+ return err
792
+ }
793
+
794
+ if err := writeTar (tw , msg , path , cmd ); err != nil {
795
+ return err
796
+ }
797
+
798
+ return nil
799
+ }
800
+
643
801
// exportSizeReport defines the message displayed when a support export archive
644
802
// is created. If the size of the archive file is greater than 25MiB, an alternate
645
803
// message is displayed.
@@ -956,6 +1114,73 @@ func gatherNamespacedAPIResources(ctx context.Context,
956
1114
return nil
957
1115
}
958
1116
1117
+ // gatherCrds gathers all the CRDs with a name=pgo label
1118
+ func gatherCrds (ctx context.Context ,
1119
+ clientset * apiextensionsclientset.Clientset ,
1120
+ clusterName string ,
1121
+ tw * tar.Writer ,
1122
+ cmd * cobra.Command ,
1123
+ ) error {
1124
+ writeInfo (cmd , "Collecting CRDs..." )
1125
+
1126
+ crdList , err := clientset .ApiextensionsV1 ().CustomResourceDefinitions ().List (ctx , metav1.ListOptions {})
1127
+
1128
+ if err != nil {
1129
+ if apierrors .IsForbidden (err ) {
1130
+ writeInfo (cmd , err .Error ())
1131
+ return nil
1132
+ }
1133
+ return err
1134
+ }
1135
+
1136
+ // Get only the CRDs matching our filter
1137
+ nameFilter := "postgres-operator.crunchydata.com"
1138
+
1139
+ filteredCRDs := & apiextensionsv1.CustomResourceDefinitionList {
1140
+ Items : []apiextensionsv1.CustomResourceDefinition {},
1141
+ }
1142
+ for _ , crd := range crdList .Items {
1143
+ if strings .Contains (crd .Name , nameFilter ) {
1144
+ filteredCRDs .Items = append (filteredCRDs .Items , crd )
1145
+ }
1146
+ }
1147
+
1148
+ if len (filteredCRDs .Items ) == 0 {
1149
+ // If we didn't find any resources, skip
1150
+ writeInfo (cmd , "Resource CRDs not found, skipping" )
1151
+ return nil
1152
+ }
1153
+
1154
+ // Create a buffer to generate string with the table formatted list
1155
+ var buf bytes.Buffer
1156
+ if err := printers .NewTablePrinter (printers.PrintOptions {}).
1157
+ PrintObj (filteredCRDs , & buf ); err != nil {
1158
+ return err
1159
+ }
1160
+
1161
+ // Define the file name/path where the list file will be created and
1162
+ // write to the tar
1163
+ path := clusterName + "/" + "crds" + "/list"
1164
+ if err := writeTar (tw , buf .Bytes (), path , cmd ); err != nil {
1165
+ return err
1166
+ }
1167
+
1168
+ for _ , obj := range filteredCRDs .Items {
1169
+ b , err := yaml .Marshal (obj )
1170
+ if err != nil {
1171
+ return err
1172
+ }
1173
+
1174
+ path := clusterName + "/" + "crds" + "/" + obj .GetName () + ".yaml"
1175
+ if err := writeTar (tw , b , path , cmd ); err != nil {
1176
+ return err
1177
+ }
1178
+ }
1179
+
1180
+ return nil
1181
+
1182
+ }
1183
+
959
1184
// gatherEvents gathers all events from a namespace, selects information (based on
960
1185
// what kubectl outputs), formats the data then prints to the tar file
961
1186
func gatherEvents (ctx context.Context ,
@@ -1229,8 +1454,9 @@ func gatherPostgresLogsAndConfigs(ctx context.Context,
1229
1454
commands := []Command {
1230
1455
{path : "pg_controldata" , description : "pg_controldata" },
1231
1456
{path : "df -h /pgdata" , description : "disk free" },
1232
- {path : "du -h /pgdata" , description : "disk usage" },
1457
+ {path : "du -h /pgdata | column -t -o \" \" " , description : "disk usage" },
1233
1458
{path : "ls /pgdata/*/archive_status/*.ready | wc -l" , description : "Archive Ready File Count" },
1459
+ {path : "psql -P format=wrapped -P columns=180 -c \" select name,setting,source,sourcefile,sourceline FROM pg_settings order by 1\" " , description : "PG Settings" },
1234
1460
}
1235
1461
1236
1462
var buf bytes.Buffer
@@ -1670,6 +1896,10 @@ func gatherPodLogs(ctx context.Context,
1670
1896
}
1671
1897
1672
1898
for _ , pod := range pods .Items {
1899
+ err = runKubectlCommand (tw , cmd , rootDir + "/describe/" + "pods/" + pod .GetName (), "describe" , "pods" , pod .GetName (), "-n" , namespace )
1900
+ if err != nil {
1901
+ writeInfo (cmd , fmt .Sprintf ("Error running kubectl describe pods: %s" , err ))
1902
+ }
1673
1903
containers := pod .Spec .Containers
1674
1904
containers = append (containers , pod .Spec .InitContainers ... )
1675
1905
for _ , container := range containers {
0 commit comments