├── .claude ├── .manifest ├── agents │ ├── architecture-patterns.md │ ├── ci-developer.md │ ├── codebase-analyzer.md │ ├── codebase-locator.md │ ├── codebase-pattern-finder.md │ ├── frontend-developer.md │ ├── go-developer.md │ ├── preflight-check-writer.md │ ├── project-builder.md │ ├── proposal-needed.md │ ├── proposal-writer.md │ ├── proposals-analyzer.md │ ├── proposals-locator.md │ ├── researcher.md │ ├── shortcut.md │ ├── testing.md │ └── web-search-researcher.md └── commands │ ├── implement.md │ └── proposal.md ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_epic.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── actions │ └── setup-go │ │ └── action.yml ├── dependabot.yml └── workflows │ ├── build-test-deploy.yaml │ ├── build-test.yaml │ ├── regression-test.yaml │ └── release.yaml ├── .gitignore ├── CONTRIBUTING.md ├── Cron-Job-Support-Bundles-PRD.md ├── LICENSE ├── Makefile ├── PROJECT ├── README.md ├── cmd ├── analyze │ ├── cli │ │ ├── root.go │ │ └── run.go │ └── main.go ├── internal │ └── util │ │ ├── profilers.go │ │ └── version.go ├── preflight │ ├── cli │ │ ├── convert.go │ │ ├── docs.go │ │ ├── lint.go │ │ ├── oci_fetch.go │ │ ├── root.go │ │ └── template.go │ └── main.go └── troubleshoot │ ├── cli │ ├── analyze.go │ ├── auto_discovery.go │ ├── auto_discovery_test.go │ ├── diff.go │ ├── diff_test.go │ ├── discovery_config.go │ ├── discovery_config_test.go │ ├── interactive_results.go │ ├── lint.go │ ├── redact.go │ ├── root.go │ ├── run.go │ ├── run_test.go │ ├── schedule.go │ └── upload.go │ └── main.go ├── config ├── crds │ ├── troubleshoot.replicated.com_analyzerjobs.yaml │ ├── troubleshoot.replicated.com_analyzers.yaml │ ├── troubleshoot.replicated.com_collectorjobs.yaml │ ├── troubleshoot.replicated.com_collectors.yaml │ ├── troubleshoot.replicated.com_preflightjobs.yaml │ ├── troubleshoot.replicated.com_preflights.yaml │ ├── troubleshoot.replicated.com_redactors.yaml │ ├── troubleshoot.replicated.com_supportbundles.yaml │ ├── troubleshoot.sh_analyzers.yaml │ ├── troubleshoot.sh_collectors.yaml │ ├── troubleshoot.sh_hostcollectors.yaml │ ├── troubleshoot.sh_hostpreflights.yaml │ ├── troubleshoot.sh_preflights.yaml │ ├── troubleshoot.sh_redactors.yaml │ ├── troubleshoot.sh_remotecollectors.yaml │ └── troubleshoot.sh_supportbundles.yaml └── samples │ ├── troubleshoot_v1beta2_analyzer.yaml │ ├── troubleshoot_v1beta2_collector.yaml │ └── troubleshoot_v1beta2_preflight.yaml ├── deploy ├── .goreleaser.yaml ├── Dockerfile.troubleshoot ├── krew │ ├── preflight.yaml │ └── support-bundle.yaml └── rbac │ └── preflight-clusterrole.yaml ├── docs ├── Person-2-PRD.md ├── arch │ ├── adr-001-ipaddress-redaction.md │ ├── adr-002-mergeable-preflights.md │ └── readme.md ├── design │ ├── design-principles.md │ ├── proposal-concurrent-collectors.md │ ├── proposal-consolidate-collection.md │ ├── proposal-mergeable-spec.md │ ├── proposal-sbctl-integration.md │ ├── proposal-standard-interface.md │ ├── proposal-updateable-specs.md │ └── template.md ├── preflight.md ├── preflight_docs.md ├── preflight_oci-fetch.md ├── preflight_template.md ├── preflight_version.md ├── support-bundle.md ├── support-bundle_analyze.md ├── support-bundle_diff.md ├── support-bundle_redact.md ├── support-bundle_version.md └── v1beta3-guide.md ├── examples ├── collect │ ├── host │ │ ├── all-collectors.yaml │ │ ├── all-kubernetes-collectors.yaml │ │ ├── block-devices.yaml │ │ ├── certificate.yaml │ │ ├── cpu.yaml │ │ ├── default.yaml │ │ ├── disk-usage.yaml │ │ ├── filesystem-performance.yaml │ │ ├── http-load-balancer.yaml │ │ ├── http.yaml │ │ ├── ipv4-interfaces.yaml │ │ ├── kernel-modules.yaml │ │ ├── memory.yaml │ │ ├── ntp.yaml │ │ ├── run-and-save-output.yaml │ │ ├── sample.yaml │ │ ├── sysctl.yaml │ │ ├── tcp-connect.yaml │ │ ├── tcp-load-balancer.yaml │ │ ├── tcp-port.yaml │ │ ├── timezone.yaml │ │ └── udp-port.yaml │ ├── remote │ │ ├── block-devices.yaml │ │ ├── certificate.yaml │ │ ├── cpu.yaml │ │ ├── disk-usage.yaml │ │ ├── filesystem-performance.yaml │ │ ├── http-load-balancer.yaml │ │ ├── http.yaml │ │ ├── ipv4-interfaces.yaml │ │ ├── kernel-modules.yaml │ │ ├── memory.yaml │ │ ├── ntp.yaml │ │ ├── sample.yaml │ │ ├── tcp-connect.yaml │ │ ├── tcp-load-balancer.yaml │ │ ├── tcp-port.yaml │ │ ├── timezone.yaml │ │ └── udp-port.yaml │ ├── v1beta3-bundle-secrets.yaml │ └── v1beta3-secret.yaml ├── preflight │ ├── all-analyzers-v1beta2.yaml │ ├── complex-v1beta3.yaml │ ├── e2e.yaml │ ├── host │ │ ├── block-devices.yaml │ │ ├── certificate.yaml │ │ ├── cpu.yaml │ │ ├── disk-usage.yaml │ │ ├── filesystem-performance.yaml │ │ ├── host-os.yaml │ │ ├── http-load-balancer.yaml │ │ ├── http.yaml │ │ ├── ipv4-interfaces.yaml │ │ ├── kernel-modules.yaml │ │ ├── memory.yaml │ │ ├── ntp.yaml │ │ ├── sample.yaml │ │ ├── subnet-available.yaml │ │ ├── subnet-contains-ip.yaml │ │ ├── sysctl.yaml │ │ ├── system-packages.yaml │ │ ├── tcp-connect.yaml │ │ ├── tcp-load-balancer.yaml │ │ ├── tcp-port.yaml │ │ ├── timezone.yaml │ │ └── udp-port.yaml │ ├── mssql.yaml │ ├── node-resources.yaml │ ├── postgres.yaml │ ├── redis.yaml │ ├── remote │ │ ├── block-devices.yaml │ │ ├── certificate.yaml │ │ ├── cpu.yaml │ │ ├── disk-usage.yaml │ │ ├── filesystem-performance.yaml │ │ ├── http-load-balancer.yaml │ │ ├── http.yaml │ │ ├── ipv4-interfaces.yaml │ │ ├── kernel-modules.yaml │ │ ├── memory.yaml │ │ ├── ntp.yaml │ │ ├── sample.yaml │ │ ├── tcp-connect.yaml │ │ ├── tcp-load-balancer.yaml │ │ ├── tcp-port.yaml │ │ ├── timezone.yaml │ │ └── udp-port.yaml │ ├── sample-preflight.yaml │ ├── simple-v1beta3.yaml │ ├── text-analyze.yaml │ ├── values-complex-full.yaml │ ├── values-complex-small.yaml │ ├── values-simple.yaml │ ├── values-v1beta3-1.yaml │ ├── values-v1beta3-2.yaml │ └── values-v1beta3-3.yaml ├── redact │ └── e2e.yaml ├── sdk │ └── helm-template │ │ ├── README.md │ │ ├── go.mod │ │ ├── go.sum │ │ ├── main.go │ │ └── mychart-0.1.0.tgz ├── support-bundle │ ├── db-collector.yaml │ ├── e2e.yaml │ ├── sample-analyzers.yaml │ ├── sample-collect-entire-configmap.yaml │ ├── sample-collectors.yaml │ ├── sample-supportbundle.yaml │ ├── secret-collectors.yaml │ ├── v1beta3 │ │ ├── README.md │ │ ├── configmap-example.yaml │ │ ├── cross-namespace-secrets.yaml │ │ ├── multiple-databases.yaml │ │ ├── optional-secrets.yaml │ │ ├── postgres-with-secret.yaml │ │ └── postgres-with-tls.yaml │ └── weave-analyzer.yaml ├── test-error-messages │ ├── helm-builtins-v1beta3.yaml │ ├── invalid-collectors-analyzers.yaml │ ├── invalid-yaml-v1beta3.yaml │ ├── missing-apiversion-v1beta3.yaml │ ├── missing-metadata-v1beta3.yaml │ ├── no-analyzers-v1beta3.yaml │ ├── simple-no-template-v1beta3.yaml │ ├── support-bundle-no-collectors-v1beta3.yaml │ ├── support-bundle-valid-v1beta3.yaml │ ├── valid-v1beta3.yaml │ ├── values-empty.yaml │ ├── values-helm-builtins.yaml │ └── wrong-apiversion-v1beta3.yaml └── troubleshoot │ ├── longhorn.yaml │ ├── sample-analyzers.yaml │ ├── sample-troubleshoot.yaml │ └── sysctl.yaml ├── go.mod ├── go.sum ├── hack └── boilerplate.go.txt ├── internal ├── specs │ ├── configmaps.go │ ├── configmaps_test.go │ ├── secrets.go │ ├── secrets_test.go │ ├── specs.go │ └── specs_test.go ├── testutils │ └── utils.go ├── traces │ ├── exporter.go │ ├── exporter_test.go │ └── otel.go └── util │ ├── util.go │ └── util_test.go ├── pkg ├── analyze │ ├── agents │ │ ├── hosted │ │ │ ├── hosted_agent.go │ │ │ └── hosted_agent_test.go │ │ ├── local │ │ │ ├── local_agent.go │ │ │ └── local_agent_test.go │ │ └── ollama │ │ │ ├── ollama_agent.go │ │ │ └── ollama_agent_test.go │ ├── agents_integration_test.go │ ├── analyzer.go │ ├── analyzer_test.go │ ├── artifacts │ │ ├── artifacts.go │ │ ├── artifacts_test.go │ │ ├── formatters.go │ │ ├── generators.go │ │ └── validators.go │ ├── ceph.go │ ├── ceph_test.go │ ├── certificates.go │ ├── certificates_test.go │ ├── cluster_container_statuses.go │ ├── cluster_container_statuses_test.go │ ├── cluster_pod_statuses.go │ ├── cluster_pod_statuses_test.go │ ├── cluster_version.go │ ├── cluster_version_test.go │ ├── collected_contents.go │ ├── collected_contents_test.go │ ├── common_status.go │ ├── comparison.go │ ├── comparison_test.go │ ├── configmap.go │ ├── configmap_test.go │ ├── container_runtime.go │ ├── container_runtime_test.go │ ├── crd.go │ ├── crd_test.go │ ├── data_test.go │ ├── database_shared.go │ ├── database_shared_test.go │ ├── deployment_status.go │ ├── deployment_status_test.go │ ├── distribution.go │ ├── distribution_test.go │ ├── download.go │ ├── download_test.go │ ├── engine.go │ ├── engine_test.go │ ├── event.go │ ├── event_test.go │ ├── files │ │ ├── deployments │ │ │ ├── default.json │ │ │ ├── kube-system.json │ │ │ └── monitoring.json │ │ ├── events │ │ │ ├── message-container-creating-failed-mount.json │ │ │ ├── message-image-pull-fail.json │ │ │ └── message-pod-init-crashloop-backoff.json │ │ ├── jobs │ │ │ ├── projectcontour.json │ │ │ └── test.json │ │ ├── nodes.json │ │ ├── pods │ │ │ ├── default-unhealthy.json │ │ │ ├── default.json │ │ │ ├── message-container-creating-failed-mount.json │ │ │ ├── message-image-pull-fail.json │ │ │ ├── message-oomkill-pod.json │ │ │ ├── message-pending-node-affinity.json │ │ │ ├── message-pending-pod-resources.json │ │ │ ├── message-pod-crashloop-backoff.json │ │ │ ├── message-pod-init-crashloop-backoff.json │ │ │ ├── other-unhealthy.json │ │ │ └── other.json │ │ ├── replicasets │ │ │ ├── default.json │ │ │ └── rook-ceph.json │ │ ├── statefulsets │ │ │ ├── default.json │ │ │ └── monitoring.json │ │ └── support-bundle │ │ │ └── cluster-resources │ │ │ ├── configmaps │ │ │ └── kube-public.json │ │ │ ├── deployments │ │ │ ├── default.json │ │ │ ├── kube-system.json │ │ │ └── monitoring.json │ │ │ ├── jobs │ │ │ ├── projectcontour.json │ │ │ └── test.json │ │ │ ├── namespaces.json │ │ │ ├── nodes.json │ │ │ ├── pvcs │ │ │ └── default.json │ │ │ ├── replicasets │ │ │ ├── default.json │ │ │ └── rook-ceph.json │ │ │ └── statefulsets │ │ │ ├── default.json │ │ │ └── monitoring.json │ ├── generators │ │ ├── generator.go │ │ └── generator_test.go │ ├── goldpinger.go │ ├── goldpinger_test.go │ ├── host_analyzer.go │ ├── host_analyzer_test.go │ ├── host_block_devices.go │ ├── host_block_devices_test.go │ ├── host_certificate.go │ ├── host_certificate_test.go │ ├── host_certificates_collection.go │ ├── host_certificates_collection_test.go │ ├── host_cpu.go │ ├── host_cpu_test.go │ ├── host_disk_usage.go │ ├── host_disk_usage_test.go │ ├── host_filesystem_performance.go │ ├── host_filesystem_performance_test.go │ ├── host_http.go │ ├── host_http_test.go │ ├── host_httploadbalancer.go │ ├── host_httploadbalancer_test.go │ ├── host_ipv4interfaces.go │ ├── host_ipv4interfaces_test.go │ ├── host_json_compare.go │ ├── host_kernel_configs.go │ ├── host_kernel_configs_test.go │ ├── host_kernel_modules.go │ ├── host_kernel_modules_test.go │ ├── host_memory.go │ ├── host_memory_test.go │ ├── host_network_namespace_connectivity.go │ ├── host_os_info.go │ ├── host_os_info_test.go │ ├── host_services.go │ ├── host_services_test.go │ ├── host_subnetavailable.go │ ├── host_subnetavailable_test.go │ ├── host_subnetcontainsip.go │ ├── host_subnetcontainsip_test.go │ ├── host_sysctl.go │ ├── host_sysctl_test.go │ ├── host_system_packages.go │ ├── host_system_packages_test.go │ ├── host_tcp_connect.go │ ├── host_tcp_connect_test.go │ ├── host_tcploadbalancer.go │ ├── host_tcploadbalancer_test.go │ ├── host_tcpportstatus.go │ ├── host_tcpportstatus_test.go │ ├── host_text_analyze.go │ ├── host_time.go │ ├── host_time_test.go │ ├── host_udpportstatus.go │ ├── host_udpportstatus_test.go │ ├── http_analyze.go │ ├── image_pull_secret.go │ ├── ingress.go │ ├── job_status.go │ ├── job_status_test.go │ ├── json_compare.go │ ├── json_compare_test.go │ ├── k8s_node_metrics.go │ ├── k8s_node_metrics_test.go │ ├── kube_resource.go │ ├── kube_resource_test.go │ ├── longhorn.go │ ├── longhorn_test.go │ ├── mssql.go │ ├── mssql_test.go │ ├── mysql.go │ ├── node_resources.go │ ├── node_resources_test.go │ ├── ollama_helper.go │ ├── postgres.go │ ├── redis.go │ ├── registry.go │ ├── replicaset_status.go │ ├── replicaset_status_test.go │ ├── secret.go │ ├── secret_test.go │ ├── statefulset_status.go │ ├── statefulset_status_test.go │ ├── storage_class.go │ ├── sysctl.go │ ├── sysctl_test.go │ ├── text_analyze.go │ ├── text_analyze_test.go │ ├── types │ │ └── restic_types.go │ ├── velero.go │ ├── velero_test.go │ ├── weave.go │ ├── weave_test.go │ ├── yaml_compare.go │ └── yaml_compare_test.go ├── apis │ ├── addtoscheme_troubleshoot_v1beta1.go │ ├── addtoscheme_troubleshoot_v1beta2.go │ ├── apis.go │ └── troubleshoot │ │ ├── group.go │ │ ├── v1beta1 │ │ ├── analyzer_shared.go │ │ ├── analyzer_types.go │ │ ├── collector_shared.go │ │ ├── collector_types.go │ │ ├── doc.go │ │ ├── preflight_types.go │ │ ├── redact_shared.go │ │ ├── redact_types.go │ │ ├── register.go │ │ ├── supportbundle_types.go │ │ ├── version.go │ │ └── zz_generated.deepcopy.go │ │ ├── v1beta2 │ │ ├── analyzer_shared.go │ │ ├── analyzer_types.go │ │ ├── collector_interfaces.go │ │ ├── collector_shared.go │ │ ├── collector_types.go │ │ ├── doc.go │ │ ├── host_collector_types.go │ │ ├── hostanalyzer_shared.go │ │ ├── hostcollector_shared.go │ │ ├── hostpreflight_types.go │ │ ├── outcome.go │ │ ├── preflight_types.go │ │ ├── redact_shared.go │ │ ├── redact_types.go │ │ ├── register.go │ │ ├── remote_collector_shared.go │ │ ├── remote_collector_types.go │ │ ├── supportbundle_types.go │ │ ├── version.go │ │ └── zz_generated.deepcopy.go │ │ └── v1beta3 │ │ ├── collector_shared.go │ │ ├── converter.go │ │ ├── converter_test.go │ │ ├── doc.go │ │ ├── register.go │ │ ├── resolver.go │ │ ├── resolver_test.go │ │ ├── string_or_valuefrom.go │ │ ├── supportbundle_types.go │ │ └── zz_generated.deepcopy.go ├── client │ └── troubleshootclientset │ │ ├── clientset.go │ │ ├── doc.go │ │ ├── fake │ │ ├── clientset_generated.go │ │ ├── doc.go │ │ └── register.go │ │ ├── scheme │ │ ├── doc.go │ │ └── register.go │ │ └── typed │ │ └── troubleshoot │ │ ├── v1beta1 │ │ ├── analyzer.go │ │ ├── collector.go │ │ ├── doc.go │ │ ├── fake │ │ │ ├── doc.go │ │ │ ├── fake_analyzer.go │ │ │ ├── fake_collector.go │ │ │ ├── fake_preflight.go │ │ │ ├── fake_redactor.go │ │ │ ├── fake_supportbundle.go │ │ │ └── fake_troubleshoot_client.go │ │ ├── generated_expansion.go │ │ ├── preflight.go │ │ ├── redactor.go │ │ ├── supportbundle.go │ │ └── troubleshoot_client.go │ │ └── v1beta2 │ │ ├── analyzer.go │ │ ├── collector.go │ │ ├── doc.go │ │ ├── fake │ │ ├── doc.go │ │ ├── fake_analyzer.go │ │ ├── fake_collector.go │ │ ├── fake_hostcollector.go │ │ ├── fake_hostpreflight.go │ │ ├── fake_preflight.go │ │ ├── fake_redactor.go │ │ ├── fake_remotecollector.go │ │ ├── fake_supportbundle.go │ │ └── fake_troubleshoot_client.go │ │ ├── generated_expansion.go │ │ ├── hostcollector.go │ │ ├── hostpreflight.go │ │ ├── preflight.go │ │ ├── redactor.go │ │ ├── remotecollector.go │ │ ├── supportbundle.go │ │ └── troubleshoot_client.go ├── collect │ ├── autodiscovery │ │ ├── discoverer.go │ │ ├── discoverer_test.go │ │ ├── interfaces.go │ │ ├── kots_detector.go │ │ ├── namespace_scanner.go │ │ ├── rbac_checker.go │ │ ├── rbac_checker_test.go │ │ ├── rbac_reporter.go │ │ └── resource_expander.go │ ├── ceph.go │ ├── certificates.go │ ├── certificates_test.go │ ├── cluster_info.go │ ├── cluster_info_test.go │ ├── cluster_resources.go │ ├── cluster_resources_test.go │ ├── collect.go │ ├── collect_test.go │ ├── collectd.go │ ├── collector.go │ ├── collector_test.go │ ├── configmap.go │ ├── configmap_test.go │ ├── copy.go │ ├── copy_from_host.go │ ├── copy_from_host_test.go │ ├── data.go │ ├── database_shared.go │ ├── dns.go │ ├── dns_test.go │ ├── etcd.go │ ├── etcd_test.go │ ├── exec.go │ ├── goldpinger.go │ ├── helm.go │ ├── helm_test.go │ ├── host_block_device.go │ ├── host_block_device_test.go │ ├── host_certificate.go │ ├── host_certificates_collection.go │ ├── host_certificates_collection_test.go │ ├── host_cgroup.go │ ├── host_cgroup_linux.go │ ├── host_cgroup_others.go │ ├── host_cgroup_test.go │ ├── host_collector.go │ ├── host_collector_test.go │ ├── host_copy.go │ ├── host_copy_test.go │ ├── host_cpu.go │ ├── host_cpu_test.go │ ├── host_disk_usage.go │ ├── host_disk_usage_test.go │ ├── host_dns.go │ ├── host_dns_test.go │ ├── host_filesystem_performance.go │ ├── host_filesystem_performance_darwin.go │ ├── host_filesystem_performance_linux.go │ ├── host_filesystem_performance_test.go │ ├── host_filesystem_performance_windows.go │ ├── host_http.go │ ├── host_httploadbalancer.go │ ├── host_ipv4interfaces.go │ ├── host_journald.go │ ├── host_journald_test.go │ ├── host_kernel_configs.go │ ├── host_kernel_configs_test.go │ ├── host_kernel_modules.go │ ├── host_kernel_modules_test.go │ ├── host_memory.go │ ├── host_memory_test.go │ ├── host_network.go │ ├── host_network_namespace_connectivity.go │ ├── host_network_test.go │ ├── host_os_info.go │ ├── host_os_info_test.go │ ├── host_run.go │ ├── host_run_test.go │ ├── host_services.go │ ├── host_subnetavailable.go │ ├── host_subnetavailable_test.go │ ├── host_sysctl.go │ ├── host_sysctl_test.go │ ├── host_system_package.go │ ├── host_tcp_connect.go │ ├── host_tcploadbalancer.go │ ├── host_tcpportstatus.go │ ├── host_time.go │ ├── host_udpportstatus.go │ ├── host_udpportstatus_test.go │ ├── http.go │ ├── http_test.go │ ├── image_facts.go │ ├── images │ │ ├── collector.go │ │ ├── collector_test.go │ │ ├── digest_resolver.go │ │ ├── digest_resolver_test.go │ │ ├── facts_builder.go │ │ ├── facts_builder_test.go │ │ ├── integration.go │ │ ├── manifest_parser.go │ │ ├── manifest_parser_test.go │ │ ├── registry_client.go │ │ ├── types.go │ │ ├── utils.go │ │ └── utils_test.go │ ├── interfaces.go │ ├── interfaces_test.go │ ├── k8s_metrics.go │ ├── k8s_metrics_test.go │ ├── k8s_node_metrics.go │ ├── k8s_node_metrics_test.go │ ├── load.go │ ├── logs.go │ ├── logs_test.go │ ├── longhorn.go │ ├── longhorn_test.go │ ├── mssql.go │ ├── mssql_test.go │ ├── mysql.go │ ├── postgres.go │ ├── postgres_test.go │ ├── rbac.go │ ├── redact.go │ ├── redis.go │ ├── redis_test.go │ ├── registry.go │ ├── registry_test.go │ ├── remote_collector.go │ ├── remote_collector_test.go │ ├── result.go │ ├── result_test.go │ ├── run.go │ ├── run_daemonset.go │ ├── run_daemonset_test.go │ ├── run_pod.go │ ├── run_pod_test.go │ ├── runner.go │ ├── secret.go │ ├── secret_test.go │ ├── sonobuoy_results.go │ ├── sysctl.go │ ├── util.go │ └── util_test.go ├── constants │ └── constants.go ├── convert │ ├── errors.go │ ├── output.go │ ├── supportbundle.go │ ├── templates.go │ ├── templates_test.go │ └── v1beta3.go ├── debug │ └── log.go ├── docrewrite │ ├── v1beta2.go │ └── v1beta2_test.go ├── httputil │ └── config.go ├── interfaceutils │ └── interfaceutils.go ├── k8sutil │ ├── auth.go │ ├── config.go │ ├── discovery │ │ ├── discovery.go │ │ └── discovery_test.go │ ├── node.go │ ├── pod.go │ ├── pod_test.go │ └── portforward.go ├── lint │ ├── fixes.go │ ├── format.go │ ├── helpers.go │ ├── lint.go │ ├── lint_test.go │ ├── templating.go │ ├── types.go │ └── validators.go ├── loader │ ├── loader.go │ ├── loader_test.go │ └── loader_v1beta3_test.go ├── logger │ └── logger.go ├── longhorn │ ├── apis │ │ └── longhorn │ │ │ ├── register.go │ │ │ └── v1beta1 │ │ │ ├── doc.go │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ └── zz_generated.deepcopy.go │ ├── client │ │ ├── clientset │ │ │ └── versioned │ │ │ │ ├── clientset.go │ │ │ │ ├── doc.go │ │ │ │ ├── fake │ │ │ │ ├── clientset_generated.go │ │ │ │ ├── doc.go │ │ │ │ └── register.go │ │ │ │ ├── scheme │ │ │ │ ├── doc.go │ │ │ │ └── register.go │ │ │ │ └── typed │ │ │ │ └── longhorn │ │ │ │ └── v1beta1 │ │ │ │ ├── backingimage.go │ │ │ │ ├── backingimagedatasource.go │ │ │ │ ├── backingimagemanager.go │ │ │ │ ├── backup.go │ │ │ │ ├── backuptarget.go │ │ │ │ ├── backupvolume.go │ │ │ │ ├── doc.go │ │ │ │ ├── engine.go │ │ │ │ ├── engineimage.go │ │ │ │ ├── fake │ │ │ │ ├── doc.go │ │ │ │ ├── fake_backingimage.go │ │ │ │ ├── fake_backingimagedatasource.go │ │ │ │ ├── fake_backingimagemanager.go │ │ │ │ ├── fake_backup.go │ │ │ │ ├── fake_backuptarget.go │ │ │ │ ├── fake_backupvolume.go │ │ │ │ ├── fake_engine.go │ │ │ │ ├── fake_engineimage.go │ │ │ │ ├── fake_instancemanager.go │ │ │ │ ├── fake_longhorn_client.go │ │ │ │ ├── fake_node.go │ │ │ │ ├── fake_recurringjob.go │ │ │ │ ├── fake_replica.go │ │ │ │ ├── fake_setting.go │ │ │ │ ├── fake_sharemanager.go │ │ │ │ └── fake_volume.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── instancemanager.go │ │ │ │ ├── longhorn_client.go │ │ │ │ ├── node.go │ │ │ │ ├── recurringjob.go │ │ │ │ ├── replica.go │ │ │ │ ├── setting.go │ │ │ │ ├── sharemanager.go │ │ │ │ └── volume.go │ │ ├── informers │ │ │ └── externalversions │ │ │ │ ├── factory.go │ │ │ │ ├── generic.go │ │ │ │ ├── internalinterfaces │ │ │ │ └── factory_interfaces.go │ │ │ │ └── longhorn │ │ │ │ ├── interface.go │ │ │ │ └── v1beta1 │ │ │ │ ├── backingimage.go │ │ │ │ ├── backingimagedatasource.go │ │ │ │ ├── backingimagemanager.go │ │ │ │ ├── backup.go │ │ │ │ ├── backuptarget.go │ │ │ │ ├── backupvolume.go │ │ │ │ ├── engine.go │ │ │ │ ├── engineimage.go │ │ │ │ ├── instancemanager.go │ │ │ │ ├── interface.go │ │ │ │ ├── node.go │ │ │ │ ├── recurringjob.go │ │ │ │ ├── replica.go │ │ │ │ ├── setting.go │ │ │ │ ├── sharemanager.go │ │ │ │ └── volume.go │ │ └── listers │ │ │ └── longhorn │ │ │ └── v1beta1 │ │ │ ├── backingimage.go │ │ │ ├── backingimagedatasource.go │ │ │ ├── backingimagemanager.go │ │ │ ├── backup.go │ │ │ ├── backuptarget.go │ │ │ ├── backupvolume.go │ │ │ ├── engine.go │ │ │ ├── engineimage.go │ │ │ ├── expansion_generated.go │ │ │ ├── instancemanager.go │ │ │ ├── node.go │ │ │ ├── recurringjob.go │ │ │ ├── replica.go │ │ │ ├── setting.go │ │ │ ├── sharemanager.go │ │ │ └── volume.go │ ├── types │ │ ├── condition.go │ │ ├── data_source.go │ │ ├── deepcopy.go │ │ ├── deploy.go │ │ ├── resource.go │ │ ├── setting.go │ │ └── types.go │ └── util │ │ ├── cmd.go │ │ ├── http.go │ │ ├── iscsi.go │ │ ├── iscsi_windows.go │ │ ├── k8s.go │ │ ├── time.go │ │ ├── time_test.go │ │ ├── util.go │ │ └── util_test.go ├── multitype │ ├── boolstring.go │ ├── boolstring_test.go │ ├── quotedbool.go │ └── quotedbool_test.go ├── namespaces │ ├── errors.go │ ├── errors_test.go │ ├── interface-pair.go │ ├── interface-pair_test.go │ ├── managed-namespace.go │ ├── managed-namespace_unsupported.go │ ├── namespace-handler.go │ ├── namespace-pinger.go │ ├── netlink-handler.go │ ├── network-namespace.go │ ├── network-namespace_test.go │ └── options.go ├── oci │ ├── pull.go │ └── pull_test.go ├── preflight │ ├── analyze.go │ ├── collect.go │ ├── concat.go │ ├── flags.go │ ├── flags_test.go │ ├── helm_renderer.go │ ├── interactive_results.go │ ├── output_to_file.go │ ├── read_specs.go │ ├── read_specs_test.go │ ├── run.go │ ├── run_test.go │ ├── template.go │ ├── template_test.go │ ├── text_results.go │ ├── types.go │ ├── upload_results.go │ ├── util.go │ ├── util_test.go │ ├── validate_specs.go │ ├── validate_specs_test.go │ └── values_defaults.go ├── redact │ ├── literal.go │ ├── multi_line.go │ ├── multi_line_test.go │ ├── redact.go │ ├── redact_test.go │ ├── single_line.go │ ├── single_line_test.go │ ├── tokenizer.go │ ├── tokenizer_test.go │ ├── yaml.go │ └── yaml_test.go ├── schedule │ ├── cli.go │ ├── daemon.go │ ├── job.go │ └── schedule_test.go ├── specs │ ├── configmaps.go │ ├── secrets.go │ └── specs.go ├── supportbundle │ ├── aftercollection.go │ ├── collect.go │ ├── extract_license.go │ ├── load.go │ ├── load_test.go │ ├── parse.go │ ├── parse_test.go │ ├── rbac.go │ ├── supportbundle.go │ ├── supportbundle_test.go │ ├── test │ │ ├── bad-upstream-uri-spec.yaml │ │ ├── bad-uri-spec.yaml │ │ ├── completebundle.yaml │ │ ├── supportbundle1.yaml │ │ ├── supportbundle2.yaml │ │ ├── uri-spec.yaml │ │ └── velero.yaml │ ├── types │ │ └── types.go │ └── upload.go ├── types │ └── types.go ├── updater │ ├── pkgmgr │ │ ├── homebrew.go │ │ ├── krew.go │ │ └── pkgmgr.go │ └── updater.go └── version │ ├── build.go │ ├── run.go │ └── version.go ├── roadmap.md ├── scheduled-job-daemon-explained.md ├── schemas ├── analyzer-troubleshoot-v1beta1.json ├── analyzer-troubleshoot-v1beta2.json ├── collector-troubleshoot-v1beta1.json ├── collector-troubleshoot-v1beta2.json ├── preflight-troubleshoot-v1beta1.json ├── preflight-troubleshoot-v1beta2.json ├── redactor-troubleshoot-v1beta1.json ├── redactor-troubleshoot-v1beta2.json ├── supportbundle-troubleshoot-v1beta1.json └── supportbundle-troubleshoot-v1beta2.json ├── scripts ├── compare_bundles.py ├── compare_rules.yaml ├── generate_summary.py ├── host-preflight-integration-test.sh ├── initialize-sbom-build.sh └── update_baselines.sh ├── test-auto-collectors.sh ├── test ├── .gitignore ├── README.md ├── baselines │ ├── README.md │ ├── preflight-v1beta2 │ │ └── baseline.tar.gz │ ├── preflight-v1beta3 │ │ └── baseline.tar.gz │ └── supportbundle │ │ └── baseline.tar.gz ├── e2e │ ├── preflight │ │ ├── host_local_collector_e2e_test.go │ │ ├── main_e2e_test.go │ │ └── spec │ │ │ └── localHostCollectors.yaml │ └── support-bundle │ │ ├── cluster_pod_statuses_e2e_test.go │ │ ├── cluster_resources_e2e_test.go │ │ ├── goldpinger_collector_e2e_test.go │ │ ├── helm_collector_e2e_test.go │ │ ├── hostOS_remote_collector_e2e_test.go │ │ ├── host_local_collector_e2e_test.go │ │ ├── host_remote_collector_e2e_test.go │ │ ├── main_e2e_test.go │ │ ├── sonobuoy_e2e_test.go │ │ ├── spec │ │ ├── clusterResources.yaml │ │ ├── helm.yaml │ │ ├── hostOSRemoteCollector.yaml │ │ ├── localHostCollectors.yaml │ │ ├── pod.yaml │ │ ├── remoteHostCollectors.yaml │ │ └── sonobuoy.yaml │ │ └── testdata │ │ ├── charts │ │ └── nginx-15.2.0.tgz │ │ └── helm-values.yaml ├── validate-preflight-e2e.sh └── validate-support-bundle-e2e.sh └── testdata ├── db ├── ca.pem ├── client-key.pem └── client.pem ├── filesystem_performance_preflight.yaml ├── goldpinger ├── checkall-one-pod.json ├── checkall-success.json └── checkall-with-error.json ├── https-proxy-replicated-app.yaml ├── kurl_preflights.yaml ├── preflightspec ├── troubleshoot_v1beta2_host_preflight_validate_empty_analyzers_gotest.yaml ├── troubleshoot_v1beta2_host_preflight_validate_empty_collectors_gotest.yaml ├── troubleshoot_v1beta2_host_preflight_validate_excluded_analyzers_gotest.yaml ├── troubleshoot_v1beta2_host_preflight_validate_excluded_collectors_gotest.yaml ├── troubleshoot_v1beta2_preflight_gotest.yaml ├── troubleshoot_v1beta2_preflight_secret_gotest.yaml ├── troubleshoot_v1beta2_preflight_secret_stringdata_gotest.yaml ├── troubleshoot_v1beta2_preflight_validate_empty_analyzers_gotest.yaml ├── troubleshoot_v1beta2_preflight_validate_empty_collectors_gotest.yaml ├── troubleshoot_v1beta2_preflight_validate_excluded_all_default_collectors_gotest.yaml ├── troubleshoot_v1beta2_preflight_validate_excluded_all_non_default_collectors_gotest.yaml ├── troubleshoot_v1beta2_preflight_validate_excluded_analyzers_gotest.yaml ├── troubleshoot_v1beta2_preflight_validate_excluded_one_default_collectors_gotest.yaml └── troubleshoot_v1beta2_preflight_validate_spec_with_upload_results_gotest.yaml ├── supportbundle ├── configmaps.yaml ├── empty.yaml ├── extracted-sb │ ├── cluster-resources │ │ └── pods │ │ │ └── logs │ │ │ └── default │ │ │ └── static-hi │ │ │ └── static-hi.log │ └── static-hi.log ├── labelled-bundle-unlabelled-redactor │ ├── redact-spec-1.yaml │ └── sb-spec-1.yaml ├── labelled-specs │ ├── 0-ns.yaml │ ├── redact-spec-1.yaml │ ├── redact-spec-2.yaml │ ├── redact-spec-3.yaml │ ├── redact-spec-4.yaml │ ├── sb-spec-1.yaml │ ├── sb-spec-2.yaml │ ├── sb-spec-3.yaml │ └── sb-spec-4.yaml ├── missing-version.tar.gz └── support-bundle.tar.gz └── yamldocs ├── helm-template.yaml ├── multidoc-spec-1.yaml ├── multidoc-spec-2.yaml ├── multidoc-spec-3.yaml └── multidoc-spec-with-invalids.yaml /.claude/.manifest: -------------------------------------------------------------------------------- 1 | agents/architecture-patterns.md 2 | agents/ci-developer.md 3 | agents/codebase-analyzer.md 4 | agents/codebase-locator.md 5 | agents/codebase-pattern-finder.md 6 | agents/frontend-developer.md 7 | agents/go-developer.md 8 | agents/project-builder.md 9 | agents/proposal-needed.md 10 | agents/proposal-writer.md 11 | agents/proposals-analyzer.md 12 | agents/proposals-locator.md 13 | agents/researcher.md 14 | agents/shortcut.md 15 | agents/testing.md 16 | agents/web-search-researcher.md 17 | commands/implement.md 18 | commands/proposal.md 19 | -------------------------------------------------------------------------------- /.claude/agents/architecture-patterns.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: architecture-patterns 3 | description: MUST USE THIS AGENT PROACTIVELY when designing an implementation plan to ensure that the architecture and direction of the plan conforms to the current best practices in this codebase. 4 | model: sonnet 5 | color: deepskyblue 6 | --- 7 | 8 | When considering various architecture patterns, we have a strong preference to re-use the current patterns in order to make the code more familiar across all developers. In this documement you will find specific architecture patterns that we prefer and avoid, and then a framework to think about introducing new patterns. -------------------------------------------------------------------------------- /.claude/agents/frontend-developer.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.claude/agents/go-developer.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: go-developer 3 | description: Writes go code for this project 4 | --- 5 | 6 | You are the agent that is invoked when needing to add or modify go code in this repo. 7 | 8 | * **Imports** - when importing local references, the import path is ALWAYS "github.com/replicatedhq/troubleshoot". 9 | 10 | 11 | * **Params** - we load parameters from environment variables in dev, but AWS Parameter Store (SSM) in prod. When adding a new config variable, you need to edit each projects param.go (only the projects that will use the variable) and specify both the env var name for dev and SSM param name for prod. 12 | 13 | 14 | * **SQL** - we write sql statements right in the code, not using any ORM. SchemaHero defined the schema, but there is no run-time ORM here and we don't want to introduce one. 15 | 16 | * **ID Generation** - -------------------------------------------------------------------------------- /.claude/agents/project-builder.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: project-builder 3 | description: MUST USE THIS AGENT PROACTIVELY when attempting to build, test, or run the project 4 | model: sonnet 5 | --- 6 | 7 | -------------------------------------------------------------------------------- /.claude/agents/shortcut.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: shortcut-story-manager 3 | description: MUST USE THIS AGENT PROACTIVELY when you need to query, create, or edit Shortcut "stories" (or tickets, issues, etc). Shortcut is where we track work for this project. Any issue that this project works on will be on the "Troubleshoot Team" team and project in Shortcut. 4 | model: sonnet 5 | color: cyan 6 | --- 7 | 8 | You are a product manager for the Troubleshoot Team team and responsible for managing shortcut stories that plan, prioritize, and track the work. You want to maintain a thorough record of the work done, including why, in each Shortcut story. 9 | 10 | -------------------------------------------------------------------------------- /.claude/agents/testing.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: testing 3 | description: MUST USE THIS AGENT PROACTIVELY when designing a plan to write tests. 4 | model: sonnet 5 | color: aquamarine 6 | --- 7 | 8 | 9 | In this document you will find preferred way to write various tests for this project. 10 | 11 | 12 | * **Avoid mocks** - While mocking our own and external APIs is tempting to create a way to test code in isolation, it creates a second implementation that requires maintaining. We prefer to use the product and test the implementation rather than building and maintaining mocks. 13 | 14 | * **Avoid dependency injection** - We don't use dependency injection frameworks in our codebase and do not want to introduce them. Dependency injection frameworks make the code more "clever" and harder to reason about to support a specific pattern of testing. We prefer to solve testing without introducing dependency injection. 15 | 16 | * **Isolated fixtures** - Avoid global fixtures that are reused between tests, even if they are specific to one test. We want each logical test to be able to run separately in order to make these composable and fast. We run all tests in parallel in the CI pipeline. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report a problem with Troubleshoot 4 | labels: 'bug' 5 | 6 | --- 7 | 8 | **Bug Description** 9 | 10 | Write a concise description of the problem here. 11 | 12 | **Expected Behavior** 13 | 14 | Include a description of the expected behaviour here. 15 | 16 | **Steps To Reproduce** 17 | 18 | Include the commands to reproduce the issue including any output. Any information that will help us to understand the problem is useful. Feel free to paste long output into a [Github gist](https://gist.github.com) and include the link here. 19 | 20 | 21 | **Additional Context** 22 | 23 | Include the following information. 24 | - Troubleshoot version. If you built from source, note that including the version of Go you used to build with. 25 | - Operating system 26 | - Operating system version 27 | - Other details that might be helpful in diagnosing the problem 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_epic.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Epic 3 | about: Create a tracking issue for a change that is larger than a single task 4 | labels: 'epic' 5 | 6 | --- 7 | 8 | # Design Proposal 9 | 10 | Link to the [proposal](https://github.com/replicatedhq/troubleshoot/tree/main/docs/design/template.md) 11 | 12 | # Definition of done 13 | 14 | Describe what specific goals can measure if this overall task is considered completed. Things to consider are documentation, high level description of the feature working, and tests. 15 | 16 | * [ ] 17 | 18 | # Subtasks 19 | 20 | Create a list of the smaller tasks to implement this design. Task can start as simple descriptions but should be converted to issues before work is started. 21 | 22 | ## Started 23 | 24 | * [ ] 25 | 26 | ## Planned 27 | 28 | * [ ] 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest a feature 4 | labels: 'feature' 5 | 6 | --- 7 | 8 | **Describe the rationale for the suggested feature.** 9 | 10 | Insert a description of the rationale for the new feature here. For example, you could describe a problem that Troubleshoot doesn't address, a limitation, or an idea to improve Troubleshoot. 11 | 12 | **Describe the feature** 13 | 14 | Insert a description of the feature here. Be specific about how it addresses the problem. Mention any limitations, or suggested improvements outlined in the rationale for the feature. 15 | 16 | **Describe alternatives you've considered** 17 | 18 | Describe alternative solutions here. Include any workarounds you've considered. 19 | 20 | **Additional context** 21 | 22 | Add additional context about the feature request. If the change is substantial, consider attaching files to the issue outlining architectural changes, data flows, file formats etc., anything that helps describe the requested change. 23 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description, Motivation and Context 2 | 3 | Please include a summary of the change or what problem it solves. Please also include relevant motivation and context. 4 | 5 | 9 | 10 | ## Checklist 11 | 12 | - [ ] New and existing tests pass locally with introduced changes. 13 | - [ ] Tests for the changes have been added (for bug fixes / features) 14 | - [ ] The commit message(s) are informative and highlight any breaking changes 15 | - [ ] Any documentation required has been added/updated. For changes to https://troubleshoot.sh/ create a PR [here](https://github.com/replicatedhq/troubleshoot.sh/pulls) 16 | 17 | ## Does this PR introduce a breaking change? 18 | - [ ] Yes 19 | - [ ] No 20 | 21 | 22 | -------------------------------------------------------------------------------- /.github/actions/setup-go/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Setup Go Environment' 2 | description: 'Setup Go with caching and common environment variables' 3 | inputs: 4 | go-version-file: 5 | description: 'Path to go.mod file' 6 | required: false 7 | default: 'go.mod' 8 | outputs: 9 | go-version: 10 | description: 'The Go version that was installed' 11 | value: ${{ steps.setup-go.outputs.go-version }} 12 | cache-hit: 13 | description: 'Whether the Go cache was hit' 14 | value: ${{ steps.setup-go.outputs.cache-hit }} 15 | runs: 16 | using: 'composite' 17 | steps: 18 | - name: Setup Go 19 | id: setup-go 20 | uses: actions/setup-go@v5 21 | with: 22 | go-version-file: ${{ inputs.go-version-file }} 23 | cache: true 24 | 25 | - name: Set Go environment variables 26 | shell: bash 27 | run: | 28 | echo "GOMAXPROCS=2" >> $GITHUB_ENV 29 | echo "GOCACHE=$(go env GOCACHE)" >> $GITHUB_ENV 30 | echo "GOMODCACHE=$(go env GOMODCACHE)" >> $GITHUB_ENV 31 | 32 | - name: Print Go environment 33 | shell: bash 34 | run: | 35 | echo "Go version: $(go version)" 36 | echo "GOOS: $(go env GOOS)" 37 | echo "GOARCH: $(go env GOARCH)" 38 | echo "Cache directory: $(go env GOCACHE)" 39 | echo "Module cache: $(go env GOMODCACHE)" 40 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gomod" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | labels: 11 | - "dependencies" 12 | - "go" 13 | - "type::security" 14 | schedule: 15 | interval: "weekly" 16 | groups: 17 | security: 18 | update-types: 19 | - "patch" 20 | - "minor" 21 | - package-ecosystem: "gomod" # See documentation for possible values 22 | directory: "/examples/sdk/helm-template" # Location of package manifests 23 | labels: 24 | - "dependencies" 25 | - "go" 26 | - "type::security" 27 | schedule: 28 | interval: "weekly" 29 | groups: 30 | security: 31 | update-types: 32 | - "patch" 33 | - "minor" 34 | 35 | # Maintain dependencies for GitHub Actions 36 | - package-ecosystem: "github-actions" 37 | # Workflow files stored in the 38 | # default location of `.github/workflows` 39 | directory: "/" 40 | labels: 41 | - "dependencies" 42 | - "github-actions" 43 | - "type::security" 44 | schedule: 45 | interval: "weekly" 46 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.ref }} 10 | cancel-in-progress: false 11 | 12 | jobs: 13 | goreleaser: 14 | runs-on: troubleshoot_release 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v5 18 | with: 19 | fetch-depth: 0 20 | 21 | - uses: azure/docker-login@v2 22 | with: 23 | username: ${{ secrets.DOCKERHUB_USER }} 24 | password: ${{ secrets.DOCKERHUB_PASSWORD }} 25 | 26 | - uses: actions/setup-go@v6 27 | with: 28 | go-version-file: 'go.mod' 29 | 30 | - name: Run GoReleaser 31 | uses: goreleaser/goreleaser-action@v6 32 | with: 33 | version: "v2.12.3" 34 | args: release --clean --config deploy/.goreleaser.yaml 35 | env: 36 | GITHUB_TOKEN: ${{ secrets.HOMEBREW_GITHUB_TOKEN }} 37 | 38 | - name: Update new preflight version in krew-index 39 | if: ${{ !contains(github.ref_name, '-') }} 40 | uses: rajatjindal/krew-release-bot@v0.0.47 41 | with: 42 | krew_template_file: deploy/krew/preflight.yaml 43 | 44 | - name: Update new support-bundle version in krew-index 45 | if: ${{ !contains(github.ref_name, '-') }} 46 | uses: rajatjindal/krew-release-bot@v0.0.47 47 | with: 48 | krew_template_file: deploy/krew/support-bundle.yaml 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Binaries for programs and plugins 3 | *.exe 4 | *.exe~ 5 | *.dll 6 | *.so 7 | *.dylib 8 | bin 9 | .DS_Store 10 | 11 | # npm generated files 12 | node_modules/ 13 | package*.json 14 | 15 | # Test binary, build with `go test -c` 16 | *.test 17 | 18 | # Output of the go coverage tool, specifically when used with LiteIDE 19 | *.out 20 | 21 | # Kubernetes Generated files - skip generated files, except for vendored files 22 | 23 | !vendor/**/zz_generated.* 24 | 25 | # Vendored files downloaded with go mod 26 | vendor 27 | 28 | # editor and IDE paraphernalia 29 | .idea 30 | *.swp 31 | *.swo 32 | *~ 33 | .envrc 34 | 35 | 36 | dist 37 | try.sh 38 | 39 | .vscode/ 40 | workspace.* 41 | 42 | cosign.key 43 | sbom/ 44 | 45 | # Ignore local pre-commit config 46 | .pre-commit-config.yaml 47 | 48 | # Ignore generated support bundles 49 | *.tar.gz 50 | !testdata/supportbundle/*.tar.gz 51 | !test/baselines/**/baseline.tar.gz 52 | 53 | # Ignore built binaries (use / prefix to avoid catching source files) 54 | /troubleshoot 55 | /troubleshoot-test 56 | cmd/troubleshoot/troubleshoot 57 | cmd/*/troubleshoot 58 | /support-bundle -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | version: "1" 2 | domain: replicated.com 3 | repo: github.com/replicatedhq/troubleshoot 4 | -------------------------------------------------------------------------------- /cmd/analyze/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | analyzecli "github.com/replicatedhq/troubleshoot/cmd/analyze/cli" 7 | ) 8 | 9 | func main() { 10 | if err := analyzecli.RootCmd().Execute(); err != nil { 11 | os.Exit(1) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /cmd/internal/util/version.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/replicatedhq/troubleshoot/pkg/version" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func VersionCmd() *cobra.Command { 11 | cmd := &cobra.Command{ 12 | Use: "version", 13 | Short: "Print the current version and exit", 14 | Long: `Print the current version and exit`, 15 | RunE: func(cmd *cobra.Command, args []string) error { 16 | fmt.Printf("Replicated Troubleshoot %s\n", version.Version()) 17 | 18 | return nil 19 | }, 20 | } 21 | return cmd 22 | } 23 | -------------------------------------------------------------------------------- /cmd/preflight/cli/oci_fetch.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/replicatedhq/troubleshoot/pkg/logger" 8 | "github.com/replicatedhq/troubleshoot/pkg/oci" 9 | "github.com/spf13/cobra" 10 | "github.com/spf13/viper" 11 | ) 12 | 13 | func OciFetchCmd() *cobra.Command { 14 | cmd := &cobra.Command{ 15 | Use: "oci-fetch [URI]", 16 | Args: cobra.MinimumNArgs(1), 17 | Short: "Fetch a preflight from an OCI registry and print it to standard out", 18 | PreRun: func(cmd *cobra.Command, args []string) { 19 | v := viper.GetViper() 20 | v.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) 21 | v.BindPFlags(cmd.Flags()) 22 | 23 | logger.SetupLogger(v) 24 | }, 25 | RunE: func(cmd *cobra.Command, args []string) error { 26 | uri := args[0] 27 | data, err := oci.PullPreflightFromOCI(uri) 28 | if err != nil { 29 | return err 30 | } 31 | fmt.Println(string(data)) 32 | return nil 33 | }, 34 | } 35 | 36 | // Initialize klog flags 37 | logger.InitKlogFlags(cmd) 38 | 39 | return cmd 40 | } 41 | -------------------------------------------------------------------------------- /cmd/preflight/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/replicatedhq/troubleshoot/cmd/preflight/cli" 5 | _ "k8s.io/client-go/plugin/pkg/client/auth" 6 | ) 7 | 8 | func main() { 9 | cli.InitAndExecute() 10 | } 11 | -------------------------------------------------------------------------------- /cmd/troubleshoot/cli/schedule.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "github.com/replicatedhq/troubleshoot/pkg/schedule" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | // Schedule returns the schedule command for managing scheduled support bundle jobs 9 | func Schedule() *cobra.Command { 10 | return schedule.CLI() 11 | } 12 | -------------------------------------------------------------------------------- /cmd/troubleshoot/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/replicatedhq/troubleshoot/cmd/troubleshoot/cli" 5 | _ "k8s.io/client-go/plugin/pkg/client/auth" 6 | ) 7 | 8 | func main() { 9 | cli.InitAndExecute() 10 | } 11 | -------------------------------------------------------------------------------- /config/samples/troubleshoot_v1beta2_analyzer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: Analyzer 3 | metadata: 4 | labels: 5 | controller-tools.k8s.io: "1.0" 6 | name: analyzer-sample 7 | spec: 8 | # Add fields here 9 | foo: bar 10 | -------------------------------------------------------------------------------- /deploy/Dockerfile.troubleshoot: -------------------------------------------------------------------------------- 1 | FROM debian:bookworm 2 | WORKDIR / 3 | 4 | RUN apt-get -qq update \ 5 | && apt-get -qq -y install \ 6 | ca-certificates kmod 7 | 8 | COPY support-bundle /troubleshoot/support-bundle 9 | COPY preflight /troubleshoot/preflight 10 | 11 | ENV PATH="/troubleshoot:${PATH}" 12 | 13 | -------------------------------------------------------------------------------- /deploy/rbac/preflight-clusterrole.yaml: -------------------------------------------------------------------------------- 1 | kind: ClusterRole 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | metadata: 4 | namespace: default 5 | name: preflight 6 | rules: 7 | - apiGroups: [""] 8 | resources: ["namespaces"] 9 | verbs: ["get", "list"] 10 | - apiGroups: [""] 11 | resources: ["pods"] 12 | verbs: ["get", "list"] 13 | -------------------------------------------------------------------------------- /docs/design/template.md: -------------------------------------------------------------------------------- 1 | # Title 2 | 3 | ## Goals 4 | 5 | ## Non Goals 6 | 7 | ## Background 8 | 9 | ## High-Level Design 10 | 11 | ## Detailed Design 12 | 13 | ## Limitations 14 | 15 | ## Assumptions 16 | 17 | ## Testing 18 | 19 | ## Alternatives Considered 20 | 21 | ## Security Considerations 22 | -------------------------------------------------------------------------------- /docs/support-bundle_analyze.md: -------------------------------------------------------------------------------- 1 | ## support-bundle analyze 2 | 3 | analyze a support bundle 4 | 5 | ### Synopsis 6 | 7 | Analyze a support bundle using the Analyzer definitions provided 8 | 9 | ``` 10 | support-bundle analyze [url] [flags] 11 | ``` 12 | 13 | ### Options 14 | 15 | ``` 16 | --bundle string filename of the support bundle to analyze 17 | -h, --help help for analyze 18 | --output string output format: json, yaml 19 | --quiet enable/disable error messaging and only show parseable output 20 | ``` 21 | 22 | ### Options inherited from parent commands 23 | 24 | ``` 25 | --cpuprofile string File path to write cpu profiling data 26 | --memprofile string File path to write memory profiling data 27 | ``` 28 | 29 | ### SEE ALSO 30 | 31 | * [support-bundle](support-bundle.md) - Generate a support bundle from a Kubernetes cluster or specified sources 32 | 33 | ###### Auto generated by spf13/cobra on 15-Sep-2025 34 | -------------------------------------------------------------------------------- /docs/support-bundle_version.md: -------------------------------------------------------------------------------- 1 | ## support-bundle version 2 | 3 | Print the current version and exit 4 | 5 | ### Synopsis 6 | 7 | Print the current version and exit 8 | 9 | ``` 10 | support-bundle version [flags] 11 | ``` 12 | 13 | ### Options 14 | 15 | ``` 16 | -h, --help help for version 17 | ``` 18 | 19 | ### Options inherited from parent commands 20 | 21 | ``` 22 | --cpuprofile string File path to write cpu profiling data 23 | --memprofile string File path to write memory profiling data 24 | ``` 25 | 26 | ### SEE ALSO 27 | 28 | * [support-bundle](support-bundle.md) - Generate a support bundle from a Kubernetes cluster or specified sources 29 | 30 | ###### Auto generated by spf13/cobra on 15-Sep-2025 31 | -------------------------------------------------------------------------------- /examples/collect/host/block-devices.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: block 5 | spec: 6 | collectors: 7 | - blockDevices: {} 8 | 9 | -------------------------------------------------------------------------------- /examples/collect/host/certificate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: certificate 5 | spec: 6 | collectors: 7 | - certificate: 8 | certificatePath: /etc/ssl/corp.crt 9 | keyPath: /etc/ssl/corp.key 10 | -------------------------------------------------------------------------------- /examples/collect/host/cpu.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: cpu 5 | spec: 6 | collectors: 7 | - cpu: {} 8 | -------------------------------------------------------------------------------- /examples/collect/host/disk-usage.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: diskUsage 5 | spec: 6 | collectors: 7 | - diskUsage: 8 | collectorName: ephemeral 9 | path: /var/lib/kubelet 10 | -------------------------------------------------------------------------------- /examples/collect/host/filesystem-performance.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: fsperf 5 | spec: 6 | collectors: 7 | - filesystemPerformance: 8 | collectorName: etcd-perf 9 | timeout: 2m 10 | directory: /var/lib/etcd 11 | fileSize: 22Mi 12 | operationSizeBytes: 2300 13 | datasync: true 14 | enableBackgroundIOPS: true 15 | backgroundIOPSWarmupSeconds: 10 16 | backgroundWriteIOPS: 300 17 | backgroundWriteIOPSJobs: 6 18 | backgroundReadIOPS: 50 19 | backgroundReadIOPSJobs: 1 20 | -------------------------------------------------------------------------------- /examples/collect/host/http-load-balancer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: httploadbalancer 5 | spec: 6 | collectors: 7 | - httpLoadBalancer: 8 | collectorName: httploadbalancer 9 | port: 80 10 | address: http://app.corporate.internal 11 | timeout: 10s 12 | -------------------------------------------------------------------------------- /examples/collect/host/http.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: http 5 | spec: 6 | collectors: 7 | - http: 8 | collectorName: registry 9 | get: 10 | url: https://registry.replicated.com 11 | -------------------------------------------------------------------------------- /examples/collect/host/ipv4-interfaces.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: ipv4Interfaces 5 | spec: 6 | collectors: 7 | - ipv4Interfaces: {} 8 | -------------------------------------------------------------------------------- /examples/collect/host/kernel-modules.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: modules 5 | spec: 6 | collectors: 7 | - kernelModules: {} 8 | -------------------------------------------------------------------------------- /examples/collect/host/memory.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: memory 5 | spec: 6 | collectors: 7 | - memory: 8 | collectorName: memory 9 | -------------------------------------------------------------------------------- /examples/collect/host/ntp.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: ntp 5 | spec: 6 | collectors: 7 | - time: {} 8 | -------------------------------------------------------------------------------- /examples/collect/host/run-and-save-output.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: run-host-cmd-and-save-output 5 | spec: 6 | collectors: 7 | - run: 8 | collectorName: "my-custom-run" 9 | command: "sh" 10 | # this is for demonstration purpose only -- you probably don't want to drop your input to the bundle! 11 | args: 12 | - "-c" 13 | - "cat $TS_INPUT_DIR/dummy.yaml > $TS_OUTPUT_DIR/dummy_content.yaml" 14 | outputDir: "myCommandOutputs" 15 | env: 16 | - AWS_REGION=us-west-1 17 | # if ignoreParentEnvs is true, it will not inherit envs from parent process. 18 | # values specified in inheritEnv will not be used either 19 | # ignoreParentEnvs: true 20 | inheritEnvs: 21 | - USER 22 | input: 23 | dummy.conf: |- 24 | [hello] 25 | hello = 1 26 | 27 | [bye] 28 | bye = 2 29 | dummy.yaml: |- 30 | username: postgres 31 | password: 32 | dbHost: 33 | map: 34 | key: value 35 | list: 36 | - val1 37 | - val2 38 | -------------------------------------------------------------------------------- /examples/collect/host/sample.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: example 5 | spec: 6 | collectors: 7 | - blockDevices: {} 8 | - certificate: 9 | certificatePath: /etc/ssl/corp.crt 10 | keyPath: /etc/ssl/corp.key 11 | - cpu: {} 12 | - diskUsage: 13 | collectorName: ephemeral 14 | path: /var/lib/kubelet 15 | - httpLoadBalancer: 16 | collectorName: httploadbalancer 17 | port: 80 18 | address: http://app.corporate.internal 19 | timeout: 10s 20 | - http: 21 | collectorName: registry 22 | get: 23 | url: https://registry.replicated.com 24 | - ipv4Interfaces: {} 25 | - memory: {} 26 | - time: {} 27 | - tcpConnect: 28 | collectorName: weave host 1 29 | address: 10.128.0.2:6783 30 | timeout: 2s 31 | - tcpLoadBalancer: 32 | collectorName: LB1 33 | address: 10.128.0.20:6443 34 | port: 6443 35 | timeout: 5000ms 36 | - tcpPortStatus: 37 | collectorName: k8s 38 | port: 6443 39 | -------------------------------------------------------------------------------- /examples/collect/host/sysctl.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: sysctl 5 | spec: 6 | collectors: 7 | - sysctl: 8 | collectorName: sysctl 9 | -------------------------------------------------------------------------------- /examples/collect/host/tcp-connect.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: connect 5 | spec: 6 | collectors: 7 | - tcpConnect: 8 | collectorName: weave host 1 9 | address: 10.128.0.2:6783 10 | -------------------------------------------------------------------------------- /examples/collect/host/tcp-load-balancer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: loadbalancer 5 | spec: 6 | collectors: 7 | - tcpLoadBalancer: 8 | collectorName: loadbalancer 9 | port: 7443 10 | address: 10.128.0.29:7444 11 | -------------------------------------------------------------------------------- /examples/collect/host/tcp-port.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: port 5 | spec: 6 | collectors: 7 | - tcpPortStatus: 8 | collectorName: k8s 9 | port: 7443 10 | -------------------------------------------------------------------------------- /examples/collect/host/timezone.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: timezone 5 | spec: 6 | collectors: 7 | - time: {} 8 | -------------------------------------------------------------------------------- /examples/collect/host/udp-port.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: port 5 | spec: 6 | collectors: 7 | - udpPortStatus: 8 | collectorName: flannel 9 | port: 8472 10 | -------------------------------------------------------------------------------- /examples/collect/remote/block-devices.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: RemoteCollector 3 | metadata: 4 | name: block 5 | spec: 6 | collectors: 7 | - blockDevices: {} 8 | 9 | -------------------------------------------------------------------------------- /examples/collect/remote/certificate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: RemoteCollector 3 | metadata: 4 | name: certificate 5 | spec: 6 | collectors: 7 | - certificate: 8 | certificatePath: /etc/ssl/corp.crt 9 | keyPath: /etc/ssl/corp.key 10 | -------------------------------------------------------------------------------- /examples/collect/remote/cpu.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: RemoteCollector 3 | metadata: 4 | name: cpu 5 | spec: 6 | collectors: 7 | - cpu: {} 8 | -------------------------------------------------------------------------------- /examples/collect/remote/disk-usage.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: RemoteCollector 3 | metadata: 4 | name: diskUsage 5 | spec: 6 | collectors: 7 | - diskUsage: 8 | collectorName: ephemeral 9 | path: /var/lib/kubelet 10 | -------------------------------------------------------------------------------- /examples/collect/remote/filesystem-performance.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: RemoteCollector 3 | metadata: 4 | name: fsperf 5 | spec: 6 | collectors: 7 | - filesystemPerformance: 8 | collectorName: etcd-perf 9 | timeout: 2m 10 | directory: /var/lib/etcd 11 | fileSize: 22Mi 12 | operationSizeBytes: 2300 13 | datasync: true 14 | enableBackgroundIOPS: true 15 | backgroundIOPSWarmupSeconds: 10 16 | backgroundWriteIOPS: 300 17 | backgroundWriteIOPSJobs: 6 18 | backgroundReadIOPS: 50 19 | backgroundReadIOPSJobs: 1 20 | -------------------------------------------------------------------------------- /examples/collect/remote/http-load-balancer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: RemoteCollector 3 | metadata: 4 | name: httploadbalancer 5 | spec: 6 | collectors: 7 | - httpLoadBalancer: 8 | collectorName: httploadbalancer 9 | port: 80 10 | address: http://app.corporate.internal 11 | timeout: 10s 12 | -------------------------------------------------------------------------------- /examples/collect/remote/http.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: RemoteCollector 3 | metadata: 4 | name: http 5 | spec: 6 | collectors: 7 | - http: 8 | collectorName: registry 9 | get: 10 | url: https://registry.replicated.com 11 | -------------------------------------------------------------------------------- /examples/collect/remote/ipv4-interfaces.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: RemoteCollector 3 | metadata: 4 | name: ipv4Interfaces 5 | spec: 6 | collectors: 7 | - ipv4Interfaces: {} 8 | -------------------------------------------------------------------------------- /examples/collect/remote/kernel-modules.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: RemoteCollector 3 | metadata: 4 | name: modules 5 | spec: 6 | collectors: 7 | - kernelModules: {} 8 | -------------------------------------------------------------------------------- /examples/collect/remote/memory.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: RemoteCollector 3 | metadata: 4 | name: memory 5 | spec: 6 | collectors: 7 | - memory: 8 | collectorName: memory 9 | -------------------------------------------------------------------------------- /examples/collect/remote/ntp.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: RemoteCollector 3 | metadata: 4 | name: ntp 5 | spec: 6 | collectors: 7 | - time: {} 8 | -------------------------------------------------------------------------------- /examples/collect/remote/sample.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: RemoteCollector 3 | metadata: 4 | name: example 5 | spec: 6 | collectors: 7 | - blockDevices: {} 8 | - certificate: 9 | certificatePath: /etc/ssl/corp.crt 10 | keyPath: /etc/ssl/corp.key 11 | - cpu: {} 12 | - diskUsage: 13 | collectorName: ephemeral 14 | path: /var/lib/kubelet 15 | - httpLoadBalancer: 16 | collectorName: httploadbalancer 17 | port: 80 18 | address: http://app.corporate.internal 19 | timeout: 10s 20 | - http: 21 | collectorName: registry 22 | get: 23 | url: https://registry.replicated.com 24 | - ipv4Interfaces: {} 25 | - memory: {} 26 | - time: {} 27 | - tcpConnect: 28 | collectorName: weave host 1 29 | address: 10.128.0.2:6783 30 | timeout: 2s 31 | - tcpLoadBalancer: 32 | collectorName: LB1 33 | address: 10.128.0.20:6443 34 | port: 6443 35 | timeout: 5000ms 36 | - tcpPortStatus: 37 | collectorName: k8s 38 | port: 6443 39 | -------------------------------------------------------------------------------- /examples/collect/remote/tcp-connect.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: RemoteCollector 3 | metadata: 4 | name: connect 5 | spec: 6 | collectors: 7 | - tcpConnect: 8 | collectorName: weave host 1 9 | address: 10.128.0.2:6783 10 | -------------------------------------------------------------------------------- /examples/collect/remote/tcp-load-balancer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: RemoteCollector 3 | metadata: 4 | name: loadbalancer 5 | spec: 6 | collectors: 7 | - tcpLoadBalancer: 8 | collectorName: loadbalancer 9 | port: 7443 10 | address: 10.128.0.29:7444 11 | -------------------------------------------------------------------------------- /examples/collect/remote/tcp-port.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: RemoteCollector 3 | metadata: 4 | name: port 5 | spec: 6 | collectors: 7 | - tcpPortStatus: 8 | collectorName: k8s 9 | port: 7443 10 | -------------------------------------------------------------------------------- /examples/collect/remote/timezone.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: RemoteCollector 3 | metadata: 4 | name: timezone 5 | spec: 6 | collectors: 7 | - time: {} 8 | -------------------------------------------------------------------------------- /examples/collect/remote/udp-port.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostCollector 3 | metadata: 4 | name: port 5 | spec: 6 | collectors: 7 | - udpPortStatus: 8 | collectorName: flannel 9 | port: 8472 10 | -------------------------------------------------------------------------------- /examples/collect/v1beta3-secret.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Secret containing database credentials 3 | apiVersion: v1 4 | kind: Secret 5 | metadata: 6 | name: test-database-credentials 7 | namespace: default 8 | type: Opaque 9 | stringData: 10 | # PostgreSQL connection URI 11 | postgres-uri: "postgresql://testuser:supersecret@postgres.example.com:5432/testdb?sslmode=require" 12 | 13 | # MySQL connection URI 14 | mysql-uri: "mysql://testuser:supersecret@mysql.example.com:3306/testdb" 15 | 16 | # Redis connection URI 17 | redis-uri: "redis://:supersecret@redis.example.com:6379" 18 | 19 | # TLS certificates (example data) 20 | ca.crt: | 21 | -----BEGIN CERTIFICATE----- 22 | MIICpDCCAYwCCQDU+pQ3ZUD30jANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls 23 | b2NhbGhvc3QwHhcNMjQwMTAxMDAwMDAwWhcNMjUwMTAxMDAwMDAwWjAUMRIwEAYD 24 | VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7 25 | VJTUt9Us8cKjMzEfYyjiWA4R4/M2bS1+fWIcPm15A8IgC0qC1J3xGhE= 26 | -----END CERTIFICATE----- 27 | 28 | client.crt: | 29 | -----BEGIN CERTIFICATE----- 30 | MIICpDCCAYwCCQDU+pQ3ZUD30jANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls 31 | b2NhbGhvc3QwHhcNMjQwMTAxMDAwMDAwWhcNMjUwMTAxMDAwMDAwWjAUMRIwEAYD 32 | VQQDDA5jbGllbnQtY2VydA== 33 | -----END CERTIFICATE----- 34 | 35 | client.key: | 36 | -----BEGIN PRIVATE KEY----- 37 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7VJTUt9Us8cKj 38 | MzEfYyjiWA4R4/M2bS1+fWIcPm15A8IgC0qC1J3xGhE= 39 | -----END PRIVATE KEY----- 40 | -------------------------------------------------------------------------------- /examples/preflight/host/block-devices.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: block 5 | spec: 6 | collectors: 7 | - blockDevices: {} 8 | analyzers: 9 | - blockDevices: 10 | outcomes: 11 | - pass: 12 | when: ".* == 1" 13 | message: One available block device 14 | - pass: 15 | when: ".* > 1" 16 | message: Multiple available block devices 17 | - fail: 18 | message: No available block devices 19 | -------------------------------------------------------------------------------- /examples/preflight/host/certificate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: certificate 5 | spec: 6 | collectors: 7 | - certificate: 8 | certificatePath: /etc/ssl/corp.crt 9 | keyPath: /etc/ssl/corp.key 10 | analyzers: 11 | - certificate: 12 | outcomes: 13 | - fail: 14 | when: "key-pair-missing" 15 | message: Certificate key pair not found in /etc/ssl 16 | - fail: 17 | when: "key-pair-switched" 18 | message: Cert and key pair are switched 19 | - fail: 20 | when: "key-pair-encrypted" 21 | message: Private key is encrypted 22 | - fail: 23 | when: "key-pair-mismatch" 24 | message: Cert and key do not match 25 | - fail: 26 | when: "key-pair-invalid" 27 | message: Certificate key pair is invalid 28 | - pass: 29 | when: "key-pair-valid" 30 | message: Certificate key pair is valid 31 | -------------------------------------------------------------------------------- /examples/preflight/host/cpu.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: cpu 5 | spec: 6 | collectors: 7 | - cpu: {} 8 | analyzers: 9 | - cpu: 10 | outcomes: 11 | - fail: 12 | when: "physical < 4" 13 | message: At least 4 physical CPU cores are required 14 | - fail: 15 | when: "logical < 8" 16 | message: At least 8 CPU cores are required 17 | - warn: 18 | when: "count < 16" 19 | message: At least 16 CPU cores preferred 20 | - pass: 21 | message: This server has sufficient CPU cores 22 | -------------------------------------------------------------------------------- /examples/preflight/host/disk-usage.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: diskUsage 5 | spec: 6 | collectors: 7 | - diskUsage: 8 | collectorName: ephemeral 9 | path: /var/lib/kubelet 10 | analyzers: 11 | - diskUsage: 12 | collectorName: ephemeral 13 | outcomes: 14 | - fail: 15 | when: "total < 20Gi" 16 | message: /var/lib/kubelet has less than 20Gi of total space 17 | - fail: 18 | when: "available < 10Gi" 19 | message: /var/lib/kubelet has less than 10Gi of disk space available 20 | - fail: 21 | when: "used/total > 70%" 22 | message: /var/lib/kubelet is more than 70% full 23 | - warn: 24 | when: "total < 40Gi" 25 | message: /var/lib/kubelet has less than 40Gi of total space 26 | - warn: 27 | when: "used/total > 60%" 28 | message: /var/lib/kubelet is more than 60% full 29 | - pass: 30 | when: "available/total >= 90%" 31 | message: /var/lib/kubelet has more than 90% available 32 | - pass: 33 | message: /var/lib/kubelet has sufficient disk space available 34 | -------------------------------------------------------------------------------- /examples/preflight/host/filesystem-performance.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: fsperf 5 | spec: 6 | collectors: 7 | - filesystemPerformance: 8 | collectorName: etcd-perf 9 | timeout: 2m 10 | directory: /var/lib/etcd 11 | fileSize: 22Mi 12 | operationSizeBytes: 2300 13 | datasync: true 14 | enableBackgroundIOPS: true 15 | backgroundIOPSWarmupSeconds: 10 16 | backgroundWriteIOPS: 300 17 | backgroundWriteIOPSJobs: 6 18 | backgroundReadIOPS: 50 19 | backgroundReadIOPSJobs: 1 20 | analyzers: 21 | - filesystemPerformance: 22 | collectorName: etcd-perf 23 | outcomes: 24 | - pass: 25 | when: "p99 < 3ms" 26 | message: "Write latency is great! (p99: {{ .P99 }})" 27 | - pass: 28 | when: "p99 < 5ms" 29 | message: "Write latency is ok (p99: {{ .P99 }})" 30 | - warn: 31 | when: "p99 < 8ms" 32 | message: "Write latency is high {{ .String }}" 33 | - fail: 34 | when: "p99 >= 8ms" 35 | message: "Write latency is too high {{ .String }}" 36 | -------------------------------------------------------------------------------- /examples/preflight/host/http-load-balancer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: httploadbalancer 5 | spec: 6 | collectors: 7 | - httpLoadBalancer: 8 | collectorName: httploadbalancer 9 | port: 80 10 | address: http://app.corporate.internal 11 | timeout: 10s 12 | analyzers: 13 | - httpLoadBalancer: 14 | collectorName: httploadbalancer 15 | outcomes: 16 | - fail: 17 | when: "connection-refused" 18 | message: Connection to port 80 via load balancer was refused. 19 | - fail: 20 | when: "address-in-use" 21 | message: Another process was already listening on port 80. 22 | - fail: 23 | when: "connection-timeout" 24 | message: Timed out connecting to port 80 via load balancer. Check your firewall. 25 | - fail: 26 | when: "bind-permission-denied" 27 | message: Bind permission denied. Try running as root. 28 | - fail: 29 | when: "error" 30 | message: Failed to connect to port 80 via load balancer. 31 | - pass: 32 | when: "connected" 33 | message: Successfully connected to port 80 via load balancer. 34 | -------------------------------------------------------------------------------- /examples/preflight/host/http.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: http 5 | spec: 6 | collectors: 7 | - http: 8 | collectorName: registry 9 | get: 10 | url: https://registry.replicated.com 11 | analyzers: 12 | - http: 13 | collectorName: registry 14 | outcomes: 15 | - fail: 16 | when: "error" 17 | message: Error connecting to registry 18 | - pass: 19 | when: "statusCode == 404" 20 | message: Connected to registry 21 | - fail: 22 | message: "Unexpected response" 23 | -------------------------------------------------------------------------------- /examples/preflight/host/ipv4-interfaces.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: ipv4Interfaces 5 | spec: 6 | collectors: 7 | - ipv4Interfaces: {} 8 | analyzers: 9 | - ipv4Interfaces: 10 | outcomes: 11 | - fail: 12 | when: "count == 0" 13 | message: No IPv4 interfaces detected 14 | - warn: 15 | when: "count >= 2" 16 | message: Multiple IPv4 interfaces detected 17 | - pass: 18 | when: "count == 1" 19 | message: IPv4 interface detected 20 | -------------------------------------------------------------------------------- /examples/preflight/host/kernel-modules.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: modules 5 | spec: 6 | collectors: 7 | - kernelModules: {} 8 | analyzers: 9 | - kernelModules: 10 | outcomes: 11 | - fail: 12 | when: "target_core_mod != loaded,loadable" 13 | message: The 'target_core_mod' kernel module is not loaded or loadable 14 | - fail: 15 | when: "target_core_file != loaded,loadable" 16 | message: The 'target_core_file' kernel module is not loaded or loadable 17 | - fail: 18 | when: "tcm_loop != loaded,loadable" 19 | message: The 'tcm_loop' kernel module is not loaded or loadable 20 | - warn: 21 | when: "nvme != loaded" 22 | message: The system is not using NVME storage, which will provide better performance 23 | - pass: 24 | when: "target_core_mod,target_core_file,tcm_loop == loaded,loadable" 25 | message: The 'target_core_mod', target_core_file', and 'tcm_loop' kernel modules are loaded or loadable 26 | -------------------------------------------------------------------------------- /examples/preflight/host/memory.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: memory 5 | spec: 6 | collectors: 7 | - memory: 8 | collectorName: memory 9 | analyzers: 10 | - memory: 11 | outcomes: 12 | - fail: 13 | when: "< 8Gi" 14 | message: At least 8Gi of memory is required 15 | - warn: 16 | when: "< 32Gi" 17 | message: At least 32Gi of memory is recommended 18 | - pass: 19 | message: The system has as sufficient memory 20 | -------------------------------------------------------------------------------- /examples/preflight/host/ntp.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: ntp 5 | spec: 6 | collectors: 7 | - time: {} 8 | analyzers: 9 | - time: 10 | outcomes: 11 | - fail: 12 | when: "ntp == unsynchronized+inactive" 13 | message: System clock not synchronized 14 | - warn: 15 | when: "ntp == unsynchronized+active" 16 | message: System clock not yet synchronized 17 | - warn: 18 | when: "ntp == synchronized+inactive" 19 | message: NTP not active 20 | - warn: 21 | when: "timezone != UTC" 22 | message: "Non UTC timezone can interfere with system function" 23 | - pass: 24 | when: "ntp == synchronized+active" 25 | message: System clock is synchronized 26 | - pass: 27 | when: "timezone == UTC" 28 | message: "timezone set to UTC" 29 | 30 | -------------------------------------------------------------------------------- /examples/preflight/host/subnet-available.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: subnet-available 5 | spec: 6 | collectors: 7 | # would output yes/no depending if there is a /22 available in 10.0.0.0/8 8 | - subnetAvailable: 9 | CIDRRangeAlloc: "10.0.0.0/8" 10 | desiredCIDR: 22 11 | analyzers: 12 | - subnetAvailable: 13 | outcomes: 14 | - fail: 15 | when: "no-subnet-available" 16 | message: failed to find available subnet 17 | - pass: 18 | when: "a-subnet-is-available" 19 | message: available /22 subnet found 20 | -------------------------------------------------------------------------------- /examples/preflight/host/subnet-contains-ip.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: subnet-contains-ip 5 | spec: 6 | analyzers: 7 | - subnetContainsIP: 8 | cidr: "10.0.0.0/8" 9 | ip: "10.0.0.5" 10 | outcomes: 11 | - fail: 12 | when: "false" 13 | message: The IP address is not within the subnet range 14 | - pass: 15 | when: "true" 16 | message: The IP address is within the subnet range 17 | -------------------------------------------------------------------------------- /examples/preflight/host/sysctl.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: sysctl 5 | spec: 6 | collectors: 7 | - sysctl: 8 | collectorName: host sysctl 9 | analyzers: 10 | - sysctl: 11 | collectorName: host sysctl 12 | outcomes: 13 | - warn: 14 | when: 'kern.ostype == Darwin' 15 | message: "Running sysctl on a Darwin host" 16 | - pass: 17 | when: 'net.ipv4.conf.default.arp_ignore > 0' 18 | message: "ARP ignore is enabled for the default interfaces interfaces on the host." 19 | -------------------------------------------------------------------------------- /examples/preflight/host/tcp-connect.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: connect 5 | spec: 6 | collectors: 7 | - tcpConnect: 8 | collectorName: weave host 1 9 | address: 10.128.0.2:6783 10 | analyzers: 11 | - tcpConnect: 12 | collectorName: weave host 1 13 | outcomes: 14 | - fail: 15 | when: "connection-refused" 16 | message: Connection to weave on host 1 was refused 17 | - fail: 18 | when: "connection-timeout" 19 | message: Timed out connecting to weave on host 1 20 | - fail: 21 | when: "error" 22 | message: Unexpected error connecting to weave on host 1 23 | - pass: 24 | when: "connected" 25 | message: Successfully connected to weave on host 1 26 | -------------------------------------------------------------------------------- /examples/preflight/host/tcp-load-balancer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: loadbalancer 5 | spec: 6 | collectors: 7 | - tcpLoadBalancer: 8 | collectorName: loadbalancer 9 | port: 7443 10 | address: 10.128.0.29:7444 11 | analyzers: 12 | - tcpLoadBalancer: 13 | collectorName: loadbalancer 14 | outcomes: 15 | - fail: 16 | when: "invalid-address" 17 | message: The Load Balancer address is not valid. 18 | - fail: 19 | when: "connection-refused" 20 | message: Connection to port 7443 via load balancer was refused. 21 | - fail: 22 | when: "address-in-use" 23 | message: Another process was already listening on port 7443. 24 | - fail: 25 | when: "connection-timeout" 26 | message: Timed out connecting to port 7443 via load balancer. Check your firewall. 27 | - fail: 28 | when: "error" 29 | message: Unexpected port status 30 | - pass: 31 | when: "connected" 32 | message: Successfully connected to port 7443 via load balancer 33 | - warn: 34 | message: Unexpected port status 35 | -------------------------------------------------------------------------------- /examples/preflight/host/tcp-port.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: port 5 | spec: 6 | collectors: 7 | - tcpPortStatus: 8 | collectorName: k8s 9 | port: 7443 10 | analyzers: 11 | - tcpPortStatus: 12 | collectorName: k8s 13 | outcomes: 14 | - fail: 15 | when: "connection-refused" 16 | message: Connection to port 7443 was refused. 17 | - fail: 18 | when: "address-in-use" 19 | message: Another process was already listening on port 7443. 20 | - fail: 21 | when: "connection-timeout" 22 | message: Timed out connecting to port 7443. Check your firewall. 23 | - fail: 24 | when: "error" 25 | message: Unexpected port status 26 | - pass: 27 | when: "connected" 28 | message: Port 7443 is open 29 | - warn: 30 | message: Unexpected port status 31 | -------------------------------------------------------------------------------- /examples/preflight/host/timezone.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: timezone 5 | spec: 6 | collectors: 7 | - time: {} 8 | analyzers: 9 | - time: 10 | outcomes: 11 | - pass: 12 | when: "timezone == UTC" 13 | message: Timezone is UTC 14 | - fail: 15 | when: "timezone != UTC" 16 | message: Timezone is not UTC 17 | -------------------------------------------------------------------------------- /examples/preflight/host/udp-port.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: port 5 | spec: 6 | collectors: 7 | - udpPortStatus: 8 | collectorName: flannel 9 | port: 8472 10 | analyzers: 11 | - udpPortStatus: 12 | collectorName: flannel 13 | outcomes: 14 | - fail: 15 | when: "address-in-use" 16 | message: Another process was already listening on port 8472. 17 | - fail: 18 | when: "error" 19 | message: Unexpected port status 20 | - pass: 21 | when: "connected" 22 | message: Port 8472 is open 23 | - warn: 24 | message: Unexpected port status 25 | -------------------------------------------------------------------------------- /examples/preflight/postgres.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: Preflight 3 | metadata: 4 | name: sample 5 | spec: 6 | collectors: 7 | - postgres: 8 | collectorName: pg 9 | uri: postgresql://user:password@hostname:5432/defaultdb?sslmode=require 10 | analyzers: 11 | - postgres: 12 | checkName: Must be postgres 10.x or later 13 | collectorName: pg 14 | outcomes: 15 | - fail: 16 | when: "connected == false" 17 | message: Cannot connect to postgres server 18 | - fail: 19 | when: "version < 10.x" 20 | message: The postgres server must be at least version 10 21 | - pass: 22 | message: The postgres connection checks out 23 | -------------------------------------------------------------------------------- /examples/preflight/redis.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: Preflight 3 | metadata: 4 | name: sample 5 | spec: 6 | collectors: 7 | - redis: 8 | collectorName: my-redis 9 | uri: rediss://default:replicated@server:6380 10 | tls: 11 | skipVerify: true 12 | analyzers: 13 | - redis: 14 | checkName: Must be redis 5.x or later 15 | collectorName: my-redis 16 | outcomes: 17 | - fail: 18 | when: "connected == false" 19 | message: Cannot connect to redis server 20 | - fail: 21 | when: "version < 5.0.0" 22 | message: The redis server must be at least version 5 23 | - pass: 24 | message: The redis connection checks out 25 | -------------------------------------------------------------------------------- /examples/preflight/remote/block-devices.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: block 5 | spec: 6 | remoteCollectors: 7 | - blockDevices: {} 8 | analyzers: 9 | - blockDevices: 10 | outcomes: 11 | - pass: 12 | when: ".* == 1" 13 | message: One available block device 14 | - pass: 15 | when: ".* > 1" 16 | message: Multiple available block devices 17 | - fail: 18 | message: No available block devices 19 | -------------------------------------------------------------------------------- /examples/preflight/remote/certificate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: certificate 5 | spec: 6 | remoteCollectors: 7 | - certificate: 8 | certificatePath: /etc/ssl/corp.crt 9 | keyPath: /etc/ssl/corp.key 10 | analyzers: 11 | - certificate: 12 | outcomes: 13 | - fail: 14 | when: "key-pair-missing" 15 | message: Certificate key pair not found in /etc/ssl 16 | - fail: 17 | when: "key-pair-switched" 18 | message: Cert and key pair are switched 19 | - fail: 20 | when: "key-pair-encrypted" 21 | message: Private key is encrypted 22 | - fail: 23 | when: "key-pair-mismatch" 24 | message: Cert and key do not match 25 | - fail: 26 | when: "key-pair-invalid" 27 | message: Certificate key pair is invalid 28 | - pass: 29 | when: "key-pair-valid" 30 | message: Certificate key pair is valid 31 | -------------------------------------------------------------------------------- /examples/preflight/remote/cpu.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: cpu 5 | spec: 6 | remoteCollectors: 7 | - cpu: {} 8 | analyzers: 9 | - cpu: 10 | outcomes: 11 | - fail: 12 | when: "physical < 4" 13 | message: At least 4 physical CPU cores are required 14 | - fail: 15 | when: "logical < 8" 16 | message: At least 8 CPU cores are required 17 | - warn: 18 | when: "count < 16" 19 | message: At least 16 CPU cores preferred 20 | - pass: 21 | message: This server has sufficient CPU cores 22 | 23 | -------------------------------------------------------------------------------- /examples/preflight/remote/disk-usage.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: diskUsage 5 | spec: 6 | remoteCollectors: 7 | - diskUsage: 8 | collectorName: ephemeral 9 | path: /var/lib/kubelet 10 | analyzers: 11 | - diskUsage: 12 | collectorName: ephemeral 13 | outcomes: 14 | - fail: 15 | when: "total < 20Gi" 16 | message: /var/lib/kubelet has less than 20Gi of total space 17 | - fail: 18 | when: "available < 10Gi" 19 | message: /var/lib/kubelet has less than 10Gi of disk space available 20 | - fail: 21 | when: "used/total > 70%" 22 | message: /var/lib/kubelet is more than 70% full 23 | - warn: 24 | when: "total < 40Gi" 25 | message: /var/lib/kubelet has less than 40Gi of total space 26 | - warn: 27 | when: "used/total > 60%" 28 | message: /var/lib/kubelet is more than 60% full 29 | - pass: 30 | when: "available/total >= 90%" 31 | message: /var/lib/kubelet has more than 90% available 32 | - pass: 33 | message: /var/lib/kubelet has sufficient disk space available 34 | -------------------------------------------------------------------------------- /examples/preflight/remote/filesystem-performance.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: fsperf 5 | spec: 6 | remoteCollectors: 7 | - filesystemPerformance: 8 | collectorName: etcd-perf 9 | timeout: 2m 10 | directory: /var/lib/etcd 11 | fileSize: 22Mi 12 | operationSizeBytes: 2300 13 | datasync: true 14 | enableBackgroundIOPS: true 15 | backgroundIOPSWarmupSeconds: 10 16 | backgroundWriteIOPS: 300 17 | backgroundWriteIOPSJobs: 6 18 | backgroundReadIOPS: 50 19 | backgroundReadIOPSJobs: 1 20 | analyzers: 21 | - filesystemPerformance: 22 | collectorName: etcd-perf 23 | outcomes: 24 | - pass: 25 | when: "p99 < 3ms" 26 | message: "Write latency is great! (p99: {{ .P99 }})" 27 | - pass: 28 | when: "p99 < 5ms" 29 | message: "Write latency is ok (p99: {{ .P99 }})" 30 | - warn: 31 | when: "p99 < 8ms" 32 | message: "Write latency is high {{ .String }}" 33 | - fail: 34 | when: "p99 >= 8ms" 35 | message: "Write latency is too high {{ .String }}" 36 | -------------------------------------------------------------------------------- /examples/preflight/remote/http-load-balancer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: httploadbalancer 5 | spec: 6 | remoteCollectors: 7 | - httpLoadBalancer: 8 | collectorName: httploadbalancer 9 | port: 80 10 | address: http://app.corporate.internal 11 | timeout: 10s 12 | analyzers: 13 | - httpLoadBalancer: 14 | collectorName: httploadbalancer 15 | outcomes: 16 | - fail: 17 | when: "connection-refused" 18 | message: Connection to port 80 via load balancer was refused. 19 | - fail: 20 | when: "address-in-use" 21 | message: Another process was already listening on port 80. 22 | - fail: 23 | when: "connection-timeout" 24 | message: Timed out connecting to port 80 via load balancer. Check your firewall. 25 | - fail: 26 | when: "bind-permission-denied" 27 | message: Bind permission denied. Try running as root. 28 | - fail: 29 | when: "error" 30 | message: Failed to connect to port 80 via load balancer. 31 | - pass: 32 | when: "connected" 33 | message: Successfully connected to port 80 via load balancer. 34 | -------------------------------------------------------------------------------- /examples/preflight/remote/http.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: http 5 | spec: 6 | remoteCollectors: 7 | - http: 8 | collectorName: registry 9 | get: 10 | url: https://registry.replicated.com 11 | analyzers: 12 | - http: 13 | collectorName: registry 14 | outcomes: 15 | - fail: 16 | when: "error" 17 | message: Error connecting to registry 18 | - pass: 19 | when: "statusCode == 404" 20 | message: Connected to registry 21 | - fail: 22 | message: "Unexpected response" 23 | -------------------------------------------------------------------------------- /examples/preflight/remote/ipv4-interfaces.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: ipv4Interfaces 5 | spec: 6 | remoteCollectors: 7 | - ipv4Interfaces: {} 8 | analyzers: 9 | - ipv4Interfaces: 10 | outcomes: 11 | - fail: 12 | when: "count == 0" 13 | message: No IPv4 interfaces detected 14 | - warn: 15 | when: "count >= 2" 16 | message: Multiple IPv4 interfaces detected 17 | - pass: 18 | when: "count == 1" 19 | message: IPv4 interface detected 20 | -------------------------------------------------------------------------------- /examples/preflight/remote/kernel-modules.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: modules 5 | spec: 6 | remoteCollectors: 7 | - kernelModules: {} 8 | analyzers: 9 | - kernelModules: 10 | outcomes: 11 | - fail: 12 | when: "target_core_mod != loaded,loadable" 13 | message: The 'target_core_mod' kernel module is not loaded or loadable 14 | - fail: 15 | when: "target_core_file != loaded,loadable" 16 | message: The 'target_core_file' kernel module is not loaded or loadable 17 | - fail: 18 | when: "tcm_loop != loaded,loadable" 19 | message: The 'tcm_loop' kernel module is not loaded or loadable 20 | - warn: 21 | when: "nvme != loaded" 22 | message: The system is not using NVME storage, which will provide better performance 23 | - pass: 24 | when: "target_core_mod,target_core_file,tcm_loop == loaded,loadable" 25 | message: The 'target_core_mod', target_core_file', and 'tcm_loop' kernel modules are loaded or loadable 26 | -------------------------------------------------------------------------------- /examples/preflight/remote/memory.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: memory 5 | spec: 6 | remoteCollectors: 7 | - memory: 8 | collectorName: memory 9 | analyzers: 10 | - memory: 11 | outcomes: 12 | - fail: 13 | when: "< 8Gi" 14 | message: At least 8Gi of memory is required 15 | - warn: 16 | when: "< 32Gi" 17 | message: At least 32Gi of memory is recommended 18 | - pass: 19 | message: The system has as sufficient memory 20 | -------------------------------------------------------------------------------- /examples/preflight/remote/ntp.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: ntp 5 | spec: 6 | remoteCollectors: 7 | - time: {} 8 | analyzers: 9 | - time: 10 | outcomes: 11 | - fail: 12 | when: "ntp == unsynchronized+inactive" 13 | message: System clock not synchronized 14 | - warn: 15 | when: "ntp == unsynchronized+active" 16 | message: System clock not yet synchronized 17 | - warn: 18 | when: "ntp == synchronized+inactive" 19 | message: NTP not active 20 | - warn: 21 | when: "timezone != UTC" 22 | message: "Non UTC timezone can interfere with system function" 23 | - pass: 24 | when: "ntp == synchronized+active" 25 | message: System clock is synchronized 26 | - pass: 27 | when: "timezone == UTC" 28 | message: "timezone set to UTC" 29 | 30 | -------------------------------------------------------------------------------- /examples/preflight/remote/tcp-connect.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: connect 5 | spec: 6 | remoteCollectors: 7 | - tcpConnect: 8 | collectorName: weave host 1 9 | address: 10.128.0.2:6783 10 | analyzers: 11 | - tcpConnect: 12 | collectorName: weave host 1 13 | outcomes: 14 | - fail: 15 | when: "connection-refused" 16 | message: Connection to weave on host 1 was refused 17 | - fail: 18 | when: "connection-timeout" 19 | message: Timed out connecting to weave on host 1 20 | - fail: 21 | when: "error" 22 | message: Unexpected error connecting to weave on host 1 23 | - pass: 24 | when: "connected" 25 | message: Successfully connected to weave on host 1 26 | -------------------------------------------------------------------------------- /examples/preflight/remote/tcp-load-balancer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: loadbalancer 5 | spec: 6 | remoteCollectors: 7 | - tcpLoadBalancer: 8 | collectorName: loadbalancer 9 | port: 7443 10 | address: 10.128.0.29:7444 11 | analyzers: 12 | - tcpLoadBalancer: 13 | collectorName: loadbalancer 14 | outcomes: 15 | - fail: 16 | when: "invalid-address" 17 | message: The Load Balancer address is not valid. 18 | - fail: 19 | when: "connection-refused" 20 | message: Connection to port 7443 via load balancer was refused. 21 | - fail: 22 | when: "address-in-use" 23 | message: Another process was already listening on port 7443. 24 | - fail: 25 | when: "connection-timeout" 26 | message: Timed out connecting to port 7443 via load balancer. Check your firewall. 27 | - fail: 28 | when: "error" 29 | message: Unexpected port status 30 | - pass: 31 | when: "connected" 32 | message: Successfully connected to port 7443 via load balancer 33 | - warn: 34 | message: Unexpected port status 35 | -------------------------------------------------------------------------------- /examples/preflight/remote/tcp-port.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: port 5 | spec: 6 | remoteCollectors: 7 | - tcpPortStatus: 8 | collectorName: k8s 9 | port: 7443 10 | analyzers: 11 | - tcpPortStatus: 12 | collectorName: k8s 13 | outcomes: 14 | - fail: 15 | when: "connection-refused" 16 | message: Connection to port 7443 was refused. 17 | - fail: 18 | when: "address-in-use" 19 | message: Another process was already listening on port 7443. 20 | - fail: 21 | when: "connection-timeout" 22 | message: Timed out connecting to port 7443. Check your firewall. 23 | - fail: 24 | when: "error" 25 | message: Unexpected port status 26 | - pass: 27 | when: "connected" 28 | message: Port 7443 is open 29 | - warn: 30 | message: Unexpected port status 31 | -------------------------------------------------------------------------------- /examples/preflight/remote/timezone.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: timezone 5 | spec: 6 | remoteCollectors: 7 | - time: {} 8 | analyzers: 9 | - time: 10 | outcomes: 11 | - pass: 12 | when: "timezone == UTC" 13 | message: Timezone is UTC 14 | - fail: 15 | when: "timezone != UTC" 16 | message: Timezone is not UTC 17 | -------------------------------------------------------------------------------- /examples/preflight/remote/udp-port.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: port 5 | spec: 6 | collectors: 7 | - udpPortStatus: 8 | collectorName: flannel 9 | port: 8472 10 | analyzers: 11 | - udpPortStatus: 12 | collectorName: flannel 13 | outcomes: 14 | - fail: 15 | when: "address-in-use" 16 | message: Another process was already listening on port 8472. 17 | - fail: 18 | when: "error" 19 | message: Unexpected port status 20 | - pass: 21 | when: "connected" 22 | message: Port 8472 is open 23 | - warn: 24 | message: Unexpected port status 25 | -------------------------------------------------------------------------------- /examples/preflight/text-analyze.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: Preflight 3 | metadata: 4 | name: example-preflight-checks 5 | spec: 6 | collectors: 7 | - data: 8 | name: config/replicas.txt 9 | data: "2" 10 | analyzers: 11 | - textAnalyze: 12 | checkName: Replica Count 13 | fileName: config/replicas.txt 14 | regexGroups: '(?P\d+)' 15 | outcomes: 16 | - fail: 17 | when: "Replicas < 5" 18 | message: That's not enough replicas! 19 | - pass: 20 | message: You've selected at leat 5 replicas -------------------------------------------------------------------------------- /examples/preflight/values-complex-small.yaml: -------------------------------------------------------------------------------- 1 | clusterVersion: 2 | enabled: true 3 | minVersion: "1.24.0" 4 | recommendedVersion: "1.28.0" -------------------------------------------------------------------------------- /examples/preflight/values-simple.yaml: -------------------------------------------------------------------------------- 1 | # Values for v1beta3-templated-from-v1beta2.yaml 2 | 3 | kubernetes: 4 | enabled: true 5 | minVersion: "1.22.0" 6 | recommendedVersion: "1.29.0" 7 | 8 | storage: 9 | enabled: true 10 | className: "default" 11 | 12 | cluster: 13 | minNodes: 3 14 | recommendedNodes: 5 15 | minCPU: 4 16 | 17 | node: 18 | minMemoryGi: 8 19 | recommendedMemoryGi: 32 20 | minEphemeralGi: 40 21 | recommendedEphemeralGi: 100 22 | 23 | ingress: 24 | enabled: true 25 | type: "Contour" 26 | contour: 27 | crdName: "ingressroutes.contour.heptio.com" 28 | crdGroup: "heptio.com" 29 | crdKind: "IngressRoute" 30 | crdVersion: "v1beta1 or later served version" 31 | 32 | runtime: 33 | enabled: true 34 | name: "containerd" 35 | cgroupDriver: "systemd" 36 | criSocket: "/run/containerd/containerd.sock" 37 | 38 | distribution: 39 | enabled: true 40 | unsupported: 41 | - docker-desktop 42 | - microk8s 43 | - minikube 44 | supported: 45 | - eks 46 | - gke 47 | - aks 48 | - kurl 49 | - digitalocean 50 | - rke2 51 | - k3s 52 | - oke 53 | - kind 54 | 55 | nodeChecks: 56 | enabled: true 57 | count: 58 | enabled: true 59 | cpu: 60 | enabled: true 61 | memory: 62 | enabled: true 63 | ephemeral: 64 | enabled: true 65 | 66 | 67 | -------------------------------------------------------------------------------- /examples/preflight/values-v1beta3-1.yaml: -------------------------------------------------------------------------------- 1 | # Minimal values for v1beta3-templated-from-v1beta2.yaml 2 | 3 | kubernetes: 4 | enabled: true 5 | minVersion: "1.22.0" 6 | recommendedVersion: "1.29.0" 7 | 8 | storage: 9 | enabled: true 10 | className: "default" 11 | 12 | nodeChecks: 13 | cpu: 14 | enabled: false 15 | ephemeral: 16 | enabled: false -------------------------------------------------------------------------------- /examples/preflight/values-v1beta3-2.yaml: -------------------------------------------------------------------------------- 1 | cluster: 2 | minNodes: 3 3 | recommendedNodes: 3 4 | minCPU: 4 5 | 6 | node: 7 | minMemoryGi: 8 8 | recommendedMemoryGi: 16 9 | minEphemeralGi: 40 10 | recommendedEphemeralGi: 40 -------------------------------------------------------------------------------- /examples/preflight/values-v1beta3-3.yaml: -------------------------------------------------------------------------------- 1 | ingress: 2 | enabled: true 3 | type: "Contour" 4 | 5 | runtime: 6 | enabled: true 7 | 8 | distribution: 9 | enabled: true 10 | 11 | nodeChecks: 12 | enabled: true 13 | count: 14 | enabled: true 15 | cpu: 16 | enabled: true 17 | memory: 18 | enabled: true 19 | ephemeral: 20 | enabled: true 21 | 22 | 23 | kubernetes: 24 | enabled: false 25 | minVersion: "1.22.0" 26 | recommendedVersion: "1.29.0" -------------------------------------------------------------------------------- /examples/redact/e2e.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: Redactor 3 | metadata: 4 | name: e2e-redactor 5 | spec: 6 | redactors: 7 | - name: redact-static-text 8 | removals: 9 | values: 10 | - static 11 | -------------------------------------------------------------------------------- /examples/sdk/helm-template/README.md: -------------------------------------------------------------------------------- 1 | ## Parse troubleshoot specs from a helm chart 2 | 3 | This is an example of using troubleshoot's `loader.LoadSpecs` API to load specs from rendered manifests in a helm chart. The chart in this example contains a kubernetes `Secret` & `ConfigMap` with troubleshoot specs, as well as a troubleshoot custom resource. The custom resource is behind a values flag and does not get rendered by default. The code adds this flag to ensure that the manifest is rendered so as to load it. 4 | 5 | The manifests are rendered with the equivalent of `helm template --values values.yaml` where the output is a YAML multidoc. `loader.LoadSpecs` will take the YAML multidoc as an input and extract troubleshoot specs inside kuberenetes `Secrets` and `ConfigMap`s, and any troubleshoot custom resources found. 6 | 7 | This application always uses the local version of troubleshoot so as to build using the latest version of the library. This ensures that the example is kept in sync with new features. 8 | 9 | ### Running 10 | 11 | ```go 12 | go mod tidy # sync go modules 13 | go run main.go # run the application. This should print out a YAML multidoc of the loaded troubleshoot specs 14 | ``` 15 | -------------------------------------------------------------------------------- /examples/sdk/helm-template/mychart-0.1.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/replicatedhq/troubleshoot/05a7a2092eef861d1b84d5e4570e62e3c138abdc/examples/sdk/helm-template/mychart-0.1.0.tgz -------------------------------------------------------------------------------- /examples/support-bundle/db-collector.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: dbs-collector 5 | spec: 6 | collectors: 7 | - mysql: 8 | collectorName: mysql 9 | uri: 'root:my-secret-pw@tcp(localhost:3306)/mysql' 10 | parameters: 11 | - character_set_server 12 | - collation_server 13 | - init_connect 14 | - innodb_file_format 15 | - innodb_large_prefix 16 | - innodb_strict_mode 17 | - log_bin_trust_function_creators 18 | - redis: 19 | collectorName: my-redis 20 | uri: rediss://default:replicated@server:6380 21 | -------------------------------------------------------------------------------- /examples/support-bundle/sample-analyzers.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: Analyzer 3 | metadata: 4 | name: a 5 | spec: 6 | analyzers: 7 | - distribution: 8 | outcomes: 9 | - fail: 10 | when: "= dockerdesktop" 11 | message: "docker for desktop is not allowed" 12 | # - fail: 13 | # when: "microk8s" 14 | # message: "mickrk8s is not prod" 15 | - fail: 16 | when: "!= openshift" 17 | message: "this should fail on anything other than openshift" 18 | - warn: 19 | when: "!= eks" 20 | message: "YMMV on not eks" 21 | - pass: 22 | message: "good work" 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/support-bundle/sample-collect-entire-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: example-collect-all-configmap-data 5 | spec: 6 | collectors: 7 | - configMap: 8 | namespace: kurl 9 | name: kurl-current-config 10 | includeAllData: true 11 | - configMap: 12 | namespace: kurl 13 | name: kurl-last-config 14 | includeAllData: true 15 | 16 | -------------------------------------------------------------------------------- /examples/support-bundle/sample-collectors.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: Collector 3 | metadata: 4 | name: collector-sample 5 | spec: 6 | collectors: [] 7 | -------------------------------------------------------------------------------- /examples/support-bundle/v1beta3/cross-namespace-secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta3 2 | kind: SupportBundle 3 | metadata: 4 | name: cross-namespace-example 5 | spec: 6 | collectors: 7 | # Database in one namespace, secret in another 8 | - postgres: 9 | collectorName: shared-database 10 | uri: 11 | valueFrom: 12 | secretKeyRef: 13 | name: shared-postgres-connection 14 | key: uri 15 | namespace: shared-services # Secret is in a different namespace 16 | 17 | # Redis accessing centralized credentials 18 | - redis: 19 | collectorName: shared-cache 20 | uri: 21 | valueFrom: 22 | secretKeyRef: 23 | name: shared-redis-creds 24 | key: uri 25 | namespace: platform-credentials 26 | --- 27 | # This secret would be in the 'shared-services' namespace 28 | apiVersion: v1 29 | kind: Secret 30 | metadata: 31 | name: shared-postgres-connection 32 | namespace: shared-services 33 | type: Opaque 34 | stringData: 35 | uri: "postgresql://shared:password@shared-postgres.shared-services.svc:5432/shared_db" 36 | --- 37 | # This secret would be in the 'platform-credentials' namespace 38 | apiVersion: v1 39 | kind: Secret 40 | metadata: 41 | name: shared-redis-creds 42 | namespace: platform-credentials 43 | type: Opaque 44 | stringData: 45 | uri: "redis://shared-redis.shared-services.svc:6379/0" 46 | -------------------------------------------------------------------------------- /examples/support-bundle/v1beta3/postgres-with-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta3 2 | kind: SupportBundle 3 | metadata: 4 | name: postgres-support-bundle 5 | spec: 6 | collectors: 7 | - postgres: 8 | collectorName: main-database 9 | uri: 10 | valueFrom: 11 | secretKeyRef: 12 | name: postgres-connection 13 | key: connection-uri 14 | --- 15 | apiVersion: v1 16 | kind: Secret 17 | metadata: 18 | name: postgres-connection 19 | type: Opaque 20 | stringData: 21 | connection-uri: "postgresql://myuser:mypassword@postgres.default.svc:5432/mydb?sslmode=require" 22 | -------------------------------------------------------------------------------- /examples/support-bundle/v1beta3/postgres-with-tls.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta3 2 | kind: SupportBundle 3 | metadata: 4 | name: postgres-tls-support-bundle 5 | spec: 6 | collectors: 7 | - postgres: 8 | collectorName: secure-database 9 | uri: 10 | valueFrom: 11 | secretKeyRef: 12 | name: postgres-connection 13 | key: connection-uri 14 | tls: 15 | cacert: 16 | valueFrom: 17 | secretKeyRef: 18 | name: postgres-tls 19 | key: ca.crt 20 | clientCert: 21 | valueFrom: 22 | secretKeyRef: 23 | name: postgres-tls 24 | key: tls.crt 25 | clientKey: 26 | valueFrom: 27 | secretKeyRef: 28 | name: postgres-tls 29 | key: tls.key 30 | --- 31 | apiVersion: v1 32 | kind: Secret 33 | metadata: 34 | name: postgres-connection 35 | type: Opaque 36 | stringData: 37 | connection-uri: "postgresql://myuser:mypassword@postgres.default.svc:5432/mydb?sslmode=verify-full" 38 | --- 39 | apiVersion: v1 40 | kind: Secret 41 | metadata: 42 | name: postgres-tls 43 | type: kubernetes.io/tls 44 | data: 45 | ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi4uLgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t 46 | tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi4uLgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t 47 | tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi4uLgotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t 48 | -------------------------------------------------------------------------------- /examples/support-bundle/weave-analyzer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: collector-sample 5 | spec: 6 | collectors: 7 | - exec: 8 | collectorName: weave-report 9 | command: 10 | - /home/weave/weave 11 | args: 12 | - --local 13 | - report 14 | containerName: weave 15 | exclude: "" 16 | name: kots/kurl/weave 17 | namespace: kube-system 18 | selector: 19 | - name=weave-net 20 | timeout: 10s 21 | 22 | analyzers: 23 | - weaveReport: 24 | reportFileGlob: kots/kurl/weave/kube-system/*/weave-report-stdout.txt 25 | -------------------------------------------------------------------------------- /examples/test-error-messages/helm-builtins-v1beta3.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta3 2 | kind: Preflight 3 | metadata: 4 | name: helm-builtins-example 5 | labels: 6 | release: {{ .Release.Name }} 7 | spec: 8 | analyzers: 9 | - docString: | 10 | Title: Example using Helm builtin objects 11 | Requirement: Demonstrates .Values, .Release, .Chart, etc. 12 | 13 | Supported Helm builtin objects: 14 | - .Values.* - User-provided values 15 | - .Release.Name - Release name (default: "preflight") 16 | - .Release.Namespace - Release namespace (default: "default") 17 | - .Release.IsInstall - Whether this is an install (true) 18 | - .Release.IsUpgrade - Whether this is an upgrade (false) 19 | - .Release.Revision - Release revision (1) 20 | - .Chart.Name - Chart name 21 | - .Chart.Version - Chart version 22 | - .Capabilities.KubeVersion - Kubernetes version capabilities 23 | clusterVersion: 24 | checkName: Kubernetes version check in {{ .Release.Namespace }} 25 | outcomes: 26 | - fail: 27 | when: '< {{ .Values.minVersion | default "1.19.0" }}' 28 | message: | 29 | Release {{ .Release.Name }} requires Kubernetes {{ .Values.minVersion | default "1.19.0" }} or later. 30 | Chart: {{ .Chart.Name }} 31 | - pass: 32 | when: '>= {{ .Values.minVersion | default "1.19.0" }}' 33 | message: Kubernetes version is supported for release {{ .Release.Name }} 34 | -------------------------------------------------------------------------------- /examples/test-error-messages/invalid-collectors-analyzers.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta3 2 | kind: SupportBundle 3 | metadata: 4 | name: invalid-collectors 5 | spec: 6 | collectors: 7 | # Unknown collector type 8 | - notACollector: {} 9 | # Known collector but missing required fields (e.g., ceph requires namespace) 10 | - ceph: {} 11 | # Field exists but wrong type (should be a list) 12 | hostCollectors: "not-a-list" 13 | analyzers: 14 | # Unknown analyzer type 15 | - notAnAnalyzer: {} 16 | # Known analyzer missing required 'outcomes' 17 | - cephStatus: 18 | namespace: default 19 | 20 | -------------------------------------------------------------------------------- /examples/test-error-messages/invalid-yaml-v1beta3.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta3 2 | kind: Preflight 3 | metadata 4 | name: invalid-yaml 5 | spec: 6 | analyzers: 7 | - clusterVersion: 8 | checkName: Kubernetes version 9 | -------------------------------------------------------------------------------- /examples/test-error-messages/missing-apiversion-v1beta3.yaml: -------------------------------------------------------------------------------- 1 | kind: Preflight 2 | metadata: 3 | name: missing-apiversion 4 | spec: 5 | analyzers: 6 | - clusterVersion: 7 | checkName: Kubernetes version 8 | outcomes: 9 | - pass: 10 | when: '>= 1.19.0' 11 | message: Kubernetes version is supported 12 | -------------------------------------------------------------------------------- /examples/test-error-messages/missing-metadata-v1beta3.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta3 2 | kind: Preflight 3 | spec: 4 | analyzers: 5 | - clusterVersion: 6 | checkName: Kubernetes version 7 | outcomes: 8 | - pass: 9 | when: '>= 1.19.0' 10 | message: Kubernetes version is supported 11 | -------------------------------------------------------------------------------- /examples/test-error-messages/no-analyzers-v1beta3.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta3 2 | kind: Preflight 3 | metadata: 4 | name: no-analyzers 5 | spec: 6 | collectors: 7 | - clusterInfo: {} 8 | -------------------------------------------------------------------------------- /examples/test-error-messages/simple-no-template-v1beta3.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta3 2 | kind: Preflight 3 | metadata: 4 | name: simple-no-template 5 | spec: 6 | analyzers: 7 | - docString: | 8 | Title: Kubernetes Version Check 9 | Requirement: Kubernetes 1.19.0 or later 10 | clusterVersion: 11 | checkName: Kubernetes version 12 | outcomes: 13 | - fail: 14 | when: '< 1.19.0' 15 | message: Kubernetes version must be at least 1.19.0 16 | - pass: 17 | when: '>= 1.19.0' 18 | message: Kubernetes version is supported 19 | -------------------------------------------------------------------------------- /examples/test-error-messages/support-bundle-no-collectors-v1beta3.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta3 2 | kind: SupportBundle 3 | metadata: 4 | name: no-collectors 5 | spec: 6 | analyzers: 7 | - clusterVersion: 8 | checkName: Kubernetes version 9 | outcomes: 10 | - pass: 11 | when: '>= 1.19.0' 12 | message: Kubernetes version is supported 13 | -------------------------------------------------------------------------------- /examples/test-error-messages/support-bundle-valid-v1beta3.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta3 2 | kind: SupportBundle 3 | metadata: 4 | name: valid-support-bundle 5 | spec: 6 | collectors: 7 | - clusterInfo: {} 8 | - clusterResources: {} 9 | analyzers: 10 | - clusterVersion: 11 | checkName: Kubernetes version 12 | outcomes: 13 | - pass: 14 | when: '>= 1.19.0' 15 | message: Kubernetes version is supported 16 | -------------------------------------------------------------------------------- /examples/test-error-messages/valid-v1beta3.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta3 2 | kind: Preflight 3 | metadata: 4 | name: valid-preflight 5 | spec: 6 | analyzers: 7 | - docString: | 8 | Title: Test Analyzer 9 | Requirement: Test requirement 10 | clusterVersion: 11 | checkName: Kubernetes version 12 | outcomes: 13 | - pass: 14 | when: '>= 1.19.0' 15 | message: Kubernetes version is supported 16 | -------------------------------------------------------------------------------- /examples/test-error-messages/values-empty.yaml: -------------------------------------------------------------------------------- 1 | # Empty values file for v1beta3 specs without templates 2 | {} 3 | -------------------------------------------------------------------------------- /examples/test-error-messages/values-helm-builtins.yaml: -------------------------------------------------------------------------------- 1 | minVersion: "1.19.0" 2 | -------------------------------------------------------------------------------- /examples/test-error-messages/wrong-apiversion-v1beta3.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: Preflight 3 | metadata: 4 | name: wrong-version 5 | spec: 6 | analyzers: 7 | - clusterVersion: 8 | checkName: Kubernetes version 9 | outcomes: 10 | - pass: 11 | when: '>= 1.19.0' 12 | message: Kubernetes version is supported 13 | -------------------------------------------------------------------------------- /examples/troubleshoot/longhorn.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: longhorn 5 | spec: 6 | collectors: 7 | - longhorn: {} 8 | analyzers: 9 | - longhorn: {} 10 | -------------------------------------------------------------------------------- /examples/troubleshoot/sample-analyzers.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: Analyzer 3 | metadata: 4 | name: a 5 | spec: 6 | analyzers: 7 | - distribution: 8 | outcomes: 9 | - fail: 10 | when: "= dockerdesktop" 11 | message: "docker for desktop is not allowed" 12 | # - fail: 13 | # when: "microk8s" 14 | # message: "mickrk8s is not prod" 15 | - fail: 16 | when: "!= openshift" 17 | message: "this should fail on anything other than openshift" 18 | - warn: 19 | when: "!= eks" 20 | message: "YMMV on not eks" 21 | - pass: 22 | message: "good work" 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/troubleshoot/sample-troubleshoot.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: Collector 3 | metadata: 4 | name: collector-sample 5 | spec: 6 | collectors: [] 7 | -------------------------------------------------------------------------------- /examples/troubleshoot/sysctl.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: sysctl 5 | spec: 6 | collectors: 7 | - sysctl: 8 | image: debian:buster-slim 9 | analyzers: 10 | - sysctl: 11 | checkName: IP forwarding enabled 12 | outcomes: 13 | - fail: 14 | when: "net.ipv4.ip_forward = 0" 15 | message: "IP forwarding is not enabled" 16 | -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc.. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ -------------------------------------------------------------------------------- /internal/specs/configmaps.go: -------------------------------------------------------------------------------- 1 | package specs 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/pkg/errors" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | "k8s.io/client-go/kubernetes" 9 | "k8s.io/klog/v2" 10 | ) 11 | 12 | func LoadFromConfigMap(ctx context.Context, client kubernetes.Interface, ns string, name string) (map[string]string, error) { 13 | foundConfigMap, err := client.CoreV1().ConfigMaps(ns).Get(ctx, name, metav1.GetOptions{}) 14 | if err != nil { 15 | return nil, errors.Wrap(err, "failed to get configmap") 16 | } 17 | 18 | klog.V(1).InfoS("Loaded data from config map", "name", 19 | foundConfigMap.Name, "namespace", foundConfigMap.Namespace, 20 | ) 21 | 22 | return foundConfigMap.Data, nil 23 | } 24 | 25 | func LoadFromConfigMapMatchingLabel(ctx context.Context, client kubernetes.Interface, label string, ns string, key string) ([]string, error) { 26 | var configMapMatchingKey []string 27 | 28 | configMaps, err := client.CoreV1().ConfigMaps(ns).List(ctx, metav1.ListOptions{LabelSelector: label}) 29 | if err != nil { 30 | return nil, errors.Wrap(err, "failed to search for configmaps in the cluster") 31 | } 32 | 33 | for _, configMap := range configMaps.Items { 34 | spec, ok := configMap.Data[key] 35 | if !ok { 36 | continue 37 | } 38 | 39 | klog.V(1).InfoS("Loaded spec from config map", "name", configMap.Name, 40 | "namespace", configMap.Namespace, "data key", key, "label selector", label, 41 | ) 42 | configMapMatchingKey = append(configMapMatchingKey, string(spec)) 43 | } 44 | 45 | return configMapMatchingKey, nil 46 | } 47 | -------------------------------------------------------------------------------- /internal/specs/secrets.go: -------------------------------------------------------------------------------- 1 | package specs 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/pkg/errors" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | "k8s.io/client-go/kubernetes" 9 | "k8s.io/klog/v2" 10 | ) 11 | 12 | func LoadFromSecret(ctx context.Context, client kubernetes.Interface, ns string, name string) (map[string][]byte, error) { 13 | foundSecret, err := client.CoreV1().Secrets(ns).Get(ctx, name, metav1.GetOptions{}) 14 | if err != nil { 15 | return nil, errors.Wrap(err, "failed to get secret") 16 | } 17 | 18 | return foundSecret.Data, nil 19 | } 20 | 21 | func LoadFromSecretMatchingLabel(ctx context.Context, client kubernetes.Interface, label string, ns string, key string) ([]string, error) { 22 | var secretsMatchingKey []string 23 | 24 | secrets, err := client.CoreV1().Secrets(ns).List(ctx, metav1.ListOptions{LabelSelector: label}) 25 | if err != nil { 26 | return nil, errors.Wrap(err, "failed to search for secrets in the cluster") 27 | } 28 | 29 | for _, secret := range secrets.Items { 30 | spec, ok := secret.Data[key] 31 | if !ok { 32 | continue 33 | } 34 | 35 | klog.V(1).InfoS("Loaded spec from secret", "name", secret.Name, 36 | "namespace", secret.Namespace, "data key", key, "label selector", label, 37 | ) 38 | secretsMatchingKey = append(secretsMatchingKey, string(spec)) 39 | } 40 | 41 | return secretsMatchingKey, nil 42 | } 43 | -------------------------------------------------------------------------------- /pkg/analyze/comparison.go: -------------------------------------------------------------------------------- 1 | package analyzer 2 | 3 | import "fmt" 4 | 5 | type ComparisonOperator int 6 | 7 | const ( 8 | Unknown ComparisonOperator = iota 9 | Equal 10 | NotEqual 11 | GreaterThan 12 | GreaterThanOrEqual 13 | LessThan 14 | LessThanOrEqual 15 | ) 16 | 17 | func ParseComparisonOperator(s string) (ComparisonOperator, error) { 18 | switch s { 19 | case "=", "==", "===": 20 | return Equal, nil 21 | case "!=", "!==": 22 | return NotEqual, nil 23 | case "<": 24 | return LessThan, nil 25 | case ">": 26 | return GreaterThan, nil 27 | case "<=": 28 | return LessThanOrEqual, nil 29 | case ">=": 30 | return GreaterThanOrEqual, nil 31 | } 32 | 33 | return Unknown, fmt.Errorf("unknown operator: %s", s) 34 | } 35 | -------------------------------------------------------------------------------- /pkg/analyze/download_test.go: -------------------------------------------------------------------------------- 1 | package analyzer 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "testing" 7 | 8 | "github.com/replicatedhq/troubleshoot/internal/testutils" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestDownloadAndExtractSupportBundle(t *testing.T) { 13 | // TODO: Add tests for web url downloads 14 | tests := []struct { 15 | name string 16 | bundleURL string 17 | wantErr bool 18 | }{ 19 | { 20 | name: "extract a bundle from a local file path", 21 | bundleURL: filepath.Join(testutils.FileDir(), "../../testdata/supportbundle/support-bundle.tar.gz"), 22 | wantErr: false, 23 | }, 24 | { 25 | name: "extract a bundle from a non-existent file path", 26 | bundleURL: "/home/someone/gibberish", 27 | wantErr: true, 28 | }, 29 | { 30 | name: "extract an invalid support bundle which has no version file", 31 | bundleURL: filepath.Join(testutils.FileDir(), "../../testdata/supportbundle/missing-version.tar.gz"), 32 | wantErr: true, 33 | }, 34 | } 35 | for _, tt := range tests { 36 | t.Run(tt.name, func(t *testing.T) { 37 | tmpDir, bundleDir, err := DownloadAndExtractSupportBundle(tt.bundleURL) 38 | defer os.RemoveAll(tmpDir) // clean up. Ignore error 39 | 40 | if err == nil { 41 | assert.DirExists(t, bundleDir) 42 | assert.FileExists(t, filepath.Join(bundleDir, "version.yaml")) 43 | } else { 44 | assert.Equal(t, "", tmpDir) 45 | assert.Equal(t, "", bundleDir) 46 | } 47 | }) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /pkg/analyze/host_json_compare.go: -------------------------------------------------------------------------------- 1 | package analyzer 2 | 3 | import ( 4 | troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" 5 | ) 6 | 7 | type AnalyzeHostJsonCompare struct { 8 | hostAnalyzer *troubleshootv1beta2.JsonCompare 9 | } 10 | 11 | func (a *AnalyzeHostJsonCompare) Title() string { 12 | return jsonCompareTitle(a.hostAnalyzer) 13 | } 14 | 15 | func (a *AnalyzeHostJsonCompare) IsExcluded() (bool, error) { 16 | return isExcluded(a.hostAnalyzer.Exclude) 17 | } 18 | 19 | func (a *AnalyzeHostJsonCompare) Analyze( 20 | getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents, 21 | ) ([]*AnalyzeResult, error) { 22 | result, err := analyzeJsonCompare(a.hostAnalyzer, getCollectedFileContents, a.Title()) 23 | if err != nil { 24 | return nil, err 25 | } 26 | result.Strict = a.hostAnalyzer.Strict.BoolOrDefaultFalse() 27 | return []*AnalyzeResult{result}, nil 28 | } 29 | -------------------------------------------------------------------------------- /pkg/analyze/host_text_analyze.go: -------------------------------------------------------------------------------- 1 | package analyzer 2 | 3 | import ( 4 | troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" 5 | ) 6 | 7 | // AnalyzeHostTextAnalyze implements HostAnalyzer interface 8 | type AnalyzeHostTextAnalyze struct { 9 | hostAnalyzer *troubleshootv1beta2.TextAnalyze 10 | } 11 | 12 | func (a *AnalyzeHostTextAnalyze) Title() string { 13 | return hostAnalyzerTitleOrDefault(a.hostAnalyzer.AnalyzeMeta, "Regular Expression") 14 | } 15 | 16 | func (a *AnalyzeHostTextAnalyze) IsExcluded() (bool, error) { 17 | return isExcluded(a.hostAnalyzer.Exclude) 18 | } 19 | 20 | func (a *AnalyzeHostTextAnalyze) Analyze( 21 | getCollectedFileContents func(string) ([]byte, error), findFiles getChildCollectedFileContents, 22 | ) ([]*AnalyzeResult, error) { 23 | return analyzeTextAnalyze(a.hostAnalyzer, findFiles, a.Title()) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/analyze/http_analyze.go: -------------------------------------------------------------------------------- 1 | package analyzer 2 | 3 | import ( 4 | troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" 5 | ) 6 | 7 | type AnalyzeHTTPAnalyze struct { 8 | analyzer *troubleshootv1beta2.HTTPAnalyze 9 | } 10 | 11 | func (a *AnalyzeHTTPAnalyze) Title() string { 12 | checkName := a.analyzer.CheckName 13 | if checkName == "" { 14 | checkName = a.analyzer.CollectorName 15 | } 16 | 17 | return checkName 18 | } 19 | 20 | func (a *AnalyzeHTTPAnalyze) IsExcluded() (bool, error) { 21 | return isExcluded(a.analyzer.Exclude) 22 | } 23 | 24 | func (a *AnalyzeHTTPAnalyze) Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) { 25 | fileName := "result.json" 26 | if a.analyzer.CollectorName != "" { 27 | fileName = a.analyzer.CollectorName + ".json" 28 | } 29 | return analyzeHTTPResult(a.analyzer, fileName, getFile, a.Title()) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/apis/addtoscheme_troubleshoot_v1beta1.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc.. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package apis 18 | 19 | import ( 20 | "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1" 21 | ) 22 | 23 | func init() { 24 | // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back 25 | AddToSchemes = append(AddToSchemes, v1beta1.SchemeBuilder.AddToScheme) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/apis/addtoscheme_troubleshoot_v1beta2.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc.. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package apis 18 | 19 | import ( 20 | "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" 21 | ) 22 | 23 | func init() { 24 | // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back 25 | AddToSchemes = append(AddToSchemes, v1beta2.SchemeBuilder.AddToScheme) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/apis/apis.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc.. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package apis contains Kubernetes API groups. 18 | package apis 19 | 20 | import ( 21 | "k8s.io/apimachinery/pkg/runtime" 22 | ) 23 | 24 | // AddToSchemes may be used to add all resources defined in the project to a Scheme 25 | var AddToSchemes runtime.SchemeBuilder 26 | 27 | // AddToScheme adds all Resources to the Scheme 28 | func AddToScheme(s *runtime.Scheme) error { 29 | return AddToSchemes.AddToScheme(s) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/apis/troubleshoot/group.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc.. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package troubleshoot contains troubleshoot API versions 18 | package troubleshoot 19 | -------------------------------------------------------------------------------- /pkg/apis/troubleshoot/v1beta1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc.. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package v1beta1 contains API Schema definitions for the troubleshoot v1beta1 API group 18 | // +k8s:openapi-gen=true 19 | // +k8s:deepcopy-gen=package,register 20 | // +k8s:conversion-gen=github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot 21 | // +k8s:defaulter-gen=TypeMeta 22 | // +groupName=troubleshoot.replicated.com 23 | package v1beta1 24 | -------------------------------------------------------------------------------- /pkg/apis/troubleshoot/v1beta1/redact_shared.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | type Regex struct { 4 | Selector string `json:"selector,omitempty" yaml:"selector,omitempty"` 5 | Redactor string `json:"redactor,omitempty" yaml:"redactor,omitempty"` 6 | } 7 | 8 | type FileSelector struct { 9 | File string `json:"file,omitempty" yaml:"file,omitempty"` 10 | Files []string `json:"files,omitempty" yaml:"files,omitempty"` 11 | } 12 | 13 | type Removals struct { 14 | Values []string `json:"values,omitempty" yaml:"values,omitempty"` 15 | Regex []Regex `json:"regex,omitempty" yaml:"regex,omitempty"` 16 | YamlPath []string `json:"yamlPath,omitempty" yaml:"yamlPath,omitempty"` 17 | } 18 | 19 | type Redact struct { 20 | Name string `json:"name,omitempty" yaml:"name,omitempty"` 21 | FileSelector FileSelector `json:"fileSelector,omitempty" yaml:"fileSelector,omitempty"` 22 | Removals Removals `json:"removals,omitempty" yaml:"removals,omitempty"` 23 | } 24 | -------------------------------------------------------------------------------- /pkg/apis/troubleshoot/v1beta1/version.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | type SupportBundleVersionSpec struct { 4 | VersionNumber string `json:"versionNumber" yaml:"versionNumber"` 5 | } 6 | 7 | type SupportBundleVersion struct { 8 | ApiVersion string `json:"apiVersion" yaml:"apiVersion"` 9 | Kind string `json:"kind" yaml:"kind"` 10 | Spec SupportBundleVersionSpec `json:"spec" yaml:"spec"` 11 | } 12 | -------------------------------------------------------------------------------- /pkg/apis/troubleshoot/v1beta2/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc.. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package v1beta2 contains API Schema definitions for the troubleshoot v1beta2 API group 18 | // +k8s:openapi-gen=true 19 | // +k8s:deepcopy-gen=package,register 20 | // +k8s:conversion-gen=github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot 21 | // +k8s:defaulter-gen=TypeMeta 22 | // +groupName=troubleshoot.sh 23 | package v1beta2 24 | -------------------------------------------------------------------------------- /pkg/apis/troubleshoot/v1beta2/outcome.go: -------------------------------------------------------------------------------- 1 | package v1beta2 2 | 3 | type SingleOutcome struct { 4 | When string `json:"when,omitempty" yaml:"when,omitempty"` 5 | Message string `json:"message,omitempty" yaml:"message,omitempty"` 6 | URI string `json:"uri,omitempty" yaml:"uri,omitempty"` 7 | } 8 | 9 | type Outcome struct { 10 | Fail *SingleOutcome `json:"fail,omitempty" yaml:"fail,omitempty"` 11 | Warn *SingleOutcome `json:"warn,omitempty" yaml:"warn,omitempty"` 12 | Pass *SingleOutcome `json:"pass,omitempty" yaml:"pass,omitempty"` 13 | } 14 | -------------------------------------------------------------------------------- /pkg/apis/troubleshoot/v1beta2/redact_shared.go: -------------------------------------------------------------------------------- 1 | package v1beta2 2 | 3 | type Regex struct { 4 | Selector string `json:"selector,omitempty" yaml:"selector,omitempty"` 5 | Redactor string `json:"redactor,omitempty" yaml:"redactor,omitempty"` 6 | } 7 | 8 | type FileSelector struct { 9 | File string `json:"file,omitempty" yaml:"file,omitempty"` 10 | Files []string `json:"files,omitempty" yaml:"files,omitempty"` 11 | } 12 | 13 | type Removals struct { 14 | Values []string `json:"values,omitempty" yaml:"values,omitempty"` 15 | Regex []Regex `json:"regex,omitempty" yaml:"regex,omitempty"` 16 | YamlPath []string `json:"yamlPath,omitempty" yaml:"yamlPath,omitempty"` 17 | } 18 | 19 | type Redact struct { 20 | Name string `json:"name,omitempty" yaml:"name,omitempty"` 21 | FileSelector FileSelector `json:"fileSelector,omitempty" yaml:"fileSelector,omitempty"` 22 | Removals Removals `json:"removals,omitempty" yaml:"removals,omitempty"` 23 | } 24 | -------------------------------------------------------------------------------- /pkg/apis/troubleshoot/v1beta2/version.go: -------------------------------------------------------------------------------- 1 | package v1beta2 2 | 3 | type SupportBundleVersionSpec struct { 4 | VersionNumber string `json:"versionNumber" yaml:"versionNumber"` 5 | } 6 | 7 | type SupportBundleVersion struct { 8 | ApiVersion string `json:"apiVersion" yaml:"apiVersion"` 9 | Kind string `json:"kind" yaml:"kind"` 10 | Spec SupportBundleVersionSpec `json:"spec" yaml:"spec"` 11 | } 12 | -------------------------------------------------------------------------------- /pkg/apis/troubleshoot/v1beta3/doc.go: -------------------------------------------------------------------------------- 1 | // +k8s:deepcopy-gen=package 2 | // +k8s:defaulter-gen=TypeMeta 3 | // +groupName=troubleshoot.sh 4 | 5 | // Package v1beta3 is the v1beta3 version of the API. 6 | package v1beta3 7 | -------------------------------------------------------------------------------- /pkg/client/troubleshootclientset/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc.. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by client-gen. DO NOT EDIT. 17 | 18 | // This package has the automatically generated clientset. 19 | package troubleshootclientset 20 | -------------------------------------------------------------------------------- /pkg/client/troubleshootclientset/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc.. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by client-gen. DO NOT EDIT. 17 | 18 | // This package has the automatically generated fake clientset. 19 | package fake 20 | -------------------------------------------------------------------------------- /pkg/client/troubleshootclientset/scheme/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc.. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by client-gen. DO NOT EDIT. 17 | 18 | // This package contains the scheme of the automatically generated clientset. 19 | package scheme 20 | -------------------------------------------------------------------------------- /pkg/client/troubleshootclientset/typed/troubleshoot/v1beta1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc.. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by client-gen. DO NOT EDIT. 17 | 18 | // This package has the automatically generated typed clients. 19 | package v1beta1 20 | -------------------------------------------------------------------------------- /pkg/client/troubleshootclientset/typed/troubleshoot/v1beta1/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc.. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by client-gen. DO NOT EDIT. 17 | 18 | // Package fake has the automatically generated clients. 19 | package fake 20 | -------------------------------------------------------------------------------- /pkg/client/troubleshootclientset/typed/troubleshoot/v1beta1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc.. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by client-gen. DO NOT EDIT. 17 | 18 | package v1beta1 19 | 20 | type AnalyzerExpansion interface{} 21 | 22 | type CollectorExpansion interface{} 23 | 24 | type PreflightExpansion interface{} 25 | 26 | type RedactorExpansion interface{} 27 | 28 | type SupportBundleExpansion interface{} 29 | -------------------------------------------------------------------------------- /pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc.. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by client-gen. DO NOT EDIT. 17 | 18 | // This package has the automatically generated typed clients. 19 | package v1beta2 20 | -------------------------------------------------------------------------------- /pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc.. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by client-gen. DO NOT EDIT. 17 | 18 | // Package fake has the automatically generated clients. 19 | package fake 20 | -------------------------------------------------------------------------------- /pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Replicated, Inc.. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // Code generated by client-gen. DO NOT EDIT. 17 | 18 | package v1beta2 19 | 20 | type AnalyzerExpansion interface{} 21 | 22 | type CollectorExpansion interface{} 23 | 24 | type HostCollectorExpansion interface{} 25 | 26 | type HostPreflightExpansion interface{} 27 | 28 | type PreflightExpansion interface{} 29 | 30 | type RedactorExpansion interface{} 31 | 32 | type RemoteCollectorExpansion interface{} 33 | 34 | type SupportBundleExpansion interface{} 35 | -------------------------------------------------------------------------------- /pkg/collect/cluster_info_test.go: -------------------------------------------------------------------------------- 1 | package collect 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_ServerVersion(t *testing.T) { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /pkg/collect/collect_test.go: -------------------------------------------------------------------------------- 1 | package collect 2 | 3 | import ( 4 | "testing" 5 | 6 | troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func Test_ensureClusterResourcesFirst(t *testing.T) { 11 | 12 | testCases := []struct { 13 | name string 14 | want []*troubleshootv1beta2.Collect 15 | list []*troubleshootv1beta2.Collect 16 | }{ 17 | { 18 | name: "Reorg OK", 19 | want: []*troubleshootv1beta2.Collect{ 20 | { 21 | ClusterResources: &troubleshootv1beta2.ClusterResources{}, 22 | }, 23 | { 24 | Data: &troubleshootv1beta2.Data{}, 25 | }, 26 | }, 27 | list: []*troubleshootv1beta2.Collect{ 28 | { 29 | Data: &troubleshootv1beta2.Data{}, 30 | }, 31 | { 32 | ClusterResources: &troubleshootv1beta2.ClusterResources{}, 33 | }, 34 | }, 35 | }, 36 | } 37 | for _, tc := range testCases { 38 | t.Run(tc.name, func(t *testing.T) { 39 | got := EnsureClusterResourcesFirst(tc.list) 40 | assert.Equal(t, tc.want, got) 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /pkg/collect/database_shared.go: -------------------------------------------------------------------------------- 1 | package collect 2 | 3 | type DatabaseConnection struct { 4 | IsConnected bool `json:"isConnected"` 5 | Error string `json:"error,omitempty"` 6 | Version string `json:"version,omitempty"` 7 | Variables map[string]string `json:"variables,omitempty"` 8 | } 9 | -------------------------------------------------------------------------------- /pkg/collect/host_cgroup_others.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | 3 | package collect 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | func discoverConfiguration(_ string) (cgroupsResult, error) { 10 | return cgroupsResult{}, fmt.Errorf("Discovery of cgroups not inimplemented for this OS") 11 | } 12 | -------------------------------------------------------------------------------- /pkg/collect/host_collector_test.go: -------------------------------------------------------------------------------- 1 | package collect 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | type Params struct { 10 | Title string 11 | ProgressChan chan error 12 | } 13 | 14 | // Mock data for testing 15 | var testParams = RemoteCollectParams{ 16 | Title: "Test", 17 | ProgressChan: make(chan interface{}), 18 | } 19 | 20 | func Test_mapCollectorResultToOutput(t *testing.T) { 21 | result := map[string][]byte{ 22 | "key1": []byte(`{"file1": "data1", "file2": "data2"}`), 23 | "key2": []byte(`{"file3": "data3"}`), 24 | } 25 | 26 | // Expected output after processing 27 | expectedCollectedData := map[string][]byte{ 28 | "key1": []byte(`{"file1": "data1", "file2": "data2"}`), 29 | "key2": []byte(`{"file3": "data3"}`), 30 | } 31 | 32 | // Run the function logic 33 | allCollectedData := mapCollectorResultToOutput(result, testParams) 34 | 35 | // Validate the collected data 36 | for key, expected := range expectedCollectedData { 37 | assert.Equal(t, string(expected), string(allCollectedData[key]), "The collected data for key %s is incorrect", key) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pkg/collect/host_cpu_test.go: -------------------------------------------------------------------------------- 1 | package collect 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | 7 | troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestCollectHostCPU_Collect(t *testing.T) { 13 | c := &CollectHostCPU{ 14 | hostCollector: &troubleshootv1beta2.CPU{}, 15 | BundlePath: "", 16 | } 17 | got, err := c.Collect(nil) 18 | require.NoError(t, err) 19 | 20 | require.Contains(t, got, "host-collectors/system/cpu.json") 21 | values := got["host-collectors/system/cpu.json"] 22 | 23 | var m map[string]interface{} 24 | err = json.Unmarshal(values, &m) 25 | require.NoError(t, err) 26 | 27 | // Check if values exist. They will be different on different machines. 28 | assert.Equal(t, 4, len(m)) 29 | assert.Contains(t, m, "logicalCount") 30 | assert.Contains(t, m, "physicalCount") 31 | assert.Contains(t, m, "flags") 32 | assert.Contains(t, m, "machineArch") 33 | } 34 | -------------------------------------------------------------------------------- /pkg/collect/host_dns_test.go: -------------------------------------------------------------------------------- 1 | package collect 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestExtractSearchFromFQDN(t *testing.T) { 8 | tests := []struct { 9 | fqdn string 10 | name string 11 | expected string 12 | }{ 13 | {"foo.com.", "foo.com", ""}, 14 | {"bar.com", "bar.com", ""}, 15 | {"*.foo.testcluster.net.", "*", "foo.testcluster.net"}, 16 | } 17 | 18 | for _, test := range tests { 19 | t.Run(test.fqdn, func(t *testing.T) { 20 | result := extractSearchFromFQDN(test.fqdn, test.name) 21 | if result != test.expected { 22 | t.Errorf("extractSearchFromFQDN(%q, %q) = %q; want %q", test.fqdn, test.name, result, test.expected) 23 | } 24 | }) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /pkg/collect/host_filesystem_performance_darwin.go: -------------------------------------------------------------------------------- 1 | package collect 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" 6 | ) 7 | 8 | func collectHostFilesystemPerformance(hostCollector *troubleshootv1beta2.FilesystemPerformance, bundlePath string) (map[string][]byte, error) { 9 | return nil, errors.New("Filesystem performance collector is only implemented for Linux") 10 | } 11 | -------------------------------------------------------------------------------- /pkg/collect/host_filesystem_performance_windows.go: -------------------------------------------------------------------------------- 1 | package collect 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" 6 | ) 7 | 8 | func collectHostFilesystemPerformance(hostCollector *troubleshootv1beta2.FilesystemPerformance, bundlePath string) (map[string][]byte, error) { 9 | return nil, errors.New("Filesystem performance collector is only implemented for Linux") 10 | } 11 | -------------------------------------------------------------------------------- /pkg/collect/host_journald_test.go: -------------------------------------------------------------------------------- 1 | package collect 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" 8 | ) 9 | 10 | func TestGenerateOptions(t *testing.T) { 11 | jd := &troubleshootv1beta2.HostJournald{ 12 | System: true, 13 | Dmesg: true, 14 | Units: []string{"unit1", "unit2"}, 15 | Since: "2022-01-01", 16 | Until: "2022-01-31", 17 | Output: "json", 18 | Lines: 100, 19 | Reverse: true, 20 | Utc: true, 21 | } 22 | 23 | expectedOptions := []string{ 24 | "--system", 25 | "--dmesg", 26 | "-u", "unit1", 27 | "-u", "unit2", 28 | "--since", "2022-01-01", 29 | "--until", "2022-01-31", 30 | "--output", "json", 31 | "-n", "100", 32 | "--reverse", 33 | "--utc", 34 | "--no-pager", 35 | } 36 | 37 | options, err := generateOptions(jd) 38 | if err != nil { 39 | t.Fatalf("generateOptions failed with error: %v", err) 40 | } 41 | 42 | if !reflect.DeepEqual(options, expectedOptions) { 43 | t.Errorf("generateOptions returned incorrect options.\nExpected: %v\nActual: %v", expectedOptions, options) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pkg/collect/host_memory_test.go: -------------------------------------------------------------------------------- 1 | package collect 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | 7 | troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestCollectHostMemory_Collect(t *testing.T) { 13 | c := &CollectHostMemory{ 14 | hostCollector: &troubleshootv1beta2.Memory{}, 15 | BundlePath: "", 16 | } 17 | got, err := c.Collect(nil) 18 | require.NoError(t, err) 19 | 20 | require.Contains(t, got, "host-collectors/system/memory.json") 21 | values := got["host-collectors/system/memory.json"] 22 | 23 | var m map[string]int 24 | err = json.Unmarshal(values, &m) 25 | require.NoError(t, err) 26 | 27 | // Check if values exist. They will be different on different machines. 28 | assert.Equal(t, 1, len(m)) 29 | assert.Contains(t, m, "total") 30 | } 31 | -------------------------------------------------------------------------------- /pkg/collect/host_os_info_test.go: -------------------------------------------------------------------------------- 1 | package collect 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | 7 | troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestCollectHostOS_Collect(t *testing.T) { 13 | c := &CollectHostOS{ 14 | hostCollector: &troubleshootv1beta2.HostOS{}, 15 | BundlePath: "", 16 | } 17 | got, err := c.Collect(nil) 18 | require.NoError(t, err) 19 | 20 | require.Contains(t, got, "host-collectors/system/hostos_info.json") 21 | values := got["host-collectors/system/hostos_info.json"] 22 | 23 | var m map[string]string 24 | err = json.Unmarshal(values, &m) 25 | require.NoError(t, err) 26 | 27 | // Check if values exist. They will be different on different machines. 28 | assert.Equal(t, 5, len(m)) 29 | assert.Contains(t, m, "name") 30 | assert.Contains(t, m, "kernelVersion") 31 | assert.Contains(t, m, "platformVersion") 32 | assert.Contains(t, m, "platform") 33 | assert.Contains(t, m, "platformFamily") 34 | } 35 | -------------------------------------------------------------------------------- /pkg/collect/interfaces.go: -------------------------------------------------------------------------------- 1 | package collect 2 | 3 | import ( 4 | "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" 5 | corev1 "k8s.io/api/core/v1" 6 | ) 7 | 8 | type ImageRunner interface { 9 | GetImage() string 10 | SetImage(string) 11 | 12 | GetImagePullSecret() *v1beta2.ImagePullSecrets 13 | SetImagePullSecret(*v1beta2.ImagePullSecrets) 14 | 15 | GetNamespace() string 16 | } 17 | 18 | var _ ImageRunner = &v1beta2.Run{} 19 | var _ ImageRunner = &v1beta2.CopyFromHost{} 20 | var _ ImageRunner = &v1beta2.Sysctl{} 21 | var _ ImageRunner = &v1beta2.Collectd{} 22 | 23 | type PodSpecRunner interface { 24 | GetPodSpec() corev1.PodSpec 25 | SetPodSpec(corev1.PodSpec) 26 | 27 | GetImagePullSecret() *v1beta2.ImagePullSecrets 28 | SetImagePullSecret(*v1beta2.ImagePullSecrets) 29 | 30 | GetNamespace() string 31 | } 32 | 33 | var _ PodSpecRunner = &v1beta2.RunPod{} 34 | -------------------------------------------------------------------------------- /pkg/collect/interfaces_test.go: -------------------------------------------------------------------------------- 1 | package collect 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func Test_interfaces(t *testing.T) { 11 | runCollector := &v1beta2.Run{} 12 | clusterInfoCollector := &v1beta2.ClusterInfo{} 13 | 14 | tests := []struct { 15 | name string 16 | collect *v1beta2.Collect 17 | want interface{} 18 | wantRunner bool 19 | }{ 20 | { 21 | name: "image runner collector", 22 | collect: &v1beta2.Collect{ 23 | Run: runCollector, 24 | }, 25 | want: runCollector, 26 | wantRunner: true, 27 | }, 28 | { 29 | name: "not image runner collector", 30 | collect: &v1beta2.Collect{ 31 | ClusterInfo: clusterInfoCollector, 32 | }, 33 | want: clusterInfoCollector, 34 | wantRunner: false, 35 | }, 36 | { 37 | name: "no collector", 38 | collect: &v1beta2.Collect{}, 39 | want: nil, 40 | wantRunner: false, 41 | }, 42 | } 43 | for _, tt := range tests { 44 | t.Run(tt.name, func(t *testing.T) { 45 | req := require.New(t) 46 | 47 | got := v1beta2.GetCollector(tt.collect) 48 | req.EqualValues(tt.want, got) 49 | 50 | runner, ok := got.(ImageRunner) 51 | req.EqualValues(tt.wantRunner, ok) 52 | if tt.wantRunner { 53 | req.EqualValues(tt.want, runner) 54 | } 55 | }) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /pkg/collect/longhorn_test.go: -------------------------------------------------------------------------------- 1 | package collect 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestParseReplicaChecksums(t *testing.T) { 10 | data := []byte(` 11 | 7cc93e21d84bb7d0db0a72281f21500ba3847dea6467631cca91523d01ace8c9 /host/var/lib/longhorn/replicas/pvc-1f9ee2f6-078e-42a6-bf5c-3eaa0722fbfc-68bd18ca/revision.counter 12 | 7637cb563f796f8d6358ff4fc635ce596e5326a7f940cc2ea2eaee0acff843ce /host/var/lib/longhorn/replicas/pvc-1f9ee2f6-078e-42a6-bf5c-3eaa0722fbfc-68bd18ca/volume-head-000.img 13 | ca21027be32ef389de0b21d0c4713e824cad7114a287e05e56de49c948492fc9 /host/var/lib/longhorn/replicas/pvc-1f9ee2f6-078e-42a6-bf5c-3eaa0722fbfc-68bd18ca/volume-head-000.img.meta 14 | e9ce811b3f11dfe3af0bdd46581f23ba2c570be5dc3b807652ad6142322c706b /host/var/lib/longhorn/replicas/pvc-1f9ee2f6-078e-42a6-bf5c-3eaa0722fbfc-68bd18ca/volume.meta 15 | `) 16 | got, err := ParseReplicaChecksum(data) 17 | assert.Nil(t, err) 18 | want := map[string]string{ 19 | "revision.counter": "7cc93e21d84bb7d0db0a72281f21500ba3847dea6467631cca91523d01ace8c9", 20 | "volume-head-000.img": "7637cb563f796f8d6358ff4fc635ce596e5326a7f940cc2ea2eaee0acff843ce", 21 | "volume-head-000.img.meta": "ca21027be32ef389de0b21d0c4713e824cad7114a287e05e56de49c948492fc9", 22 | "volume.meta": "e9ce811b3f11dfe3af0bdd46581f23ba2c570be5dc3b807652ad6142322c706b", 23 | } 24 | assert.Equal(t, want, got) 25 | } 26 | -------------------------------------------------------------------------------- /pkg/collect/result_test.go: -------------------------------------------------------------------------------- 1 | package collect 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | "github.com/replicatedhq/troubleshoot/internal/testutils" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestCollectorResult_AddResult(t *testing.T) { 12 | r := CollectorResult{"a": []byte("a")} 13 | 14 | other := CollectorResult{"b": []byte("b")} 15 | r.AddResult(other) 16 | 17 | assert.Equal(t, 2, len(r)) 18 | assert.Equal(t, []byte("a"), r["a"]) 19 | assert.Equal(t, []byte("b"), r["b"]) 20 | } 21 | 22 | func TestCollectorResultFromBundle(t *testing.T) { 23 | tests := []struct { 24 | name string 25 | bundleDir string 26 | want CollectorResult 27 | wantErr bool 28 | }{ 29 | { 30 | name: "creates collector results from a bundle successfully", 31 | bundleDir: filepath.Join(testutils.FileDir(), "../../testdata/supportbundle/extracted-sb"), 32 | want: CollectorResult{ 33 | "cluster-resources/pods/logs/default/static-hi/static-hi.log": nil, 34 | "static-hi.log": nil, 35 | }, 36 | wantErr: false, 37 | }, 38 | { 39 | name: "fails to create collector results from a missing directory", 40 | bundleDir: "gibberish", 41 | want: nil, 42 | wantErr: true, 43 | }, 44 | } 45 | 46 | for _, tt := range tests { 47 | t.Run(tt.name, func(t *testing.T) { 48 | got, err := CollectorResultFromBundle(tt.bundleDir) 49 | assert.Equal(t, tt.want, got) 50 | assert.Equal(t, (err != nil), tt.wantErr) 51 | }) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pkg/convert/errors.go: -------------------------------------------------------------------------------- 1 | package convert 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | ) 6 | 7 | type FuncError struct { 8 | Name string // Name of function. 9 | Err error // Pre-formatted error. 10 | } 11 | 12 | func (e FuncError) Error() string { 13 | return e.Err.Error() 14 | } 15 | 16 | // Panic will panic with a recoverable error. 17 | func Panic(name string, err error) error { 18 | panic(Error(name, err)) 19 | } 20 | 21 | // Error will wrap a template error in an ExecError causing template.Execute to recover. 22 | func Error(name string, err error) error { 23 | return FuncError{ 24 | Name: name, 25 | Err: err, 26 | } 27 | } 28 | 29 | func IsFuncError(err error) bool { 30 | _, ok := errors.Cause(err).(FuncError) 31 | return ok 32 | } 33 | -------------------------------------------------------------------------------- /pkg/convert/output.go: -------------------------------------------------------------------------------- 1 | package convert 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | ) 7 | 8 | // ValidateOutputPath takes an output file path and returns it as an absolute path. 9 | // It returns an error if the absolute path cannot be determined or if the parent directory does not exist. 10 | func ValidateOutputPath(outputPath string) (string, error) { 11 | outputPath, err := filepath.Abs(outputPath) 12 | if err != nil { 13 | return "", err 14 | } 15 | if _, err := os.Stat(filepath.Dir(outputPath)); err != nil { 16 | return "", err 17 | } 18 | return outputPath, nil 19 | } 20 | -------------------------------------------------------------------------------- /pkg/convert/templates_test.go: -------------------------------------------------------------------------------- 1 | package convert 2 | 3 | import "testing" 4 | 5 | func TestExecute(t *testing.T) { 6 | type args struct { 7 | text string 8 | data interface{} 9 | } 10 | tests := []struct { 11 | name string 12 | args args 13 | want string 14 | wantErr bool 15 | }{ 16 | { 17 | name: "{{repl", 18 | args: args{ 19 | text: "{{repl printf \"%s\" \"hello\"}}", 20 | data: nil, 21 | }, 22 | want: "hello", 23 | }, 24 | { 25 | name: "repl{{", 26 | args: args{ 27 | text: "repl{{ printf \"%s\" \"hello\"}}", 28 | data: nil, 29 | }, 30 | want: "hello", 31 | }, 32 | } 33 | for _, tt := range tests { 34 | t.Run(tt.name, func(t *testing.T) { 35 | got, err := Execute(tt.args.text, tt.args.data) 36 | if (err != nil) != tt.wantErr { 37 | t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr) 38 | return 39 | } 40 | if got != tt.want { 41 | t.Errorf("Execute() = %v, want %v", got, tt.want) 42 | } 43 | }) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pkg/debug/log.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/spf13/viper" 7 | ) 8 | 9 | func Print(v ...interface{}) { 10 | if viper.GetBool("debug") { 11 | log.Print(v...) 12 | } 13 | } 14 | 15 | func Printf(format string, v ...interface{}) { 16 | if viper.GetBool("debug") { 17 | log.Printf(format, v...) 18 | } 19 | } 20 | 21 | func Println(v ...interface{}) { 22 | if viper.GetBool("debug") { 23 | log.Println(v...) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pkg/docrewrite/v1beta2.go: -------------------------------------------------------------------------------- 1 | package docrewrite 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | "gopkg.in/yaml.v2" 6 | ) 7 | 8 | func ConvertToV1Beta2(doc []byte) ([]byte, error) { 9 | var parsed map[string]interface{} 10 | err := yaml.Unmarshal(doc, &parsed) 11 | if err != nil { 12 | return nil, errors.Wrap(err, "failed to unmarshal yaml") 13 | } 14 | 15 | v, ok := parsed["apiVersion"] 16 | if !ok { 17 | return nil, errors.New("no apiVersion in document") 18 | } 19 | 20 | if v == "troubleshoot.sh/v1beta2" { 21 | return doc, nil 22 | } 23 | 24 | if v == "troubleshoot.sh/v1beta3" { 25 | // For v1beta3, just change the apiVersion to v1beta2 26 | // The actual template rendering will be handled elsewhere 27 | parsed["apiVersion"] = "troubleshoot.sh/v1beta2" 28 | newDoc, err := yaml.Marshal(parsed) 29 | if err != nil { 30 | return nil, errors.Wrap(err, "failed to marshal new spec") 31 | } 32 | return newDoc, nil 33 | } 34 | 35 | if v != "troubleshoot.replicated.com/v1beta1" { 36 | return nil, errors.Errorf("cannot convert %s", v) 37 | } 38 | 39 | parsed["apiVersion"] = "troubleshoot.sh/v1beta2" 40 | newDoc, err := yaml.Marshal(parsed) 41 | if err != nil { 42 | return nil, errors.Wrap(err, "failed to marshal new spec") 43 | } 44 | 45 | return newDoc, nil 46 | } 47 | -------------------------------------------------------------------------------- /pkg/httputil/config.go: -------------------------------------------------------------------------------- 1 | package httputil 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | var ( 8 | httpTransport *http.Transport 9 | httpClient = &http.Client{} 10 | ) 11 | 12 | func AddTransport(transport *http.Transport) { 13 | httpTransport = transport 14 | } 15 | 16 | func GetHttpClient() *http.Client { 17 | if httpTransport != nil { 18 | httpClient.Transport = httpTransport 19 | return httpClient 20 | } 21 | 22 | return http.DefaultClient 23 | } 24 | -------------------------------------------------------------------------------- /pkg/interfaceutils/interfaceutils.go: -------------------------------------------------------------------------------- 1 | package interfaceutils 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/pkg/errors" 9 | ) 10 | 11 | func GetAtPath(input interface{}, path string) (interface{}, error) { 12 | parts := strings.SplitN(path, ".", 2) 13 | key := parts[0] 14 | if isArrayIndex(key) { 15 | i, err := getArrayIndexValue(key) 16 | if err != nil { 17 | return nil, errors.Wrapf(err, "failed to get index value of %s", key) 18 | } 19 | obj, ok := input.([]interface{}) 20 | if !ok { 21 | return nil, errors.New(fmt.Sprintf("input is not an array: %+v", input)) 22 | } 23 | input = obj[i] 24 | } else { 25 | switch t := input.(type) { 26 | case map[interface{}]interface{}: 27 | input = input.(map[interface{}]interface{})[key] 28 | case map[string]interface{}: 29 | input = input.(map[string]interface{})[key] 30 | default: 31 | return nil, errors.New(fmt.Sprintf("input is not a map, but rather a %v: %+v", t, input)) 32 | } 33 | } 34 | 35 | if len(parts) > 1 { 36 | return GetAtPath(input, parts[1]) 37 | } 38 | 39 | return input, nil 40 | } 41 | 42 | func isArrayIndex(key string) bool { 43 | return strings.HasPrefix(key, "[") && strings.HasSuffix(key, "]") 44 | } 45 | 46 | func getArrayIndexValue(key string) (int, error) { 47 | key = strings.TrimPrefix(key, "[") 48 | key = strings.TrimSuffix(key, "]") 49 | i, err := strconv.Atoi(key) 50 | if err != nil { 51 | return -1, errors.Wrapf(err, "failed to parse index %s", key) 52 | } 53 | return i, nil 54 | } 55 | -------------------------------------------------------------------------------- /pkg/k8sutil/auth.go: -------------------------------------------------------------------------------- 1 | package k8sutil 2 | 3 | import ( 4 | "context" 5 | 6 | authorizationv1 "k8s.io/api/authorization/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | "k8s.io/client-go/kubernetes" 9 | ) 10 | 11 | // CanIListAndGetAllSecretsAndConfigMaps checks if the current user can list and get secrets and configmaps 12 | // from all namespaces 13 | func CanIListAndGetAllSecretsAndConfigMaps(ctx context.Context, client kubernetes.Interface) (bool, error) { 14 | canis := []struct{ ns, verb, resource string }{ 15 | {"", "get", "secrets"}, 16 | {"", "get", "configmaps"}, 17 | {"", "list", "secrets"}, 18 | {"", "list", "configmaps"}, 19 | } 20 | 21 | for _, cani := range canis { 22 | ican, err := authCanI(ctx, client, cani.ns, cani.verb, cani.resource) 23 | if err != nil { 24 | return false, err 25 | } 26 | 27 | if !ican { 28 | return false, nil 29 | } 30 | } 31 | 32 | return true, nil 33 | } 34 | 35 | func authCanI(ctx context.Context, client kubernetes.Interface, ns, verb, resource string) (bool, error) { 36 | sar := &authorizationv1.SelfSubjectAccessReview{ 37 | Spec: authorizationv1.SelfSubjectAccessReviewSpec{ 38 | ResourceAttributes: &authorizationv1.ResourceAttributes{ 39 | Namespace: ns, 40 | Verb: verb, 41 | Resource: resource, 42 | }, 43 | }, 44 | } 45 | 46 | resp, err := client.AuthorizationV1().SelfSubjectAccessReviews().Create(ctx, sar, metav1.CreateOptions{}) 47 | if err != nil { 48 | return false, err 49 | } 50 | 51 | return resp.Status.Allowed, nil 52 | } 53 | -------------------------------------------------------------------------------- /pkg/k8sutil/config.go: -------------------------------------------------------------------------------- 1 | package k8sutil 2 | 3 | import ( 4 | flag "github.com/spf13/pflag" 5 | "k8s.io/cli-runtime/pkg/genericclioptions" 6 | "k8s.io/client-go/rest" 7 | "k8s.io/client-go/tools/clientcmd" 8 | ) 9 | 10 | var ( 11 | kubernetesConfigFlags *genericclioptions.ConfigFlags 12 | ) 13 | 14 | func init() { 15 | kubernetesConfigFlags = genericclioptions.NewConfigFlags(false) 16 | } 17 | 18 | func AddFlags(flags *flag.FlagSet) { 19 | kubernetesConfigFlags.AddFlags(flags) 20 | } 21 | 22 | func GetKubeconfig() clientcmd.ClientConfig { 23 | return kubernetesConfigFlags.ToRawKubeConfigLoader() 24 | } 25 | 26 | func GetRESTConfig() (*rest.Config, error) { 27 | return kubernetesConfigFlags.ToRESTConfig() 28 | } 29 | -------------------------------------------------------------------------------- /pkg/k8sutil/discovery/discovery.go: -------------------------------------------------------------------------------- 1 | package discovery 2 | 3 | import ( 4 | "k8s.io/client-go/discovery" 5 | ) 6 | 7 | // HasResource takes an api version and a kind of a resource and checks if the resource 8 | // is supported by the k8s api server. 9 | func HasResource(dc discovery.DiscoveryInterface, apiVersion, kind string) (bool, error) { 10 | _, apiLists, err := dc.ServerGroupsAndResources() 11 | if err != nil { 12 | return false, err 13 | } 14 | // Compare the resource api version and kind and find the resource. 15 | for _, apiList := range apiLists { 16 | if apiList.GroupVersion == apiVersion { 17 | for _, r := range apiList.APIResources { 18 | if r.Kind == kind { 19 | return true, nil 20 | } 21 | } 22 | } 23 | } 24 | return false, nil 25 | } 26 | -------------------------------------------------------------------------------- /pkg/k8sutil/node.go: -------------------------------------------------------------------------------- 1 | package k8sutil 2 | 3 | import v1 "k8s.io/api/core/v1" 4 | 5 | const UnreachableTaint = "node.kubernetes.io/unreachable" 6 | const NotReadyTaint = "node.kubernetes.io/not-ready" 7 | const UnschedulableTaint = "node.kubernetes.io/unschedulable" 8 | 9 | func NodeIsReady(node v1.Node) bool { 10 | for _, taint := range node.Spec.Taints { 11 | switch taint.Key { 12 | case NotReadyTaint: 13 | return false 14 | case UnreachableTaint: 15 | return false 16 | case UnschedulableTaint: 17 | return false 18 | } 19 | } 20 | return true 21 | } 22 | -------------------------------------------------------------------------------- /pkg/lint/types.go: -------------------------------------------------------------------------------- 1 | package lint 2 | 3 | // Core types used by the lint package 4 | 5 | type LintResult struct { 6 | FilePath string 7 | Errors []LintError 8 | Warnings []LintWarning 9 | } 10 | 11 | type LintError struct { 12 | Line int 13 | Column int 14 | Message string 15 | Field string 16 | } 17 | 18 | type LintWarning struct { 19 | Line int 20 | Column int 21 | Message string 22 | Field string 23 | } 24 | 25 | type LintOptions struct { 26 | FilePaths []string 27 | Fix bool 28 | Format string // "text" or "json" 29 | ValuesFiles []string // Path to YAML files with template values (for v1beta3) 30 | SetValues []string // Template values from command line (for v1beta3) 31 | } 32 | 33 | // HasErrors returns true if any of the results contain errors 34 | func HasErrors(results []LintResult) bool { 35 | for _, result := range results { 36 | if len(result.Errors) > 0 { 37 | return true 38 | } 39 | } 40 | return false 41 | } 42 | -------------------------------------------------------------------------------- /pkg/longhorn/apis/longhorn/register.go: -------------------------------------------------------------------------------- 1 | package longhorn 2 | 3 | const ( 4 | GroupName = "longhorn.io" 5 | ) 6 | -------------------------------------------------------------------------------- /pkg/longhorn/apis/longhorn/v1beta1/doc.go: -------------------------------------------------------------------------------- 1 | // +k8s:deepcopy-gen=package,register 2 | 3 | // Package v1beta1 is the v1beta1 version of the API. 4 | // +groupName=longhorn.io 5 | package v1beta1 6 | -------------------------------------------------------------------------------- /pkg/longhorn/apis/longhorn/v1beta1/register.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import ( 4 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | "k8s.io/apimachinery/pkg/runtime" 6 | "k8s.io/apimachinery/pkg/runtime/schema" 7 | 8 | "github.com/replicatedhq/troubleshoot/pkg/longhorn/apis/longhorn" 9 | ) 10 | 11 | var ( 12 | SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) 13 | AddToScheme = SchemeBuilder.AddToScheme 14 | ) 15 | 16 | var SchemeGroupVersion = schema.GroupVersion{Group: longhorn.GroupName, Version: "v1beta1"} 17 | 18 | func Resource(resource string) schema.GroupResource { 19 | return SchemeGroupVersion.WithResource(resource).GroupResource() 20 | } 21 | 22 | func addKnownTypes(scheme *runtime.Scheme) error { 23 | scheme.AddKnownTypes(SchemeGroupVersion, 24 | &Volume{}, 25 | &VolumeList{}, 26 | &Engine{}, 27 | &EngineList{}, 28 | &Replica{}, 29 | &ReplicaList{}, 30 | &Setting{}, 31 | &SettingList{}, 32 | &EngineImage{}, 33 | &EngineImageList{}, 34 | &Node{}, 35 | &NodeList{}, 36 | &InstanceManager{}, 37 | &InstanceManagerList{}, 38 | &ShareManager{}, 39 | &ShareManagerList{}, 40 | &BackingImage{}, 41 | &BackingImageList{}, 42 | &BackingImageManager{}, 43 | &BackingImageManagerList{}, 44 | &BackingImageDataSource{}, 45 | &BackingImageDataSourceList{}, 46 | &BackupTarget{}, 47 | &BackupTargetList{}, 48 | &BackupVolume{}, 49 | &BackupVolumeList{}, 50 | &Backup{}, 51 | &BackupList{}, 52 | &RecurringJob{}, 53 | &RecurringJobList{}, 54 | ) 55 | metav1.AddToGroupVersion(scheme, SchemeGroupVersion) 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /pkg/longhorn/client/clientset/versioned/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated clientset. 20 | package versioned 21 | -------------------------------------------------------------------------------- /pkg/longhorn/client/clientset/versioned/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated fake clientset. 20 | package fake 21 | -------------------------------------------------------------------------------- /pkg/longhorn/client/clientset/versioned/scheme/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package contains the scheme of the automatically generated clientset. 20 | package scheme 21 | -------------------------------------------------------------------------------- /pkg/longhorn/client/clientset/versioned/typed/longhorn/v1beta1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated typed clients. 20 | package v1beta1 21 | -------------------------------------------------------------------------------- /pkg/longhorn/client/clientset/versioned/typed/longhorn/v1beta1/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // Package fake has the automatically generated clients. 20 | package fake 21 | -------------------------------------------------------------------------------- /pkg/longhorn/client/clientset/versioned/typed/longhorn/v1beta1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package v1beta1 20 | 21 | type BackingImageExpansion interface{} 22 | 23 | type BackingImageDataSourceExpansion interface{} 24 | 25 | type BackingImageManagerExpansion interface{} 26 | 27 | type BackupExpansion interface{} 28 | 29 | type BackupTargetExpansion interface{} 30 | 31 | type BackupVolumeExpansion interface{} 32 | 33 | type EngineExpansion interface{} 34 | 35 | type EngineImageExpansion interface{} 36 | 37 | type InstanceManagerExpansion interface{} 38 | 39 | type NodeExpansion interface{} 40 | 41 | type RecurringJobExpansion interface{} 42 | 43 | type ReplicaExpansion interface{} 44 | 45 | type SettingExpansion interface{} 46 | 47 | type ShareManagerExpansion interface{} 48 | 49 | type VolumeExpansion interface{} 50 | -------------------------------------------------------------------------------- /pkg/longhorn/types/deploy.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | const ( 4 | LonghornManagerDaemonSetName = "longhorn-manager" 5 | 6 | DriverDeployerName = "longhorn-driver-deployer" 7 | CSIAttacherName = "csi-attacher" 8 | CSIProvisionerName = "csi-provisioner" 9 | CSIResizerName = "csi-resizer" 10 | CSISnapshotterName = "csi-snapshotter" 11 | CSIPluginName = "longhorn-csi-plugin" 12 | ) 13 | -------------------------------------------------------------------------------- /pkg/longhorn/util/cmd.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "bufio" 5 | "github.com/pkg/errors" 6 | "os/exec" 7 | "strings" 8 | ) 9 | 10 | func CmdOutLines(cmd *exec.Cmd, cancel <-chan interface{}) (<-chan string, <-chan error) { 11 | r := make(chan string) 12 | errCh := make(chan error, 1) 13 | out, err := cmd.StdoutPipe() 14 | if err != nil { 15 | defer close(errCh) 16 | defer close(r) 17 | errCh <- errors.Wrapf(err, "error obtaining stdout of `%s`", strings.Join(cmd.Args, " ")) 18 | return r, errCh 19 | } 20 | if err := cmd.Start(); err != nil { 21 | defer close(errCh) 22 | defer close(r) 23 | errCh <- errors.Wrapf(err, "error starting cmd `%s`", strings.Join(cmd.Args, " ")) 24 | return r, errCh 25 | } 26 | go func() { 27 | defer close(errCh) 28 | defer close(r) 29 | defer func() { 30 | if err := cmd.Wait(); err != nil { 31 | errCh <- err 32 | } 33 | }() 34 | scanner := bufio.NewScanner(out) 35 | for scanner.Scan() { 36 | select { 37 | case <-cancel: 38 | break 39 | case r <- scanner.Text(): 40 | // continue 41 | } 42 | } 43 | }() 44 | return r, errCh 45 | } 46 | -------------------------------------------------------------------------------- /pkg/longhorn/util/http.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | "net/http" 7 | ) 8 | 9 | func CopyReq(req *http.Request) *http.Request { 10 | r := *req 11 | buf, _ := ioutil.ReadAll(r.Body) 12 | req.Body = ioutil.NopCloser(bytes.NewBuffer(buf)) 13 | r.Body = ioutil.NopCloser(bytes.NewBuffer(buf)) 14 | return &r 15 | } 16 | -------------------------------------------------------------------------------- /pkg/longhorn/util/iscsi_windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | // +build windows 3 | 4 | package util 5 | 6 | import ( 7 | "github.com/pkg/errors" 8 | ) 9 | 10 | func GetDiskInfo(directory string) (*DiskInfo, error) { 11 | return nil, errors.Errorf("cannot get disk info of directory %v", directory) 12 | } 13 | 14 | func RemoveHostDirectoryContent(directory string) error { 15 | return errors.Errorf("failed to remove host directory %v", directory) 16 | } 17 | 18 | func CopyHostDirectoryContent(src, dest string) error { 19 | return errors.Errorf("failed to copy the content from %v to %v for the host", src, dest) 20 | } 21 | 22 | func CreateDiskPathReplicaSubdirectory(path string) error { 23 | return errors.Errorf("error creating data path %v on host", path) 24 | } 25 | 26 | func ExpandFileSystem(volumeName string) error { 27 | return errors.Errorf("error expanding filsystem on %v", volumeName) 28 | } 29 | 30 | func DetectFileSystem(volumeName string) (string, error) { 31 | return "", errors.Errorf("error detecting filsystem on %v", volumeName) 32 | } 33 | 34 | func GetDiskConfig(path string) (*DiskConfig, error) { 35 | return nil, errors.Errorf("error getting disk config from %v", path) 36 | } 37 | 38 | func GenerateDiskConfig(path string) (*DiskConfig, error) { 39 | return nil, errors.Errorf("error generating disk config from %v", path) 40 | } 41 | -------------------------------------------------------------------------------- /pkg/longhorn/util/k8s.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/api/meta" 5 | "k8s.io/apimachinery/pkg/runtime" 6 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 7 | ) 8 | 9 | func AddFinalizer(name string, obj runtime.Object) error { 10 | metadata, err := meta.Accessor(obj) 11 | if err != nil { 12 | return err 13 | } 14 | 15 | exists := false 16 | finalizers := metadata.GetFinalizers() 17 | for _, f := range finalizers { 18 | if f == name { 19 | exists = true 20 | break 21 | } 22 | } 23 | if !exists { 24 | metadata.SetFinalizers(append(finalizers, name)) 25 | } 26 | 27 | return nil 28 | } 29 | 30 | func RemoveFinalizer(name string, obj runtime.Object) error { 31 | metadata, err := meta.Accessor(obj) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | var finalizers []string 37 | for _, finalizer := range metadata.GetFinalizers() { 38 | if finalizer == name { 39 | continue 40 | } 41 | finalizers = append(finalizers, finalizer) 42 | } 43 | metadata.SetFinalizers(finalizers) 44 | 45 | return nil 46 | } 47 | 48 | func FinalizerExists(name string, obj runtime.Object) bool { 49 | metadata, err := meta.Accessor(obj) 50 | if err != nil { 51 | utilruntime.HandleError(err) 52 | return false 53 | } 54 | for _, finalizer := range metadata.GetFinalizers() { 55 | if finalizer == name { 56 | return true 57 | } 58 | } 59 | return false 60 | } 61 | -------------------------------------------------------------------------------- /pkg/longhorn/util/util_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "github.com/stretchr/testify/require" 5 | "testing" 6 | ) 7 | 8 | func TestConvertSize(t *testing.T) { 9 | assert := require.New(t) 10 | 11 | size, err := ConvertSize("0m") 12 | assert.Nil(err) 13 | assert.Equal(int64(0), size) 14 | 15 | size, err = ConvertSize("0Mi") 16 | assert.Nil(err) 17 | assert.Equal(int64(0), size) 18 | 19 | size, err = ConvertSize("1024k") 20 | assert.Nil(err) 21 | assert.Equal(int64(1024*1000), size) 22 | 23 | size, err = ConvertSize("1024Ki") 24 | assert.Nil(err) 25 | assert.Equal(int64(1024*1024), size) 26 | 27 | size, err = ConvertSize("1024") 28 | assert.Nil(err) 29 | assert.Equal(int64(1024), size) 30 | 31 | size, err = ConvertSize("1Gi") 32 | assert.Nil(err) 33 | assert.Equal(int64(1024*1024*1024), size) 34 | 35 | size, err = ConvertSize("1G") 36 | assert.Nil(err) 37 | assert.Equal(int64(1e9), size) 38 | } 39 | 40 | func TestRoundUpSize(t *testing.T) { 41 | assert := require.New(t) 42 | 43 | assert.Equal(int64(SizeAlignment), RoundUpSize(0)) 44 | assert.Equal(int64(2*SizeAlignment), RoundUpSize(SizeAlignment+1)) 45 | } 46 | -------------------------------------------------------------------------------- /pkg/namespaces/errors.go: -------------------------------------------------------------------------------- 1 | package namespaces 2 | 3 | import "fmt" 4 | 5 | // WrapIfFail executes the provided function. If the function succeeds it 6 | // simply returns the original error (that can be nil). If the function fails 7 | // then it assesses if an original error was provided and wraps it if true. 8 | // This function is a sugar to be used at when deferring function that can also 9 | // return errors, we don't want to loose any context. 10 | func WrapIfFail(msg string, originalerr error, fn func() error) error { 11 | if fnerr := fn(); fnerr != nil { 12 | if originalerr == nil { 13 | return fmt.Errorf("%s: %w", msg, fnerr) 14 | } 15 | return fmt.Errorf("%s: %w: %w", msg, fnerr, originalerr) 16 | } 17 | return originalerr 18 | } 19 | -------------------------------------------------------------------------------- /pkg/namespaces/managed-namespace_unsupported.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | 3 | package namespaces 4 | 5 | import ( 6 | "fmt" 7 | "net" 8 | "runtime" 9 | ) 10 | 11 | type NamespacePinger struct { 12 | InternalIP net.IP 13 | ExternalIP net.IP 14 | } 15 | 16 | func (n *NamespacePinger) Close() error { 17 | return fmt.Errorf("namespaces not supported on %s platform", runtime.GOOS) 18 | } 19 | 20 | func (n *NamespacePinger) PingUDP(_ net.IP) error { 21 | return fmt.Errorf("namespaces not supported on %s platform", runtime.GOOS) 22 | } 23 | 24 | func (n *NamespacePinger) PingTCP(_ net.IP) error { 25 | return fmt.Errorf("namespaces not supported on %s platform", runtime.GOOS) 26 | } 27 | 28 | func (n *NamespacePinger) StartTCPEchoServer(errors chan error) { 29 | go func() { 30 | errors <- fmt.Errorf("namespaces not supported on %s platform", runtime.GOOS) 31 | }() 32 | } 33 | 34 | func (n *NamespacePinger) StartUDPEchoServer(errors chan error) { 35 | go func() { 36 | errors <- fmt.Errorf("namespaces not supported on %s platform", runtime.GOOS) 37 | }() 38 | } 39 | 40 | func NewNamespacePinger(_, _ string, _ ...Option) (*NamespacePinger, error) { 41 | return nil, fmt.Errorf("namespaces not supported on %s platform", runtime.GOOS) 42 | } 43 | -------------------------------------------------------------------------------- /pkg/namespaces/namespace-handler.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | 3 | package namespaces 4 | 5 | import "github.com/vishvananda/netns" 6 | 7 | // NamespaceHandler is an interface that represents the netns functions that 8 | // we need to mock. This only exists for test purposes. 9 | type NamespaceHandler interface { 10 | DeleteNamed(string) error 11 | Set(netns.NsHandle) error 12 | Get() (netns.NsHandle, error) 13 | NewNamed(string) (netns.NsHandle, error) 14 | } 15 | 16 | // NamespaceHandle is a struct that exists solely for the purpose of mocking 17 | // netns functions on tests. It just wraps calls to the netns package. 18 | type NamespaceHandle struct{} 19 | 20 | // DeleteNamed calls netns.DeleteNamed. 21 | func (n NamespaceHandle) DeleteNamed(name string) error { 22 | return netns.DeleteNamed(name) 23 | } 24 | 25 | // Set calls netns.Set. 26 | func (n NamespaceHandle) Set(ns netns.NsHandle) error { 27 | return netns.Set(ns) 28 | } 29 | 30 | // Get calls netns.Get. 31 | func (n NamespaceHandle) Get() (netns.NsHandle, error) { 32 | return netns.Get() 33 | } 34 | 35 | // NewNamed calls netns.NewNamed. 36 | func (n NamespaceHandle) NewNamed(name string) (netns.NsHandle, error) { 37 | return netns.NewNamed(name) 38 | } 39 | -------------------------------------------------------------------------------- /pkg/namespaces/options.go: -------------------------------------------------------------------------------- 1 | package namespaces 2 | 3 | import "time" 4 | 5 | // Option is a function that sets an optional configuration. 6 | type Option func(*Configuration) 7 | 8 | // Configuration holds the runtime configuration for this package. 9 | type Configuration struct { 10 | // Logf is a function that will be used to log messages. If not 11 | // provided the default logger will be used. 12 | Logf func(string, ...interface{}) 13 | // Port is the port to use for the UDP and TCP pings. 14 | Port int 15 | // Timeout is the timeout for the UDP and TCP connection to finish. 16 | Timeout time.Duration 17 | } 18 | 19 | // NewConfiguration creates a new configuration with the provided options. 20 | func NewConfiguration(options ...Option) Configuration { 21 | cfg := Configuration{ 22 | Logf: func(string, ...interface{}) {}, 23 | Port: 8080, 24 | Timeout: 5 * time.Second, 25 | } 26 | for _, o := range options { 27 | o(&cfg) 28 | } 29 | return cfg 30 | } 31 | 32 | // WithLogf sets the log function for this package. 33 | func WithLogf(f func(string, ...interface{})) Option { 34 | return func(c *Configuration) { 35 | c.Logf = f 36 | } 37 | } 38 | 39 | // WithPort sets the port to use for the UDP and TCP pings. 40 | func WithPort(port int) Option { 41 | return func(c *Configuration) { 42 | c.Port = port 43 | } 44 | } 45 | 46 | // WithTimeout sets the timeout for the UDP and TCP connections. 47 | func WithTimeout(timeout time.Duration) Option { 48 | return func(c *Configuration) { 49 | c.Timeout = timeout 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /pkg/preflight/concat.go: -------------------------------------------------------------------------------- 1 | package preflight 2 | 3 | import ( 4 | troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" 5 | ) 6 | 7 | func ConcatPreflightSpec(target *troubleshootv1beta2.Preflight, source *troubleshootv1beta2.Preflight) *troubleshootv1beta2.Preflight { 8 | if source == nil { 9 | return target 10 | } 11 | var newSpec *troubleshootv1beta2.Preflight 12 | if target == nil { 13 | newSpec = source 14 | } else { 15 | newSpec = target.DeepCopy() 16 | newSpec.Spec.Collectors = append(newSpec.Spec.Collectors, source.Spec.Collectors...) 17 | newSpec.Spec.RemoteCollectors = append(newSpec.Spec.RemoteCollectors, source.Spec.RemoteCollectors...) 18 | newSpec.Spec.Analyzers = append(newSpec.Spec.Analyzers, source.Spec.Analyzers...) 19 | } 20 | return newSpec 21 | } 22 | 23 | func ConcatHostPreflightSpec(target *troubleshootv1beta2.HostPreflight, source *troubleshootv1beta2.HostPreflight) *troubleshootv1beta2.HostPreflight { 24 | if source == nil { 25 | return target 26 | } 27 | var newSpec *troubleshootv1beta2.HostPreflight 28 | if target == nil { 29 | newSpec = source 30 | } else { 31 | newSpec = target.DeepCopy() 32 | newSpec.Spec.Collectors = append(newSpec.Spec.Collectors, source.Spec.Collectors...) 33 | newSpec.Spec.RemoteCollectors = append(newSpec.Spec.RemoteCollectors, source.Spec.RemoteCollectors...) 34 | newSpec.Spec.Analyzers = append(newSpec.Spec.Analyzers, source.Spec.Analyzers...) 35 | } 36 | return newSpec 37 | } 38 | -------------------------------------------------------------------------------- /pkg/preflight/types.go: -------------------------------------------------------------------------------- 1 | package preflight 2 | 3 | type UploadPreflightResult struct { 4 | Strict bool `json:"strict,omitempty"` 5 | IsFail bool `json:"isFail,omitempty"` 6 | IsWarn bool `json:"isWarn,omitempty"` 7 | IsPass bool `json:"isPass,omitempty"` 8 | 9 | Title string `json:"title"` 10 | Message string `json:"message"` 11 | URI string `json:"uri,omitempty"` 12 | } 13 | 14 | type UploadPreflightError struct { 15 | Error string `json:"error"` 16 | } 17 | 18 | type UploadPreflightResults struct { 19 | Results []*UploadPreflightResult `json:"results,omitempty"` 20 | Errors []*UploadPreflightError `json:"errors,omitempty"` 21 | } 22 | -------------------------------------------------------------------------------- /pkg/specs/secrets.go: -------------------------------------------------------------------------------- 1 | package specs 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/pkg/errors" 7 | specs "github.com/replicatedhq/troubleshoot/internal/specs" 8 | "github.com/replicatedhq/troubleshoot/pkg/k8sutil" 9 | "k8s.io/client-go/kubernetes" 10 | ) 11 | 12 | // LoadFromSecret reads data from a secret in the cluster 13 | // Deprecated: Remove in a future version (v1.0). Future loader functions 14 | // will be created 15 | func LoadFromSecret(namespace string, secretName string, key string) ([]byte, error) { 16 | config, err := k8sutil.GetRESTConfig() 17 | if err != nil { 18 | return nil, errors.Wrap(err, "failed to convert kube flags to rest config") 19 | } 20 | 21 | client, err := kubernetes.NewForConfig(config) 22 | if err != nil { 23 | return nil, errors.Wrap(err, "failed to convert create k8s client") 24 | } 25 | 26 | data, err := specs.LoadFromSecret(context.TODO(), client, namespace, secretName) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | spec, ok := data[key] 32 | if !ok { 33 | return nil, errors.Errorf("spec not found in secret %q: key=%q", secretName, key) 34 | } 35 | 36 | return spec, nil 37 | } 38 | 39 | // LoadFromSecretMatchingLabel reads data from a secret in the cluster using a label selector 40 | // Deprecated: Remove in a future version (v1.0). Future loader functions will be created 41 | func LoadFromSecretMatchingLabel(client kubernetes.Interface, labelSelector string, namespace string, key string) ([]string, error) { 42 | return specs.LoadFromSecretMatchingLabel(context.TODO(), client, labelSelector, namespace, key) 43 | } 44 | -------------------------------------------------------------------------------- /pkg/specs/specs.go: -------------------------------------------------------------------------------- 1 | package specs 2 | 3 | import ( 4 | "context" 5 | 6 | specs "github.com/replicatedhq/troubleshoot/internal/specs" 7 | "k8s.io/apimachinery/pkg/labels" 8 | "k8s.io/client-go/kubernetes" 9 | ) 10 | 11 | // SplitTroubleshootSecretLabelSelector splits a label selector into two selectors, if applicable: 12 | // 1. troubleshoot.io/kind=support-bundle and non-troubleshoot (if contains) labels selector. 13 | // 2. troubleshoot.sh/kind=support-bundle and non-troubleshoot (if contains) labels selector. 14 | // Deprecated: Remove in a future version (v1.0). Future loader functions will be created 15 | func SplitTroubleshootSecretLabelSelector(client kubernetes.Interface, labelSelector labels.Selector) ([]string, error) { 16 | return specs.SplitTroubleshootSecretLabelSelector(context.TODO(), labelSelector) 17 | } 18 | -------------------------------------------------------------------------------- /pkg/supportbundle/test/bad-upstream-uri-spec.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: default 5 | spec: 6 | uri: https://raw.githubusercontent.com/adamancini/troubleshoot-specs/main/host/bad-spec.yaml 7 | collectors: 8 | - clusterInfo: {} 9 | - clusterResources: {} 10 | analyzers: 11 | - cephStatus: {} 12 | - longhorn: {} 13 | -------------------------------------------------------------------------------- /pkg/supportbundle/test/bad-uri-spec.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: default 5 | spec: 6 | uri: https://githubusercontent.com/adamancini/troubleshoot-specs/main/host/cluster-down 7 | collectors: 8 | - clusterInfo: {} 9 | - clusterResources: {} 10 | analyzers: 11 | - cephStatus: {} 12 | - longhorn: {} 13 | -------------------------------------------------------------------------------- /pkg/supportbundle/test/completebundle.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: default 5 | spec: 6 | collectors: 7 | - clusterResources: {} 8 | - clusterInfo: {} 9 | analyzers: 10 | - clusterPodStatuses: 11 | outcomes: 12 | - fail: 13 | when: "!= Healthy" 14 | message: "Status: {{ .Status.Reason }}" 15 | - nodeResources: 16 | checkName: Node status check 17 | outcomes: 18 | - fail: 19 | when: "nodeCondition(Ready) == False" 20 | message: "Not all nodes are online." 21 | - fail: 22 | when: "nodeCondition(Ready) == Unknown" 23 | message: "Not all nodes are online." 24 | - pass: 25 | message: "All nodes are online." 26 | -------------------------------------------------------------------------------- /pkg/supportbundle/test/supportbundle1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: default 5 | spec: 6 | collectors: 7 | - clusterResources: {} 8 | analyzers: 9 | - clusterPodStatuses: 10 | outcomes: 11 | - fail: 12 | when: "!= Healthy" 13 | message: "Status: {{ .Status.Reason }}" 14 | -------------------------------------------------------------------------------- /pkg/supportbundle/test/supportbundle2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: default 5 | spec: 6 | collectors: 7 | - clusterInfo: {} 8 | analyzers: 9 | - nodeResources: 10 | checkName: Node status check 11 | outcomes: 12 | - fail: 13 | when: "nodeCondition(Ready) == False" 14 | message: "Not all nodes are online." 15 | - fail: 16 | when: "nodeCondition(Ready) == Unknown" 17 | message: "Not all nodes are online." 18 | - pass: 19 | message: "All nodes are online." 20 | -------------------------------------------------------------------------------- /pkg/supportbundle/test/uri-spec.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: default 5 | spec: 6 | uri: https://raw.githubusercontent.com/replicatedhq/troubleshoot-specs/main/host/cluster-down.yaml 7 | collectors: 8 | - clusterInfo: {} 9 | - clusterResources: {} 10 | analyzers: 11 | - cephStatus: {} 12 | - longhorn: {} 13 | -------------------------------------------------------------------------------- /pkg/supportbundle/test/velero.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: velero 5 | spec: 6 | collectors: 7 | - logs: 8 | namespace: velero 9 | name: velero/logs 10 | analyzers: 11 | - velero: {} 12 | -------------------------------------------------------------------------------- /pkg/supportbundle/types/types.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | corev1 "k8s.io/api/core/v1" 5 | ) 6 | 7 | type PodDetails struct { 8 | PodDefinition corev1.Pod `json:"podDefinition"` 9 | PodEvents []corev1.Event `json:"podEvents"` 10 | PodContainers []PodContainer `json:"podContainers"` 11 | } 12 | 13 | type PodContainer struct { 14 | Name string `json:"name"` 15 | LogsFilePath string `json:"logsFilePath"` 16 | IsInitContainer bool `json:"isInitContainer"` 17 | } 18 | -------------------------------------------------------------------------------- /pkg/types/types.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type NotFoundError struct { 8 | Name string 9 | } 10 | 11 | func (e *NotFoundError) Error() string { 12 | return e.Name + ": not found" 13 | } 14 | 15 | type ExitError interface { 16 | Error() string 17 | ExitStatus() int 18 | } 19 | 20 | type ExitCodeError struct { 21 | Msg string 22 | Code int 23 | Err error 24 | } 25 | 26 | type ExitCodeWarning struct { 27 | Msg string 28 | } 29 | 30 | func (e *ExitCodeError) Error() string { 31 | return e.Msg 32 | } 33 | 34 | func (e *ExitCodeError) Unwrap() error { 35 | return e.Err 36 | } 37 | 38 | func (e *ExitCodeError) ExitStatus() int { 39 | return e.Code 40 | } 41 | 42 | func NewExitCodeError(exitCode int, theErr error) *ExitCodeError { 43 | useErr := "" 44 | if theErr != nil { 45 | useErr = theErr.Error() 46 | } 47 | return &ExitCodeError{Msg: useErr, Code: exitCode, Err: theErr} 48 | } 49 | 50 | func NewExitCodeWarning(theErrMsg string) *ExitCodeWarning { 51 | return &ExitCodeWarning{Msg: theErrMsg} 52 | } 53 | 54 | func (e *ExitCodeWarning) Warning() string { 55 | return fmt.Sprintf("Warning: %s", e.Msg) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/updater/pkgmgr/pkgmgr.go: -------------------------------------------------------------------------------- 1 | package pkgmgr 2 | 3 | // PackageManager represents an external package manager that can manage the binary 4 | type PackageManager interface { 5 | // IsInstalled returns true if the package/formula is installed via this package manager 6 | IsInstalled() (bool, error) 7 | // UpgradeCommand returns the command the user should run to upgrade 8 | UpgradeCommand() string 9 | // Name returns the human-readable name of the package manager 10 | Name() string 11 | } 12 | -------------------------------------------------------------------------------- /pkg/version/build.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | // NOTE: these variables are injected at build time 4 | 5 | var ( 6 | version, gitSHA, buildTime string 7 | ) 8 | -------------------------------------------------------------------------------- /pkg/version/run.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | var RunAt time.Time 8 | 9 | func init() { 10 | RunAt = time.Now().UTC() 11 | initBuild() 12 | } 13 | -------------------------------------------------------------------------------- /scripts/host-preflight-integration-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function say() { 4 | echo 5 | echo "---------------------------------------------------------------" 6 | echo $1 7 | echo "---------------------------------------------------------------" 8 | echo 9 | } 10 | 11 | 12 | if [ "$EUID" -ne 0 ] 13 | then say "Use sudo to run this script" 14 | exit 15 | fi 16 | 17 | for FILE in $(find ../examples/preflight/host/*.yaml) 18 | do 19 | if [ "${FILE}" = "../examples/preflight/host/tcp-load-balancer.yaml" ] 20 | then say "Skipping ${FILE}" 21 | continue 22 | fi 23 | 24 | say "Running Test ${FILE}" 25 | 26 | ../bin/preflight --interactive=false $FILE 27 | done 28 | 29 | 30 | -------------------------------------------------------------------------------- /scripts/initialize-sbom-build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | if [ -n "${COSIGN_KEY}" ] 6 | then 7 | echo "Writing cosign key to file" 8 | echo "${COSIGN_KEY}" | base64 -d > ./cosign.key 9 | else 10 | echo "ERROR: Missing COSIGN_KEY!" 11 | fi 12 | 13 | if ! command -v spdx-sbom-generator &> /dev/null 14 | then 15 | echo "Installing spdx-sbom-generator" 16 | curl -L https://github.com/spdx/spdx-sbom-generator/releases/download/v0.0.13/spdx-sbom-generator-v0.0.13-linux-amd64.tar.gz -o ./sbom/spdx-sbom-generator.tar.gz 17 | curl -L https://github.com/spdx/spdx-sbom-generator/releases/download/v0.0.13/spdx-sbom-generator-v0.0.13-linux-amd64.tar.gz.md5 -o ./sbom/spdx-sbom-generator.tar.gz.md5 18 | md5sum ./sbom/spdx-sbom-generator.tar.gz | cut --bytes=1-32 > ./sbom/checksum 19 | 20 | if ! cmp ./sbom/checksum ./sbom/spdx-sbom-generator.tar.gz.md5 21 | then 22 | echo "ERROR: spdx-sbom-generator.tar.gz md5 sum does not match!" 23 | exit 1 24 | fi 25 | 26 | tar -xzvf ./sbom/spdx-sbom-generator.tar.gz -C sbom 27 | fi 28 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore test outputs (bundles are large, should be in artifacts) 2 | output/ 3 | 4 | # Ignore extracted bundle contents during local testing 5 | extracted/ 6 | tmp/ 7 | *.tmp 8 | 9 | # Ignore local test runs 10 | local-test-*/ 11 | -------------------------------------------------------------------------------- /test/baselines/README.md: -------------------------------------------------------------------------------- 1 | # Regression Test Baselines 2 | 3 | This directory contains known-good baseline bundles used for regression testing. 4 | 5 | ## Directory Structure 6 | 7 | - `preflight-v1beta3/` - Baseline for complex-v1beta3.yaml spec 8 | - `preflight-v1beta2/` - Baseline for all-analyzers-v1beta2.yaml spec 9 | - `supportbundle/` - Baseline for all-kubernetes-collectors.yaml spec 10 | - `metadata.json` - Metadata about when baselines were last updated 11 | 12 | ## Creating Initial Baselines 13 | 14 | If this directory is empty, baselines need to be created: 15 | 16 | 1. Run the regression test workflow manually 17 | 2. Download the artifacts 18 | 3. Inspect bundles to verify correctness 19 | 4. Use `scripts/update_baselines.sh` to copy them here 20 | 21 | See `test/README.md` for detailed instructions. 22 | 23 | ## Updating Baselines 24 | 25 | Baselines should only be updated when: 26 | - New collectors are added 27 | - Collector output format changes intentionally 28 | - Kubernetes version is upgraded 29 | - Bug fixes that change collector behavior 30 | 31 | **Never update baselines to make failing tests pass without investigation!** 32 | 33 | Use `scripts/update_baselines.sh` to update from a workflow run. 34 | -------------------------------------------------------------------------------- /test/baselines/preflight-v1beta2/baseline.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/replicatedhq/troubleshoot/05a7a2092eef861d1b84d5e4570e62e3c138abdc/test/baselines/preflight-v1beta2/baseline.tar.gz -------------------------------------------------------------------------------- /test/baselines/preflight-v1beta3/baseline.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/replicatedhq/troubleshoot/05a7a2092eef861d1b84d5e4570e62e3c138abdc/test/baselines/preflight-v1beta3/baseline.tar.gz -------------------------------------------------------------------------------- /test/baselines/supportbundle/baseline.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/replicatedhq/troubleshoot/05a7a2092eef861d1b84d5e4570e62e3c138abdc/test/baselines/supportbundle/baseline.tar.gz -------------------------------------------------------------------------------- /test/e2e/preflight/spec/localHostCollectors.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: ec-cluster-preflight 5 | spec: 6 | collectors: 7 | - cpu: {} 8 | analyzers: 9 | - cpu: 10 | checkName: "Number of CPUs" 11 | outcomes: 12 | - pass: 13 | when: "count < 2" 14 | message: At least 2 CPU cores are required, and 4 CPU cores are recommended 15 | - warn: 16 | when: "count < 4" 17 | message: At least 4 CPU cores are recommended 18 | - pass: 19 | message: This server has at least 4 CPU cores 20 | -------------------------------------------------------------------------------- /test/e2e/support-bundle/host_remote_collector_e2e_test.go: -------------------------------------------------------------------------------- 1 | package e2e 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | "os" 8 | "os/exec" 9 | "testing" 10 | 11 | "sigs.k8s.io/e2e-framework/pkg/envconf" 12 | "sigs.k8s.io/e2e-framework/pkg/features" 13 | ) 14 | 15 | func TestHostRemoteCollector(t *testing.T) { 16 | feature := features.New("Host OS Remote Collector Test"). 17 | Assess("run support bundle command successfully", func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context { 18 | var out bytes.Buffer 19 | var errOut bytes.Buffer 20 | supportbundleName := "host-os-remote-collector" 21 | cmd := exec.CommandContext(ctx, sbBinary(), "spec/remoteHostCollectors.yaml", "--interactive=false", fmt.Sprintf("-o=%s", supportbundleName)) 22 | cmd.Stdout = &out 23 | cmd.Stderr = &errOut 24 | err := cmd.Run() 25 | if err != nil { 26 | t.Fatalf("Failed to run the binary: %v\n%s", err, errOut.String()) 27 | } 28 | 29 | defer func() { 30 | err := os.Remove(fmt.Sprintf("%s.tar.gz", supportbundleName)) 31 | if err != nil { 32 | t.Fatalf("Error removing file: %v", err) 33 | } 34 | }() 35 | 36 | // At this point, we only care that the binary ran successfully, no need to check folder contents. 37 | t.Logf("Binary executed successfully: %s", out.String()) 38 | 39 | return ctx 40 | }).Feature() 41 | 42 | testenv.Test(t, feature) 43 | } 44 | -------------------------------------------------------------------------------- /test/e2e/support-bundle/spec/clusterResources.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: sample 5 | spec: 6 | collectors: 7 | - clusterResources: {} 8 | -------------------------------------------------------------------------------- /test/e2e/support-bundle/spec/helm.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: troubleshoot.sh/v1beta2 3 | kind: SupportBundle 4 | metadata: 5 | name: default 6 | spec: 7 | collectors: 8 | - helm: 9 | collectValues: true 10 | -------------------------------------------------------------------------------- /test/e2e/support-bundle/spec/hostOSRemoteCollector.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: sb 5 | spec: 6 | runHostCollectorsInPod: true 7 | hostCollectors: 8 | - hostOS: {} 9 | -------------------------------------------------------------------------------- /test/e2e/support-bundle/spec/localHostCollectors.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: "remote-host-collectors" 5 | spec: 6 | hostCollectors: 7 | - ipv4Interfaces: {} 8 | - hostServices: {} 9 | - cpu: {} 10 | - hostOS: {} 11 | - memory: {} 12 | - blockDevices: {} 13 | - kernelConfigs: {} 14 | - sysctl: {} 15 | - copy: 16 | collectorName: etc-resolv 17 | path: /etc/resolv.conf 18 | - dns: 19 | collectorName: replicated-app-resolve 20 | hostnames: 21 | - replicated.app 22 | - diskUsage: 23 | collectorName: root-disk-usage 24 | path: / 25 | - diskUsage: 26 | collectorName: tmp 27 | path: /tmp 28 | - http: 29 | collectorName: get-replicated-app 30 | get: 31 | url: https://replicated.app 32 | - run: 33 | collectorName: uptime 34 | command: uptime 35 | -------------------------------------------------------------------------------- /test/e2e/support-bundle/spec/pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: sample 5 | spec: 6 | collectors: 7 | - clusterResources: {} 8 | - clusterInfo: 9 | exclude: false 10 | analyzers: 11 | - clusterPodStatuses: 12 | checkName: "Pod(s) health status(es)" 13 | outcomes: 14 | - fail: 15 | title: "Pod {{ .Name }} is unable to pull images" 16 | when: "== ImagePullBackOff" 17 | message: "A Pod, {{ .Name }}, is unable to pull its image. Status is: {{ .Status.Reason }}. Message is: {{ .Status.Message }}" 18 | - warn: 19 | title: "Pod {{ .Name }} is unhealthy" 20 | when: "!= Healthy" 21 | message: "A Pod, {{ .Name }}, is unhealthy with a status of: {{ .Status.Reason }}. Message is: {{ .Status.Message }}" 22 | - pass: 23 | title: "Pod {{ .Name }} is healthy" 24 | message: "Pod {{ .Name }} is healthy" 25 | -------------------------------------------------------------------------------- /test/e2e/support-bundle/spec/remoteHostCollectors.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: "remote-host-collectors" 5 | spec: 6 | runHostCollectorsInPod: true 7 | hostCollectors: 8 | - ipv4Interfaces: {} 9 | - hostServices: {} 10 | - cpu: {} 11 | - hostOS: {} 12 | - memory: {} 13 | - blockDevices: {} 14 | - kernelConfigs: {} 15 | - sysctl: {} 16 | - copy: 17 | collectorName: etc-resolv 18 | path: /etc/resolv.conf 19 | - dns: 20 | collectorName: replicated-app-resolve 21 | hostnames: 22 | - replicated.app 23 | - diskUsage: 24 | collectorName: root-disk-usage 25 | path: / 26 | - diskUsage: 27 | collectorName: tmp 28 | path: /tmp 29 | - http: 30 | collectorName: get-replicated-app 31 | get: 32 | url: https://replicated.app 33 | - run: 34 | collectorName: uptime 35 | command: uptime 36 | -------------------------------------------------------------------------------- /test/e2e/support-bundle/spec/sonobuoy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: sonobuoy 5 | spec: 6 | collectors: 7 | - sonobuoy: {} 8 | -------------------------------------------------------------------------------- /test/e2e/support-bundle/testdata/charts/nginx-15.2.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/replicatedhq/troubleshoot/05a7a2092eef861d1b84d5e4570e62e3c138abdc/test/e2e/support-bundle/testdata/charts/nginx-15.2.0.tgz -------------------------------------------------------------------------------- /test/e2e/support-bundle/testdata/helm-values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | extraEnvVars: 3 | - name: TEST_ENV_VAR 4 | value: "test-value" 5 | -------------------------------------------------------------------------------- /testdata/db/ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDYDCCAkigAwIBAgIUSM5DVlWGz84qTEXTOyQGCbwMfUgwDQYJKoZIhvcNAQEL 3 | BQAwRzELMAkGA1UEBhMCVUsxFDASBgNVBAgTC094Zm9yZHNoaXJlMQ8wDQYDVQQH 4 | EwZEaWRjb3QxETAPBgNVBAMTCERhdGFiYXNlMCAXDTIyMTEyMzEyMTYwMFoYDzIx 5 | MjIxMDMwMTIxNjAwWjBHMQswCQYDVQQGEwJVSzEUMBIGA1UECBMLT3hmb3Jkc2hp 6 | cmUxDzANBgNVBAcTBkRpZGNvdDERMA8GA1UEAxMIRGF0YWJhc2UwggEiMA0GCSqG 7 | SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDP27FhXvn3PNULwygCWlpAUQThnIp/GQBO 8 | dA4P3bZRKQg3bSVcAd9oPaab8yTW8H/50lvR7EoZRA+06+XjkQ2eOrndvM2FML4h 9 | q5c6IOMII60ARuvJTa+gPbQMP3fy7NN3DoacNd6I5NnvHQBGSVmhT7wYLkzq0rmT 10 | 98WwbPjPk0m+bncN94TTF3tQryqiQ8AuyL+HGaQS5NExB6Rp25jr0GXy+B5K4FbI 11 | gg4Qv8xkpkhNn02vZFTPQage+cm6FoZ6ntN7uBdk4MJYEKXUana6hRH6fKvx3bS6 12 | T3LTXGlb1T8DX6yC5aI3c1BV3VeEfaqo1F0O2zIQdfKFMlRpG/8PAgMBAAGjQjBA 13 | MA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQHbBoB 14 | 7NHvhVEhrqGARbkW6fa1KDANBgkqhkiG9w0BAQsFAAOCAQEABtGMXguhuiTR6d7c 15 | hS1AfoCKQp8tJSLmwy3uu8rhAMjRTxMNUHwfKjRz6WBU/YgrI7gi/BCkRR4wPqVM 16 | 95QtfE5MeSB8esFaDl3V4YdGjxwO6xfnRm3fre9S+2VGXiLFWjtMnV5F7a6k9lPr 17 | HZceK0OzHObu4v/3p35BGq5KXfGy0K4AovmuG1nQ+okylu5FAZKIxjHkXze3h39O 18 | UrdRr1sgWaVI+//aG7u14IQGZcN/8COk5akzwUgBQnS/v8IbjgX8f/seVtbEb0+q 19 | WLWLjHpx5iYAD/Y7hCquO9aGBoDkbFbG/DBmVp/XMF4xFLeYMyA/pZlfPbgsVZgB 20 | El37MQ== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /testdata/db/client.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDsDCCApigAwIBAgIUHnMIIZgJwDbjXw6aHnXJL5nUhuQwDQYJKoZIhvcNAQEL 3 | BQAwRzELMAkGA1UEBhMCVUsxFDASBgNVBAgTC094Zm9yZHNoaXJlMQ8wDQYDVQQH 4 | EwZEaWRjb3QxETAPBgNVBAMTCERhdGFiYXNlMCAXDTIyMTEyMzEyMTYwMFoYDzIx 5 | MjIxMDMwMTIxNjAwWjBFMQswCQYDVQQGEwJVSzEUMBIGA1UECBMLT3hmb3Jkc2hp 6 | cmUxDzANBgNVBAcTBkRpZGNvdDEPMA0GA1UEAxMGY2xpZW50MIIBIjANBgkqhkiG 7 | 9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2FXj0wxictJb2z2mXhEfQ6e1JBXQz5I3CGgF 8 | PNccyKC1Giq4rDJgPB13OmyCzQKI2D/VjI5ovOe84H/OBwXqOHrMnQ1NUuhohDsL 9 | n6eeW4TS0TmI0bxs/LovY170LNWCo/W2Zry+h9R8lWBl0cFfHZhO2QTgdfSHUG+u 10 | F8BXHS6Tmj9TasaGCKZQ+i3ahKM1PagauYShRnWeT4KTCeNcrKmbLhiOUMYaUYjI 11 | Z2dMDncnyOSNt44NlaudSICpZzoSYAY0aMPwSedyiiT/1KCPHPPwfeZ5Y846aRmZ 12 | uhmkj4XIdWJCLYNCsflw6Jbe36CQTcpbvJYSzpszreB1argItQIDAQABo4GTMIGQ 13 | MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw 14 | DAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUjdiaa0Yfd1H/QmJMhvJU7ZvU2i8wHwYD 15 | VR0jBBgwFoAUB2waAezR74VRIa6hgEW5Fun2tSgwEQYDVR0RBAowCIIGY2xpZW50 16 | MA0GCSqGSIb3DQEBCwUAA4IBAQC66ONbumXIUfNbs4YLABQSHhk3UWaXo8GcW8W0 17 | pqi6WnN98Pzbo3rN6HFYgt09LIm0bzRAQDWqkG/D2revc1XI3bVtpQu4rfOl3LOO 18 | 1JHTgObLQNOpYnp3Bzbx7GQPWoGl06tNNQf20V1BGFQtRDpXGZZ3hROezc6AxC9S 19 | xk9u2voc3sqImzOZm4seEIlH0bBh6jGu7Sx78GpmTEUuvPJoPf/jfQKQHXlucsIm 20 | +K5BPPnjs3yNH1g3A8pJh58SQq6Bazm+pUfuA5VbVOoxS0VeHGnCPDpLh3Wu/HLc 21 | BPXv6bGMK44ZXfMEiGQji7LMfPhM+94TBa7SoqrD+mUVyiBc 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /testdata/filesystem_performance_preflight.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: sample 5 | spec: 6 | collectors: 7 | - filesystemPerformance: 8 | collectorName: Filesystem Latency Two Minute Benchmark 9 | timeout: 2m 10 | directory: /var/lib/etcd 11 | fileSize: 22Mi 12 | operationSizeBytes: 2300 13 | datasync: true 14 | enableBackgroundIOPS: true 15 | backgroundIOPSWarmupSeconds: 10 16 | backgroundWriteIOPS: 300 17 | backgroundWriteIOPSJobs: 6 18 | backgroundReadIOPS: 50 19 | backgroundReadIOPSJobs: 1 20 | -------------------------------------------------------------------------------- /testdata/goldpinger/checkall-one-pod.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosts": [ 3 | { 4 | "hostIP": "172.18.0.3", 5 | "podIP": "10.42.0.17", 6 | "podName": "gp-goldpinger-xn9rg" 7 | } 8 | ], 9 | "responses": { 10 | "gp-goldpinger-xn9rg": { 11 | "HostIP": "172.18.0.3", 12 | "OK": true, 13 | "PodIP": "10.42.0.17", 14 | "response": { 15 | "podResults": { 16 | "gp-goldpinger-xn9rg": { 17 | "HostIP": "172.18.0.3", 18 | "OK": true, 19 | "PingTime": "2024-09-24T07:10:10.431Z", 20 | "PodIP": "10.42.0.17", 21 | "response": { 22 | "boot_time": "2024-09-24T07:09:40.411Z" 23 | }, 24 | "status-code": 200 25 | } 26 | } 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /testdata/preflightspec/troubleshoot_v1beta2_host_preflight_validate_empty_analyzers_gotest.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: sample 5 | spec: 6 | collectors: 7 | - cpu: {} 8 | analyzers: [] 9 | -------------------------------------------------------------------------------- /testdata/preflightspec/troubleshoot_v1beta2_host_preflight_validate_empty_collectors_gotest.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: HostPreflight 3 | metadata: 4 | name: sample 5 | spec: 6 | analyzers: 7 | - hostOS: 8 | outcomes: 9 | - fail: 10 | when: "ubuntu-16.04-kernel < 4.15" 11 | message: unsupported distribution 12 | - pass: 13 | when: "ubuntu-18.04-kernel >= 4.15" 14 | message: supported distribution 15 | -------------------------------------------------------------------------------- /testdata/preflightspec/troubleshoot_v1beta2_preflight_gotest.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: Preflight 3 | metadata: 4 | name: go-test-preflight 5 | spec: 6 | # TODO: mock a local HTTP server to send this to 7 | #uploadResultsTo: http://someurl 8 | collectors: 9 | - data: 10 | name: example.json 11 | data: | 12 | { 13 | "foo": "bar", 14 | "stuff": { 15 | "foo": "bar", 16 | "bar": true 17 | }, 18 | "morestuff": [ 19 | { 20 | "foo": { 21 | "bar": 123 22 | } 23 | } 24 | ] 25 | } 26 | analyzers: 27 | - jsonCompare: 28 | checkName: Compare JSON Example 29 | fileName: example.json 30 | path: "morestuff.[0].foo.bar" 31 | value: | 32 | 123 33 | outcomes: 34 | - fail: 35 | when: "false" 36 | message: The collected data does not match the value. 37 | - pass: 38 | when: "true" 39 | message: The collected data matches the value. 40 | -------------------------------------------------------------------------------- /testdata/preflightspec/troubleshoot_v1beta2_preflight_validate_empty_analyzers_gotest.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: Preflight 3 | metadata: 4 | name: sample 5 | spec: 6 | collectors: 7 | - clusterInfo: {} 8 | analyzers: [] 9 | -------------------------------------------------------------------------------- /testdata/preflightspec/troubleshoot_v1beta2_preflight_validate_empty_collectors_gotest.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: Preflight 3 | metadata: 4 | name: sample 5 | spec: 6 | collectors: [] 7 | analyzers: 8 | - clusterVersion: 9 | outcomes: 10 | - pass: 11 | message: Cluster is up to date 12 | -------------------------------------------------------------------------------- /testdata/preflightspec/troubleshoot_v1beta2_preflight_validate_excluded_all_default_collectors_gotest.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: Preflight 3 | metadata: 4 | name: sample 5 | spec: 6 | collectors: 7 | - clusterInfo: 8 | exclude: true 9 | - clusterResources: 10 | exclude: true 11 | analyzers: 12 | - clusterVersion: 13 | outcomes: 14 | - pass: 15 | message: Cluster is up to date 16 | -------------------------------------------------------------------------------- /testdata/preflightspec/troubleshoot_v1beta2_preflight_validate_excluded_all_non_default_collectors_gotest.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: Preflight 3 | metadata: 4 | name: sample 5 | spec: 6 | collectors: 7 | - http: 8 | exclude: true 9 | name: healthz 10 | get: 11 | url: http://api:3000/healthz 12 | - data: 13 | exclude: true 14 | collectorName: my-password-dump 15 | name: data 16 | data: | 17 | my super secret password is abc123 18 | another redaction will go here 19 | analyzers: 20 | - distribution: 21 | outcomes: 22 | - pass: 23 | when: "== k3s" 24 | message: K3S is a supported distribution 25 | - warn: 26 | message: Unable to determine the distribution of Kubernetes 27 | - clusterVersion: 28 | outcomes: 29 | - pass: 30 | message: Cluster is up to date 31 | -------------------------------------------------------------------------------- /testdata/preflightspec/troubleshoot_v1beta2_preflight_validate_excluded_analyzers_gotest.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: Preflight 3 | metadata: 4 | name: sample 5 | spec: 6 | collectors: 7 | - clusterInfo: {} 8 | - clusterResources: {} 9 | analyzers: 10 | - distribution: 11 | exclude: true 12 | outcomes: 13 | - pass: 14 | when: "== k3s" 15 | message: K3S is a supported distribution 16 | - warn: 17 | message: Unable to determine the distribution of Kubernetes 18 | - clusterVersion: 19 | exclude: true 20 | outcomes: 21 | - pass: 22 | message: Cluster is up to date 23 | -------------------------------------------------------------------------------- /testdata/preflightspec/troubleshoot_v1beta2_preflight_validate_excluded_one_default_collectors_gotest.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: Preflight 3 | metadata: 4 | name: sample 5 | spec: 6 | collectors: 7 | - clusterInfo: 8 | exclude: true 9 | analyzers: 10 | - clusterVersion: 11 | outcomes: 12 | - pass: 13 | message: Cluster is up to date 14 | -------------------------------------------------------------------------------- /testdata/preflightspec/troubleshoot_v1beta2_preflight_validate_spec_with_upload_results_gotest.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: Preflight 3 | metadata: 4 | name: go-test-preflight 5 | spec: 6 | uploadResultsTo: http://someurl 7 | collectors: 8 | - data: 9 | name: example.json 10 | data: | 11 | { 12 | "foo": "bar", 13 | "stuff": { 14 | "foo": "bar", 15 | "bar": true 16 | }, 17 | "morestuff": [ 18 | { 19 | "foo": { 20 | "bar": 123 21 | } 22 | } 23 | ] 24 | } 25 | analyzers: 26 | - jsonCompare: 27 | checkName: Compare JSON Example 28 | fileName: example.json 29 | path: "morestuff.[0].foo.bar" 30 | value: | 31 | 123 32 | outcomes: 33 | - fail: 34 | when: "false" 35 | message: The collected data does not match the value. 36 | - pass: 37 | when: "true" 38 | message: The collected data matches the value. 39 | -------------------------------------------------------------------------------- /testdata/supportbundle/configmaps.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: example-collect-all-configmap-data 5 | spec: 6 | collectors: 7 | - configMap: 8 | name: kube-root-ca.crt 9 | # expected in default namespace 10 | - configMap: 11 | name: cluster-info 12 | # missing namespace parameter 13 | - configMap: 14 | name: does-not-exist 15 | - configMap: 16 | selector: 17 | - app=kube-proxy 18 | # only selector set 19 | - configMap: 20 | name: coredns 21 | namespace: kube-system 22 | -------------------------------------------------------------------------------- /testdata/supportbundle/empty.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: troubleshoot.sh/v1beta2 2 | kind: SupportBundle 3 | metadata: 4 | name: empty 5 | spec: {} 6 | -------------------------------------------------------------------------------- /testdata/supportbundle/extracted-sb/cluster-resources/pods/logs/default/static-hi/static-hi.log: -------------------------------------------------------------------------------- 1 | hi static! 2 | 3 | -------------------------------------------------------------------------------- /testdata/supportbundle/extracted-sb/static-hi.log: -------------------------------------------------------------------------------- 1 | cluster-resources/pods/logs/default/static-hi/static-hi.log -------------------------------------------------------------------------------- /testdata/supportbundle/labelled-bundle-unlabelled-redactor/redact-spec-1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: unlabelled-redactor-spec-1 5 | data: 6 | redactor-spec: | 7 | apiVersion: troubleshoot.sh/v1beta2 8 | kind: Redactor 9 | metadata: 10 | name: unlabelled-redactor-spec-1 11 | spec: 12 | redactors: 13 | - name: redact-text-1 14 | removals: 15 | values: 16 | - REDACT FIRST TEXT PLEASE 17 | customer-redactor-spec: | 18 | apiVersion: troubleshoot.sh/v1beta2 19 | kind: Redactor 20 | metadata: 21 | name: unlabelled-redactor-spec-1 22 | spec: 23 | redactors: 24 | - name: redact-text-1 25 | removals: 26 | values: 27 | - REDACT FIRST TEXT PLEASE 28 | garbagge: MWdRRTlBRi9YNzB3eUE2VEgvWjdhRFVUR1UvRmU3TXdUR3Q4cnE4Nkti 29 | -------------------------------------------------------------------------------- /testdata/supportbundle/labelled-bundle-unlabelled-redactor/sb-spec-1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: labelled-support-bundle-1 5 | labels: 6 | troubleshoot.io/kind: support-bundle 7 | stringData: 8 | support-bundle-spec: | 9 | apiVersion: troubleshoot.sh/v1beta2 10 | kind: SupportBundle 11 | metadata: 12 | name: labelled-support-bundle-1 13 | spec: 14 | collectors: 15 | - data: 16 | name: echo-hi-1 17 | data: "I am labelled-support-bundle-1 REDACT FIRST TEXT PLEASE" 18 | custom-spec-key: | 19 | apiVersion: troubleshoot.sh/v1beta2 20 | kind: SupportBundle 21 | metadata: 22 | name: custom-spec-key 23 | spec: 24 | collectors: 25 | - data: 26 | name: echo-hi-3 27 | data: "I am custom-spec-key REDACT FIRST TEXT PLEASE" 28 | garbagge: MWdRRTlBRi9YNzB3eUE2VEgvWjdhRFVUR1UvRmU3TXdUR3Q4cnE4Nkti 29 | -------------------------------------------------------------------------------- /testdata/supportbundle/labelled-specs/0-ns.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: labelled-specs 5 | -------------------------------------------------------------------------------- /testdata/supportbundle/labelled-specs/redact-spec-1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: labelled-redactor-spec-1 5 | labels: 6 | troubleshoot.io/kind: support-bundle 7 | data: 8 | redactor-spec: | 9 | apiVersion: troubleshoot.sh/v1beta2 10 | kind: Redactor 11 | metadata: 12 | name: labelled-redactor-spec-1 13 | spec: 14 | redactors: 15 | - name: redact-text-1 16 | removals: 17 | values: 18 | - REDACT FIRST TEXT PLEASE 19 | customer-redactor-spec: | 20 | apiVersion: troubleshoot.sh/v1beta2 21 | kind: Redactor 22 | metadata: 23 | name: labelled-redactor-spec-1 24 | spec: 25 | redactors: 26 | - name: redact-text-1 27 | removals: 28 | values: 29 | - REDACT FIRST TEXT PLEASE 30 | garbagge: MWdRRTlBRi9YNzB3eUE2VEgvWjdhRFVUR1UvRmU3TXdUR3Q4cnE4Nkti 31 | -------------------------------------------------------------------------------- /testdata/supportbundle/labelled-specs/redact-spec-2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: labelled-redactor-spec-2 5 | labels: 6 | troubleshoot.io/kind: support-bundle 7 | namespace: labelled-specs 8 | stringData: 9 | redactor-spec: | 10 | apiVersion: troubleshoot.sh/v1beta2 11 | kind: Redactor 12 | metadata: 13 | name: labelled-redactor-spec-2 14 | spec: 15 | redactors: 16 | - name: redact-text-2 17 | removals: 18 | values: 19 | - REDACT SECOND TEXT PLEASE 20 | garbagge: MWdRRTlBRi9YNzB3eUE2VEgvWjdhRFVUR1UvRmU3TXdUR3Q4cnE4Nkti 21 | -------------------------------------------------------------------------------- /testdata/supportbundle/labelled-specs/redact-spec-3.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: labelled-redactor-spec-3 5 | labels: 6 | troubleshoot.sh/kind: support-bundle 7 | data: 8 | redactor-spec: | 9 | apiVersion: troubleshoot.sh/v1beta2 10 | kind: Redactor 11 | metadata: 12 | name: labelled-redactor-spec-3 13 | spec: 14 | redactors: 15 | - name: redact-text-3 16 | removals: 17 | values: 18 | - REDACT FIRST TEXT PLEASE 19 | garbagge: MWdRRTlBRi9YNzB3eUE2VEgvWjdhRFVUR1UvRmU3TXdUR3Q4cnE4Nkti 20 | -------------------------------------------------------------------------------- /testdata/supportbundle/labelled-specs/redact-spec-4.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: labelled-redactor-spec-4 5 | labels: 6 | troubleshoot.sh/kind: support-bundle 7 | namespace: labelled-specs 8 | stringData: 9 | redactor-spec: | 10 | apiVersion: troubleshoot.sh/v1beta2 11 | kind: Redactor 12 | metadata: 13 | name: labelled-redactor-spec-4 14 | spec: 15 | redactors: 16 | - name: redact-text-4 17 | removals: 18 | values: 19 | - REDACT SECOND TEXT PLEASE 20 | garbagge: MWdRRTlBRi9YNzB3eUE2VEgvWjdhRFVUR1UvRmU3TXdUR3Q4cnE4Nkti 21 | -------------------------------------------------------------------------------- /testdata/supportbundle/labelled-specs/sb-spec-1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: labelled-support-bundle-1 5 | labels: 6 | troubleshoot.io/kind: support-bundle 7 | stringData: 8 | support-bundle-spec: | 9 | apiVersion: troubleshoot.sh/v1beta2 10 | kind: SupportBundle 11 | metadata: 12 | name: labelled-support-bundle-1 13 | spec: 14 | collectors: 15 | - data: 16 | name: echo-hi-1 17 | data: "I am labelled-support-bundle-1 REDACT FIRST TEXT PLEASE" 18 | custom-spec-key: | 19 | apiVersion: troubleshoot.sh/v1beta2 20 | kind: SupportBundle 21 | metadata: 22 | name: custom-spec-key 23 | spec: 24 | collectors: 25 | - data: 26 | name: echo-hi-3 27 | data: "I am custom-spec-key REDACT FIRST TEXT PLEASE" 28 | garbagge: MWdRRTlBRi9YNzB3eUE2VEgvWjdhRFVUR1UvRmU3TXdUR3Q4cnE4Nkti 29 | -------------------------------------------------------------------------------- /testdata/supportbundle/labelled-specs/sb-spec-2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: labelled-support-bundle-2 5 | labels: 6 | troubleshoot.io/kind: support-bundle 7 | namespace: labelled-specs 8 | data: 9 | support-bundle-spec: | 10 | apiVersion: troubleshoot.sh/v1beta2 11 | kind: SupportBundle 12 | metadata: 13 | name: labelled-support-bundle-2 14 | spec: 15 | collectors: 16 | - data: 17 | name: echo-hi-2 18 | data: "I am labelled-support-bundle-2 REDACT SECOND TEXT PLEASE" 19 | garbagge: MWdRRTlBRi9YNzB3eUE2VEgvWjdhRFVUR1UvRmU3TXdUR3Q4cnE4Nkti 20 | -------------------------------------------------------------------------------- /testdata/supportbundle/labelled-specs/sb-spec-3.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: labelled-support-bundle-3 5 | labels: 6 | troubleshoot.sh/kind: support-bundle 7 | stringData: 8 | support-bundle-spec: | 9 | apiVersion: troubleshoot.sh/v1beta2 10 | kind: SupportBundle 11 | metadata: 12 | name: labelled-support-bundle-3 13 | spec: 14 | collectors: 15 | - data: 16 | name: echo-hi-3 17 | data: "I am labelled-support-bundle-3 REDACT FIRST TEXT PLEASE" 18 | garbagge: MWdRRTlBRi9YNzB3eUE2VEgvWjdhRFVUR1UvRmU3TXdUR3Q4cnE4Nkti 19 | -------------------------------------------------------------------------------- /testdata/supportbundle/labelled-specs/sb-spec-4.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: labelled-support-bundle-4 5 | labels: 6 | troubleshoot.sh/kind: support-bundle 7 | namespace: labelled-specs 8 | data: 9 | support-bundle-spec: | 10 | apiVersion: troubleshoot.sh/v1beta2 11 | kind: SupportBundle 12 | metadata: 13 | name: labelled-support-bundle-4 14 | spec: 15 | collectors: 16 | - data: 17 | name: echo-hi-4 18 | data: "I am labelled-support-bundle-4 REDACT SECOND TEXT PLEASE" 19 | garbagge: MWdRRTlBRi9YNzB3eUE2VEgvWjdhRFVUR1UvRmU3TXdUR3Q4cnE4Nkti 20 | -------------------------------------------------------------------------------- /testdata/supportbundle/missing-version.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/replicatedhq/troubleshoot/05a7a2092eef861d1b84d5e4570e62e3c138abdc/testdata/supportbundle/missing-version.tar.gz -------------------------------------------------------------------------------- /testdata/supportbundle/support-bundle.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/replicatedhq/troubleshoot/05a7a2092eef861d1b84d5e4570e62e3c138abdc/testdata/supportbundle/support-bundle.tar.gz -------------------------------------------------------------------------------- /testdata/yamldocs/multidoc-spec-2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: multi-spec 5 | labels: 6 | troubleshoot.io/kind: support-bundle 7 | data: 8 | support-bundle-spec: |- 9 | apiVersion: troubleshoot.sh/v1beta2 10 | kind: SupportBundle 11 | spec: 12 | collectors: 13 | - logs: 14 | name: all-logs 15 | redactor-spec: | 16 | apiVersion: troubleshoot.sh/v1beta2 17 | kind: Redactor 18 | spec: 19 | redactors: 20 | - name: redact-text-1 21 | removals: 22 | values: 23 | - abc123 24 | preflight.yaml: |- 25 | apiVersion: troubleshoot.sh/v1beta2 26 | kind: Preflight 27 | spec: 28 | collectors: 29 | - clusterResources: 30 | ignoreRBAC: true 31 | analyzers: 32 | - clusterVersion: 33 | outcomes: 34 | - pass: 35 | message: Cluster is up to date 36 | -------------------------------------------------------------------------------- /testdata/yamldocs/multidoc-spec-3.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: sb-spec 5 | labels: 6 | troubleshoot.io/kind: support-bundle 7 | data: 8 | support-bundle-spec: |- 9 | apiVersion: troubleshoot.sh/v1beta2 10 | kind: SupportBundle 11 | spec: 12 | collectors: 13 | - logs: 14 | name: all-logs 15 | --- 16 | apiVersion: troubleshoot.sh/v1beta2 17 | kind: SupportBundle 18 | spec: 19 | collectors: 20 | - clusterResources: {} 21 | --- 22 | apiVersion: troubleshoot.sh/v1beta2 23 | kind: SupportBundle 24 | spec: 25 | collectors: 26 | - clusterInfo: {} 27 | --------------------------------------------------------------------------------