diff --git a/cmd/troubleshoot/cli/run.go b/cmd/troubleshoot/cli/run.go index 837475255..f141ed87d 100644 --- a/cmd/troubleshoot/cli/run.go +++ b/cmd/troubleshoot/cli/run.go @@ -9,6 +9,7 @@ import ( "os" "os/signal" "path/filepath" + "reflect" "sync" "time" @@ -183,6 +184,7 @@ func runTroubleshoot(v *viper.Viper, args []string) error { OutputPath: v.GetString("output"), Redact: v.GetBool("redact"), FromCLI: true, + RunHostCollectorsInPod: mainBundle.Metadata.RunHostCollectorsInPod, } nonInteractiveOutput := analysisOutput{} @@ -197,7 +199,7 @@ func runTroubleshoot(v *viper.Viper, args []string) error { if len(response.AnalyzerResults) > 0 { if interactive { - if err := showInteractiveResults(mainBundle.Name, response.AnalyzerResults, response.ArchivePath); err != nil { + if err := showInteractiveResults(mainBundle.Metadata.Name, response.AnalyzerResults, response.ArchivePath); err != nil { interactive = false } } else { @@ -206,7 +208,7 @@ func runTroubleshoot(v *viper.Viper, args []string) error { } if !response.FileUploaded { - if appName := mainBundle.Labels["applicationName"]; appName != "" { + if appName := mainBundle.Metadata.Labels["applicationName"]; appName != "" { f := `A support bundle for %s has been created in this directory named %s. Please upload it on the Troubleshoot page of the %s Admin Console to begin analysis.` @@ -335,14 +337,25 @@ func loadSpecs(ctx context.Context, args []string, client kubernetes.Interface) APIVersion: "troubleshoot.sh/v1beta2", Kind: "SupportBundle", }, - ObjectMeta: metav1.ObjectMeta{ - Name: "merged-support-bundle-spec", + Metadata: troubleshootv1beta2.SupportBundleMetadata{ + ObjectMeta: metav1.ObjectMeta{ + Name: "merged-support-bundle-spec", + }, + RunHostCollectorsInPod: false, }, } + + var enableRunHostCollectorsInPod bool + for _, sb := range kinds.SupportBundlesV1Beta2 { sb := sb mainBundle = supportbundle.ConcatSpec(mainBundle, &sb) + //check if sb has metadata and if it has RunHostCollectorsInPod set to true + if !reflect.DeepEqual(sb.Metadata.ObjectMeta, metav1.ObjectMeta{}) && sb.Metadata.RunHostCollectorsInPod { + enableRunHostCollectorsInPod = sb.Metadata.RunHostCollectorsInPod + } } + mainBundle.Metadata.RunHostCollectorsInPod = enableRunHostCollectorsInPod for _, c := range kinds.CollectorsV1Beta2 { mainBundle.Spec.Collectors = util.Append(mainBundle.Spec.Collectors, c.Spec.Collectors) diff --git a/config/crds/troubleshoot.sh_hostpreflights.yaml b/config/crds/troubleshoot.sh_hostpreflights.yaml index 907e3632a..f8c06613c 100644 --- a/config/crds/troubleshoot.sh_hostpreflights.yaml +++ b/config/crds/troubleshoot.sh_hostpreflights.yaml @@ -1860,6 +1860,13 @@ spec: - backgroundWriteIOPSJobs - enableBackgroundIOPS type: object + hostOS: + properties: + collectorName: + type: string + exclude: + type: BoolString + type: object hostServices: properties: collectorName: diff --git a/config/crds/troubleshoot.sh_preflights.yaml b/config/crds/troubleshoot.sh_preflights.yaml index 26250cd5f..5bb6ac940 100644 --- a/config/crds/troubleshoot.sh_preflights.yaml +++ b/config/crds/troubleshoot.sh_preflights.yaml @@ -18703,6 +18703,13 @@ spec: - backgroundWriteIOPSJobs - enableBackgroundIOPS type: object + hostOS: + properties: + collectorName: + type: string + exclude: + type: BoolString + type: object hostServices: properties: collectorName: diff --git a/config/crds/troubleshoot.sh_remotecollectors.yaml b/config/crds/troubleshoot.sh_remotecollectors.yaml index 70608680c..e408a2c18 100644 --- a/config/crds/troubleshoot.sh_remotecollectors.yaml +++ b/config/crds/troubleshoot.sh_remotecollectors.yaml @@ -193,6 +193,13 @@ spec: - backgroundWriteIOPSJobs - enableBackgroundIOPS type: object + hostOS: + properties: + collectorName: + type: string + exclude: + type: BoolString + type: object hostServices: properties: collectorName: diff --git a/pkg/apis/troubleshoot/v1beta2/remote_collector_shared.go b/pkg/apis/troubleshoot/v1beta2/remote_collector_shared.go index 9eb7a33af..25c99acce 100644 --- a/pkg/apis/troubleshoot/v1beta2/remote_collector_shared.go +++ b/pkg/apis/troubleshoot/v1beta2/remote_collector_shared.go @@ -16,6 +16,9 @@ type RemoteCollectorMeta struct { type RemoteCPU struct { RemoteCollectorMeta `json:",inline" yaml:",inline"` } +type RemoteHostOS struct { + RemoteCollectorMeta `json:",inline" yaml:",inline"` +} type RemoteMemory struct { RemoteCollectorMeta `json:",inline" yaml:",inline"` @@ -170,6 +173,7 @@ type RemoteCollect struct { FilesystemPerformance *RemoteFilesystemPerformance `json:"filesystemPerformance,omitempty" yaml:"filesystemPerformance,omitempty"` Certificate *RemoteCertificate `json:"certificate,omitempty" yaml:"certificate,omitempty"` HostServices *RemoteServices `json:"hostServices,omitempty" yaml:"hostServices,omitempty"` + HostOS *RemoteHostOS `json:"hostOS,omitempty" yaml:"hostOS,omitempty"` } func (c *RemoteCollect) AccessReviewSpecs(overrideNS string) []authorizationv1.SelfSubjectAccessReviewSpec { diff --git a/pkg/apis/troubleshoot/v1beta2/supportbundle_types.go b/pkg/apis/troubleshoot/v1beta2/supportbundle_types.go index 40465f892..4c1a6df39 100644 --- a/pkg/apis/troubleshoot/v1beta2/supportbundle_types.go +++ b/pkg/apis/troubleshoot/v1beta2/supportbundle_types.go @@ -20,6 +20,11 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +type SupportBundleMetadata struct { + metav1.ObjectMeta `json:",inline" yaml:",inline"` + RunHostCollectorsInPod bool `json:"runHostCollectorsInPod,omitempty" yaml:"runHostCollectorsInPod,omitempty"` +} + // SupportBundleSpec defines the desired state of SupportBundle type SupportBundleSpec struct { AfterCollection []*AfterCollection `json:"afterCollection,omitempty" yaml:"afterCollection,omitempty"` @@ -43,8 +48,8 @@ type SupportBundleStatus struct { // SupportBundle is the Schema for the SupportBundles API // +k8s:openapi-gen=true type SupportBundle struct { - metav1.TypeMeta `json:",inline" yaml:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` + metav1.TypeMeta `json:",inline" yaml:",inline"` + Metadata SupportBundleMetadata `json:"metadata,omitempty" yaml:"metadata,omitempty"` Spec SupportBundleSpec `json:"spec,omitempty" yaml:"spec,omitempty"` Status SupportBundleStatus `json:"status,omitempty"` diff --git a/pkg/apis/troubleshoot/v1beta2/zz_generated.deepcopy.go b/pkg/apis/troubleshoot/v1beta2/zz_generated.deepcopy.go index adb8e6be7..6172d11f3 100644 --- a/pkg/apis/troubleshoot/v1beta2/zz_generated.deepcopy.go +++ b/pkg/apis/troubleshoot/v1beta2/zz_generated.deepcopy.go @@ -3887,6 +3887,11 @@ func (in *RemoteCollect) DeepCopyInto(out *RemoteCollect) { *out = new(RemoteServices) (*in).DeepCopyInto(*out) } + if in.HostOS != nil { + in, out := &in.HostOS, &out.HostOS + *out = new(RemoteHostOS) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemoteCollect. @@ -4106,6 +4111,22 @@ func (in *RemoteHTTPLoadBalancer) DeepCopy() *RemoteHTTPLoadBalancer { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RemoteHostOS) DeepCopyInto(out *RemoteHostOS) { + *out = *in + in.RemoteCollectorMeta.DeepCopyInto(&out.RemoteCollectorMeta) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemoteHostOS. +func (in *RemoteHostOS) DeepCopy() *RemoteHostOS { + if in == nil { + return nil + } + out := new(RemoteHostOS) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RemoteIPV4Interfaces) DeepCopyInto(out *RemoteIPV4Interfaces) { *out = *in @@ -4620,7 +4641,7 @@ func (in *SubnetAvailableAnalyze) DeepCopy() *SubnetAvailableAnalyze { func (in *SupportBundle) DeepCopyInto(out *SupportBundle) { *out = *in out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Metadata.DeepCopyInto(&out.Metadata) in.Spec.DeepCopyInto(&out.Spec) out.Status = in.Status } @@ -4675,6 +4696,22 @@ func (in *SupportBundleList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SupportBundleMetadata) DeepCopyInto(out *SupportBundleMetadata) { + *out = *in + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SupportBundleMetadata. +func (in *SupportBundleMetadata) DeepCopy() *SupportBundleMetadata { + if in == nil { + return nil + } + out := new(SupportBundleMetadata) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SupportBundleSpec) DeepCopyInto(out *SupportBundleSpec) { *out = *in diff --git a/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/fake/fake_supportbundle.go b/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/fake/fake_supportbundle.go index b28fc4025..45d40c88c 100644 --- a/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/fake/fake_supportbundle.go +++ b/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/fake/fake_supportbundle.go @@ -64,7 +64,7 @@ func (c *FakeSupportBundles) List(ctx context.Context, opts v1.ListOptions) (res } list := &v1beta2.SupportBundleList{ListMeta: obj.(*v1beta2.SupportBundleList).ListMeta} for _, item := range obj.(*v1beta2.SupportBundleList).Items { - if label.Matches(labels.Set(item.Labels)) { + if label.Matches(labels.Set(item.Metadata.Labels)) { list.Items = append(list.Items, item) } } diff --git a/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/supportbundle.go b/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/supportbundle.go index 49399e767..521f4df90 100644 --- a/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/supportbundle.go +++ b/pkg/client/troubleshootclientset/typed/troubleshoot/v1beta2/supportbundle.go @@ -127,7 +127,7 @@ func (c *supportBundles) Update(ctx context.Context, supportBundle *v1beta2.Supp err = c.client.Put(). Namespace(c.ns). Resource("supportbundles"). - Name(supportBundle.Name). + Name(supportBundle.Metadata.Name). VersionedParams(&opts, scheme.ParameterCodec). Body(supportBundle). Do(ctx). @@ -142,7 +142,7 @@ func (c *supportBundles) UpdateStatus(ctx context.Context, supportBundle *v1beta err = c.client.Put(). Namespace(c.ns). Resource("supportbundles"). - Name(supportBundle.Name). + Name(supportBundle.Metadata.Name). SubResource("status"). VersionedParams(&opts, scheme.ParameterCodec). Body(supportBundle). diff --git a/pkg/collect/cluster_resources_test.go b/pkg/collect/cluster_resources_test.go index d261d02ed..f8bf6c0a2 100644 --- a/pkg/collect/cluster_resources_test.go +++ b/pkg/collect/cluster_resources_test.go @@ -481,9 +481,11 @@ func TestCollectClusterResources_CustomResource(t *testing.T) { // Create a CR sbObject := troubleshootv1beta2.SupportBundle{ - ObjectMeta: metav1.ObjectMeta{ - Name: "supportbundle", - Namespace: "default", + Metadata: troubleshootv1beta2.SupportBundleMetadata{ + ObjectMeta: metav1.ObjectMeta{ + Name: "supportbundle", + Namespace: "default", + }, }, TypeMeta: metav1.TypeMeta{ Kind: "SupportBundle", diff --git a/pkg/collect/collect.go b/pkg/collect/collect.go index 2ead4cae8..1aef28f3f 100644 --- a/pkg/collect/collect.go +++ b/pkg/collect/collect.go @@ -15,8 +15,9 @@ import ( var ( // ErrCollectorNotFound is returned when an undefined host collector is // specified by the user. - ErrHostCollectorNotFound = errors.New("unrecognized host collector") - ErrInsufficientPermissionsToRun = errors.New("insufficient permissions to run all collectors") + ErrHostCollectorNotFound = errors.New("unrecognized host collector") + ErrInsufficientPermissionsToRun = errors.New("insufficient permissions to run all collectors") + ErrRemoteCollectorNotImplemented = errors.New("unimplemented remote collector") ) type CollectorRunOpts struct { @@ -28,7 +29,8 @@ type CollectorRunOpts struct { PullPolicy string LabelSelector string Timeout time.Duration - ProgressChan chan interface{} + ProgressChan chan<- interface{} + NamePrefix string } type CollectProgress struct { @@ -48,7 +50,7 @@ type RemoteCollectResult struct { AllCollectedData map[string][]byte Collectors RemoteCollectors Spec *troubleshootv1beta2.RemoteCollector - isRBACAllowed bool + IsRBACAllowed bool } // CollectHost runs the collection phase for a local collector. @@ -107,6 +109,7 @@ func CollectRemote(c *troubleshootv1beta2.RemoteCollector, additionalRedactors * LabelSelector: opts.LabelSelector, Namespace: opts.Namespace, Timeout: opts.Timeout, + NamePrefix: opts.NamePrefix, } collectors = append(collectors, &collector) } @@ -129,7 +132,7 @@ func CollectRemote(c *troubleshootv1beta2.RemoteCollector, additionalRedactors * } if foundForbidden && !opts.CollectWithoutPermissions { - collectResult.isRBACAllowed = false + collectResult.IsRBACAllowed = false return collectResult, ErrInsufficientPermissionsToRun } diff --git a/pkg/collect/host_block_device.go b/pkg/collect/host_block_device.go index b6e553c1c..86c1bee86 100644 --- a/pkg/collect/host_block_device.go +++ b/pkg/collect/host_block_device.go @@ -108,3 +108,7 @@ func parseLsblkOutput(output []byte) ([]BlockDeviceInfo, error) { return devices, nil } + +func (c *CollectHostBlockDevices) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_certificate.go b/pkg/collect/host_certificate.go index f2a5214ba..c6da05db3 100644 --- a/pkg/collect/host_certificate.go +++ b/pkg/collect/host_certificate.go @@ -77,3 +77,7 @@ func isEncryptedKey(filename string) (bool, error) { } return bytes.Contains(data, []byte("ENCRYPTED")), nil } + +func (c *CollectHostCertificate) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_certificates_collection.go b/pkg/collect/host_certificates_collection.go index ecb0c89ae..3abe98c8e 100644 --- a/pkg/collect/host_certificates_collection.go +++ b/pkg/collect/host_certificates_collection.go @@ -104,3 +104,7 @@ func HostCertsParser(certPath string) HostCertificatesCollection { Message: CertValid, } } + +func (c *CollectHostCertificatesCollection) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_cgroup.go b/pkg/collect/host_cgroup.go index 5a81a5bd3..b046bd24b 100644 --- a/pkg/collect/host_cgroup.go +++ b/pkg/collect/host_cgroup.go @@ -97,3 +97,7 @@ func parseV1ControllerNames(r io.Reader) ([]string, error) { return names, nil } + +func (c *CollectHostCGroups) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_collector.go b/pkg/collect/host_collector.go index 4833024a0..04e96de0a 100644 --- a/pkg/collect/host_collector.go +++ b/pkg/collect/host_collector.go @@ -8,6 +8,7 @@ type HostCollector interface { Title() string IsExcluded() (bool, error) Collect(progressChan chan<- interface{}) (map[string][]byte, error) + RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) // RemoteCollect is used to priviledge pods to collect data from different nodes } func GetHostCollector(collector *troubleshootv1beta2.HostCollect, bundlePath string) (HostCollector, bool) { diff --git a/pkg/collect/host_copy.go b/pkg/collect/host_copy.go index 8642ba1aa..2e7201f13 100644 --- a/pkg/collect/host_copy.go +++ b/pkg/collect/host_copy.go @@ -137,3 +137,7 @@ func (c *CollectHostCopy) copyDir(src, dst string, result CollectorResult) error return nil } + +func (c *CollectHostCopy) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_cpu.go b/pkg/collect/host_cpu.go index a353a7f4b..feae0a29d 100644 --- a/pkg/collect/host_cpu.go +++ b/pkg/collect/host_cpu.go @@ -54,3 +54,7 @@ func (c *CollectHostCPU) Collect(progressChan chan<- interface{}) (map[string][] return output, nil } + +func (c *CollectHostCPU) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_disk_usage.go b/pkg/collect/host_disk_usage.go index 8c540f308..8c0e376a2 100644 --- a/pkg/collect/host_disk_usage.go +++ b/pkg/collect/host_disk_usage.go @@ -86,3 +86,7 @@ func traverseFiletreeDirExists(filename string) (string, error) { } return "", errors.New("max recursion exceeded") } + +func (c *CollectHostDiskUsage) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_dns.go b/pkg/collect/host_dns.go index 7f8757935..b362a802c 100644 --- a/pkg/collect/host_dns.go +++ b/pkg/collect/host_dns.go @@ -203,3 +203,7 @@ func queryDNS(name, query, server string) DNSEntry { } return entry } + +func (c *CollectHostDNS) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_filesystem_performance.go b/pkg/collect/host_filesystem_performance.go index 829e09cf1..36b102025 100644 --- a/pkg/collect/host_filesystem_performance.go +++ b/pkg/collect/host_filesystem_performance.go @@ -460,3 +460,7 @@ func collectFioResults(ctx context.Context, hostCollector *troubleshootv1beta2.F return &result, nil } + +func (c *CollectHostFilesystemPerformance) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_http.go b/pkg/collect/host_http.go index fe7cd960c..06e9685d0 100644 --- a/pkg/collect/host_http.go +++ b/pkg/collect/host_http.go @@ -65,3 +65,7 @@ func (c *CollectHostHTTP) Collect(progressChan chan<- interface{}) (map[string][ return httpOutput, nil } + +func (c *CollectHostHTTP) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_httploadbalancer.go b/pkg/collect/host_httploadbalancer.go index de68055b8..5eebea06a 100644 --- a/pkg/collect/host_httploadbalancer.go +++ b/pkg/collect/host_httploadbalancer.go @@ -181,3 +181,7 @@ func attemptPOST(address string, request []byte, response []byte) NetworkStatus return NetworkStatusConnected } + +func (c *CollectHostHTTPLoadBalancer) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_ipv4interfaces.go b/pkg/collect/host_ipv4interfaces.go index 1fae0cec5..c2133dc4f 100644 --- a/pkg/collect/host_ipv4interfaces.go +++ b/pkg/collect/host_ipv4interfaces.go @@ -58,3 +58,7 @@ func (c *CollectHostIPV4Interfaces) Collect(progressChan chan<- interface{}) (ma HostIPV4InterfacesPath: b, }, nil } + +func (c *CollectHostIPV4Interfaces) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_journald.go b/pkg/collect/host_journald.go index c85caebd4..57dbbffa0 100644 --- a/pkg/collect/host_journald.go +++ b/pkg/collect/host_journald.go @@ -157,3 +157,7 @@ func getTimeout(timeout string) (time.Duration, error) { return time.ParseDuration(timeout) } + +func (c *CollectHostJournald) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_kernel_configs.go b/pkg/collect/host_kernel_configs.go index 8ab0dcd86..d671ce01b 100644 --- a/pkg/collect/host_kernel_configs.go +++ b/pkg/collect/host_kernel_configs.go @@ -140,3 +140,7 @@ func parseKConfigs(r io.Reader) (KConfigs, error) { } return configs, nil } + +func (c *CollectHostKernelConfigs) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_kernel_modules.go b/pkg/collect/host_kernel_modules.go index ce7925d8d..cb64ac351 100644 --- a/pkg/collect/host_kernel_modules.go +++ b/pkg/collect/host_kernel_modules.go @@ -190,3 +190,7 @@ func (l kernelModulesLoaded) collect() (map[string]KernelModuleInfo, error) { } return modules, nil } + +func (c *CollectHostKernelModules) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_memory.go b/pkg/collect/host_memory.go index 12b133f63..f7e4ad281 100644 --- a/pkg/collect/host_memory.go +++ b/pkg/collect/host_memory.go @@ -47,3 +47,7 @@ func (c *CollectHostMemory) Collect(progressChan chan<- interface{}) (map[string return output, nil } + +func (c *CollectHostMemory) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_os_info.go b/pkg/collect/host_os_info.go index f31f15140..7d1ed09d7 100644 --- a/pkg/collect/host_os_info.go +++ b/pkg/collect/host_os_info.go @@ -6,6 +6,7 @@ import ( "github.com/pkg/errors" troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" + "github.com/replicatedhq/troubleshoot/pkg/k8sutil" osutils "github.com/shirou/gopsutil/v3/host" ) @@ -52,3 +53,62 @@ func (c *CollectHostOS) Collect(progressChan chan<- interface{}) (map[string][]b return output, nil } + +func (c *CollectHostOS) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + restConfig, err := k8sutil.GetRESTConfig() + if err != nil { + return nil, errors.Wrap(err, "failed to convert kube flags to rest config") + } + + createOpts := CollectorRunOpts{ + KubernetesRestConfig: restConfig, + Image: "replicated/troubleshoot:latest", + Namespace: "default", + Timeout: defaultTimeout, + NamePrefix: "hostos-remote", + ProgressChan: progressChan, + } + + remoteCollector := &troubleshootv1beta2.RemoteCollector{ + Spec: troubleshootv1beta2.RemoteCollectorSpec{ + Collectors: []*troubleshootv1beta2.RemoteCollect{ + { + HostOS: &troubleshootv1beta2.RemoteHostOS{}, + }, + }, + }, + } + // empty redactor for now + additionalRedactors := &troubleshootv1beta2.Redactor{} + // re-use the collect.CollectRemote function to run the remote collector + results, err := CollectRemote(remoteCollector, additionalRedactors, createOpts) + if err != nil { + return nil, errors.Wrap(err, "failed to run remote collector") + } + + output := NewResult() + + // save the first result we find in the node and save it + for _, result := range results.AllCollectedData { + var nodeResult map[string]string + if err := json.Unmarshal(result, &nodeResult); err != nil { + return nil, errors.Wrap(err, "failed to marshal node results") + } + + for _, collectorResult := range nodeResult { + var collectedItems HostOSInfo + if err := json.Unmarshal([]byte(collectorResult), &collectedItems); err != nil { + return nil, errors.Wrap(err, "failed to marshal collector results") + } + + b, err := json.MarshalIndent(collectedItems, "", " ") + if err != nil { + return nil, errors.Wrap(err, "failed to marshal host os info") + } + output.SaveResult(c.BundlePath, HostOSInfoPath, bytes.NewBuffer(b)) + return output, nil + } + } + + return nil, errors.New("failed to find host os info") +} diff --git a/pkg/collect/host_run.go b/pkg/collect/host_run.go index ca754b5f1..7a2e8d37e 100644 --- a/pkg/collect/host_run.go +++ b/pkg/collect/host_run.go @@ -245,3 +245,7 @@ func (c *CollectHostRun) attemptToConvertCmdToAbsPath() string { return cmdAbsPath } + +func (c *CollectHostRun) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_services.go b/pkg/collect/host_services.go index effbea766..033f9067f 100644 --- a/pkg/collect/host_services.go +++ b/pkg/collect/host_services.go @@ -71,3 +71,7 @@ func (c *CollectHostServices) Collect(progressChan chan<- interface{}) (map[stri HostServicesPath: b, }, nil } + +func (c *CollectHostServices) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_subnetavailable.go b/pkg/collect/host_subnetavailable.go index a1457a185..2bd72c445 100644 --- a/pkg/collect/host_subnetavailable.go +++ b/pkg/collect/host_subnetavailable.go @@ -276,3 +276,7 @@ func netOverlaps(n1, n2 *net.IPNet) bool { return false } + +func (c *CollectHostSubnetAvailable) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_system_package.go b/pkg/collect/host_system_package.go index ef073a1a3..8897e2bf5 100644 --- a/pkg/collect/host_system_package.go +++ b/pkg/collect/host_system_package.go @@ -206,3 +206,7 @@ func matchMajorVersion(version string, major string) bool { } return false } + +func (c *CollectHostSystemPackages) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_tcp_connect.go b/pkg/collect/host_tcp_connect.go index eb3491cbc..eaa95b397 100644 --- a/pkg/collect/host_tcp_connect.go +++ b/pkg/collect/host_tcp_connect.go @@ -75,3 +75,7 @@ func attemptConnect(address string, timeout time.Duration) NetworkStatus { conn.Close() return NetworkStatusConnected } + +func (c *CollectHostTCPConnect) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_tcploadbalancer.go b/pkg/collect/host_tcploadbalancer.go index 033a73022..90c8ae281 100644 --- a/pkg/collect/host_tcploadbalancer.go +++ b/pkg/collect/host_tcploadbalancer.go @@ -76,3 +76,7 @@ func (c *CollectHostTCPLoadBalancer) Collect(progressChan chan<- interface{}) (m name: b, }, nil } + +func (c *CollectHostTCPLoadBalancer) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_tcpportstatus.go b/pkg/collect/host_tcpportstatus.go index 8c62b4841..54b6bd45f 100644 --- a/pkg/collect/host_tcpportstatus.go +++ b/pkg/collect/host_tcpportstatus.go @@ -118,3 +118,7 @@ func getLocalIPv4() (net.IP, error) { return nil, errors.New("No network interface has an IPv4 address") } + +func (c *CollectHostTCPPortStatus) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_time.go b/pkg/collect/host_time.go index 304d33f13..10c21545a 100644 --- a/pkg/collect/host_time.go +++ b/pkg/collect/host_time.go @@ -101,3 +101,7 @@ func (c *CollectHostTime) Collect(progressChan chan<- interface{}) (map[string][ HostTimePath: b, }, nil } + +func (c *CollectHostTime) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/host_udpportstatus.go b/pkg/collect/host_udpportstatus.go index f190fe41c..bb0535aee 100644 --- a/pkg/collect/host_udpportstatus.go +++ b/pkg/collect/host_udpportstatus.go @@ -76,3 +76,7 @@ func (c *CollectHostUDPPortStatus) Collect(progressChan chan<- interface{}) (map name: b, }, nil } + +func (c *CollectHostUDPPortStatus) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) { + return nil, ErrRemoteCollectorNotImplemented +} diff --git a/pkg/collect/remote_collector.go b/pkg/collect/remote_collector.go index 57570c15c..5b16537d1 100644 --- a/pkg/collect/remote_collector.go +++ b/pkg/collect/remote_collector.go @@ -32,6 +32,7 @@ type RemoteCollector struct { Namespace string BundlePath string Timeout time.Duration + NamePrefix string } type RemoteCollectors []*RemoteCollector @@ -98,7 +99,11 @@ func (c *RemoteCollector) RunCollectorSync(globalRedactors []*troubleshootv1beta return nil, errors.Wrap(err, "failed to get the list of nodes matching a nodeSelector") } - result, err := c.RunRemote(ctx, runner, nodes, hostCollector, names.SimpleNameGenerator, remoteCollectorNamePrefix) + if c.NamePrefix == "" { + c.NamePrefix = remoteCollectorNamePrefix + } + + result, err := c.RunRemote(ctx, runner, nodes, hostCollector, names.SimpleNameGenerator, c.NamePrefix) if err != nil { return nil, errors.Wrap(err, "failed to run collector remotely") } @@ -310,6 +315,13 @@ func (c *RemoteCollector) toHostCollector() (*troubleshootv1beta2.HostCollect, e Exclude: c.Collect.HostServices.Exclude, }, } + case c.Collect.HostOS != nil: + hostCollect.HostOS = &troubleshootv1beta2.HostOS{ + HostCollectorMeta: troubleshootv1beta2.HostCollectorMeta{ + CollectorName: c.Collect.HostOS.CollectorName, + Exclude: c.Collect.HostOS.Exclude, + }, + } default: return nil, errors.New("no spec found to run") } diff --git a/pkg/collect/runner.go b/pkg/collect/runner.go index 0d62d4add..40b0baa7f 100644 --- a/pkg/collect/runner.go +++ b/pkg/collect/runner.go @@ -18,6 +18,7 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/util/retry" "k8s.io/klog/v2" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) @@ -189,6 +190,9 @@ func createCollectorPod(ctx context.Context, client kubernetes.Interface, scheme Kind: "Pod", }, Spec: corev1.PodSpec{ + HostNetwork: true, + HostPID: true, + HostIPC: true, NodeSelector: nodeSelector, ServiceAccountName: serviceAccountName, RestartPolicy: corev1.RestartPolicyNever, @@ -197,11 +201,14 @@ func createCollectorPod(ctx context.Context, client kubernetes.Interface, scheme Image: imageName, ImagePullPolicy: imagePullPolicy, Name: runnerContainerName, - Command: []string{"collect"}, + Command: []string{"/bin/bash", "-c"}, Args: []string{ - "--collect-without-permissions", - "--format=raw", - "/troubleshoot/specs/collector.json", + `cp /troubleshoot/collect /host/collect && + cp /troubleshoot/specs/collector.json /host/collector.json && + chroot /host /bin/bash -c './collect --collect-without-permissions --format=raw collector.json'`, + }, + SecurityContext: &corev1.SecurityContext{ + Privileged: ptr.To(true), }, VolumeMounts: []corev1.VolumeMount{ { @@ -210,14 +217,8 @@ func createCollectorPod(ctx context.Context, client kubernetes.Interface, scheme ReadOnly: true, }, { - Name: "kernel-modules", - MountPath: "/lib/modules", - ReadOnly: true, - }, - { - Name: "ntp", - MountPath: "/run/dbus", - ReadOnly: true, + Name: "host-root", + MountPath: "/host", }, }, }, @@ -234,18 +235,10 @@ func createCollectorPod(ctx context.Context, client kubernetes.Interface, scheme }, }, { - Name: "kernel-modules", - VolumeSource: corev1.VolumeSource{ - HostPath: &corev1.HostPathVolumeSource{ - Path: "/lib/modules", - }, - }, - }, - { - Name: "ntp", + Name: "host-root", VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ - Path: "/run/dbus", + Path: "/", }, }, }, diff --git a/pkg/loader/loader_test.go b/pkg/loader/loader_test.go index 606db12d2..beb5feeec 100644 --- a/pkg/loader/loader_test.go +++ b/pkg/loader/loader_test.go @@ -25,13 +25,16 @@ func TestLoadingHelmTemplate_Succeeds(t *testing.T) { assert.Len(t, kinds.RemoteCollectorsV1Beta2, 0) require.Len(t, kinds.PreflightsV1Beta2, 2) require.Len(t, kinds.RedactorsV1Beta2, 1) - require.Len(t, kinds.SupportBundlesV1Beta2, 2) + require.Len(t, kinds.SupportBundlesV1Beta2, 3) // Assert a few fields from the loaded troubleshoot specs assert.Equal(t, "redactor-spec-1", kinds.RedactorsV1Beta2[0].ObjectMeta.Name) assert.Equal(t, "REDACT SECOND TEXT PLEASE", kinds.RedactorsV1Beta2[0].Spec.Redactors[0].Removals.Values[0]) - assert.Equal(t, "sb-spec-1", kinds.SupportBundlesV1Beta2[0].ObjectMeta.Name) - assert.Equal(t, "sb-spec-2", kinds.SupportBundlesV1Beta2[1].ObjectMeta.Name) + assert.Equal(t, "sb-spec-1", kinds.SupportBundlesV1Beta2[0].Metadata.Name) + assert.Equal(t, "sb-spec-2", kinds.SupportBundlesV1Beta2[1].Metadata.Name) + assert.Equal(t, "sb-spec-3", kinds.SupportBundlesV1Beta2[2].Metadata.Name) + assert.Equal(t, false, kinds.SupportBundlesV1Beta2[0].Metadata.RunHostCollectorsInPod) + assert.Equal(t, true, kinds.SupportBundlesV1Beta2[2].Metadata.RunHostCollectorsInPod) assert.Equal(t, "wg-easy", kinds.SupportBundlesV1Beta2[1].Spec.Collectors[0].Logs.CollectorName) assert.Equal(t, "Node Count Check", kinds.PreflightsV1Beta2[0].Spec.Analyzers[0].NodeResources.CheckName) assert.Len(t, kinds.PreflightsV1Beta2[0].Spec.Collectors, 0) @@ -345,8 +348,10 @@ func TestLoadingMultidocsWithTroubleshootSpecs(t *testing.T) { Kind: "SupportBundle", APIVersion: "troubleshoot.sh/v1beta2", }, - ObjectMeta: metav1.ObjectMeta{ - Name: "my-support-bundle", + Metadata: troubleshootv1beta2.SupportBundleMetadata{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-support-bundle", + }, }, Spec: troubleshootv1beta2.SupportBundleSpec{ Collectors: []*troubleshootv1beta2.Collect{ @@ -625,8 +630,10 @@ func TestLoadingEmptySpec(t *testing.T) { Kind: "SupportBundle", APIVersion: "troubleshoot.sh/v1beta2", }, - ObjectMeta: metav1.ObjectMeta{ - Name: "empty", + Metadata: troubleshootv1beta2.SupportBundleMetadata{ + ObjectMeta: metav1.ObjectMeta{ + Name: "empty", + }, }, }, }, diff --git a/pkg/supportbundle/collect.go b/pkg/supportbundle/collect.go index cfac2c75f..a839ca0d9 100644 --- a/pkg/supportbundle/collect.go +++ b/pkg/supportbundle/collect.go @@ -50,14 +50,38 @@ func runHostCollectors(ctx context.Context, hostCollectors []*troubleshootv1beta } opts.ProgressChan <- fmt.Sprintf("[%s] Running host collector...", collector.Title()) - result, err := collector.Collect(opts.ProgressChan) - if err != nil { - span.SetStatus(codes.Error, err.Error()) - opts.ProgressChan <- errors.Errorf("failed to run host collector: %s: %v", collector.Title(), err) - } - span.End() - for k, v := range result { - allCollectedData[k] = v + if opts.RunHostCollectorsInPod { + result, err := collector.RemoteCollect(opts.ProgressChan) + if err != nil { + // If the collector does not have a remote collector implementation, try to run it locally + if errors.Is(err, collect.ErrRemoteCollectorNotImplemented) { + result, err = collector.Collect(opts.ProgressChan) + if err != nil { + span.SetStatus(codes.Error, err.Error()) + opts.ProgressChan <- errors.Errorf("failed to run host collector: %s: %v", collector.Title(), err) + } + } else { + // If the collector has a remote collector implementation, but it failed to run, return the error + span.SetStatus(codes.Error, err.Error()) + opts.ProgressChan <- errors.Errorf("failed to run host collector: %s: %v", collector.Title(), err) + } + } + // If the collector has a remote collector implementation, and it ran successfully, return the result + span.End() + for k, v := range result { + allCollectedData[k] = v + } + } else { + // If the collector does not enable run host collectors in pod, run it locally + result, err := collector.Collect(opts.ProgressChan) + if err != nil { + span.SetStatus(codes.Error, err.Error()) + opts.ProgressChan <- errors.Errorf("failed to run host collector: %s: %v", collector.Title(), err) + } + span.End() + for k, v := range result { + allCollectedData[k] = v + } } } diff --git a/pkg/supportbundle/load.go b/pkg/supportbundle/load.go index bbbaf275f..6b61546cc 100644 --- a/pkg/supportbundle/load.go +++ b/pkg/supportbundle/load.go @@ -62,7 +62,9 @@ func ParseSupportBundle(doc []byte, followURI bool) (*troubleshootv1beta2.Suppor APIVersion: "troubleshoot.sh/v1beta2", Kind: "SupportBundle", }, - ObjectMeta: collector.ObjectMeta, + Metadata: troubleshootv1beta2.SupportBundleMetadata{ + ObjectMeta: collector.ObjectMeta, + }, Spec: troubleshootv1beta2.SupportBundleSpec{ Collectors: collector.Spec.Collectors, Analyzers: []*troubleshootv1beta2.Analyze{}, diff --git a/pkg/supportbundle/supportbundle.go b/pkg/supportbundle/supportbundle.go index 955e4be2e..53a430a79 100644 --- a/pkg/supportbundle/supportbundle.go +++ b/pkg/supportbundle/supportbundle.go @@ -37,6 +37,7 @@ type SupportBundleCreateOpts struct { OutputPath string Redact bool FromCLI bool + RunHostCollectorsInPod bool } type SupportBundleResponse struct { diff --git a/schemas/preflight-troubleshoot-v1beta2.json b/schemas/preflight-troubleshoot-v1beta2.json index 485311f4e..93e56b2f8 100644 --- a/schemas/preflight-troubleshoot-v1beta2.json +++ b/schemas/preflight-troubleshoot-v1beta2.json @@ -17160,6 +17160,17 @@ } } }, + "hostOS": { + "type": "object", + "properties": { + "collectorName": { + "type": "string" + }, + "exclude": { + "oneOf": [{"type": "string"},{"type": "boolean"}] + } + } + }, "hostServices": { "type": "object", "properties": { diff --git a/test/e2e/support-bundle/hostOS_remote_collector_e2e_test.go b/test/e2e/support-bundle/hostOS_remote_collector_e2e_test.go new file mode 100644 index 000000000..21e9788fc --- /dev/null +++ b/test/e2e/support-bundle/hostOS_remote_collector_e2e_test.go @@ -0,0 +1,67 @@ +package e2e + +import ( + "bytes" + "context" + "fmt" + "os" + "os/exec" + "testing" + + "golang.org/x/exp/slices" + "sigs.k8s.io/e2e-framework/pkg/envconf" + "sigs.k8s.io/e2e-framework/pkg/features" +) + +func TestHostOSRemoteCollector(t *testing.T) { + tests := []struct { + paths []string + expectType string + }{ + { + paths: []string{ + "hostos_info.json", + }, + expectType: "file", + }, + } + + feature := features.New("Host OS Remote Collector Test"). + Assess("check support bundle catch host os remote collector", func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context { + var out bytes.Buffer + supportbundleName := "host-os-remote-collector" + tarPath := fmt.Sprintf("%s.tar.gz", supportbundleName) + targetFolder := fmt.Sprintf("%s/host-collectors/system/", supportbundleName) + cmd := exec.CommandContext(ctx, sbBinary(), "spec/hostOSRemoteCollector.yaml", "--interactive=false", fmt.Sprintf("-o=%s", supportbundleName)) + cmd.Stdout = &out + err := cmd.Run() + if err != nil { + t.Fatal(err) + } + + defer func() { + err := os.Remove(fmt.Sprintf("%s.tar.gz", supportbundleName)) + if err != nil { + t.Fatal("Error remove file:", err) + } + }() + + files, _, err := readFilesAndFoldersFromTar(tarPath, targetFolder) + if err != nil { + t.Fatal(err) + } + + for _, test := range tests { + if test.expectType == "file" { + for _, path := range test.paths { + if !slices.Contains(files, path) { + t.Fatalf("Expected file %s not found", path) + } + } + } + } + + return ctx + }).Feature() + testenv.Test(t, feature) +} diff --git a/test/e2e/support-bundle/spec/hostOSRemoteCollector.yaml b/test/e2e/support-bundle/spec/hostOSRemoteCollector.yaml new file mode 100644 index 000000000..15c9ae807 --- /dev/null +++ b/test/e2e/support-bundle/spec/hostOSRemoteCollector.yaml @@ -0,0 +1,8 @@ +apiVersion: troubleshoot.sh/v1beta2 +kind: SupportBundle +metadata: + name: sb + runHostCollectorsInPod: true # default is false +spec: + hostCollectors: + - hostOS: {} diff --git a/testdata/yamldocs/helm-template.yaml b/testdata/yamldocs/helm-template.yaml index 711e7b2bf..56cb856bd 100644 --- a/testdata/yamldocs/helm-template.yaml +++ b/testdata/yamldocs/helm-template.yaml @@ -35,6 +35,25 @@ data: collectorName: wg-easy --- # Source: wg-easy/templates/replicated-library.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: sb-spec-3-configmap + labels: + troubleshoot.io/kind: support-bundle +data: + support-bundle-spec: |- + apiVersion: troubleshoot.sh/v1beta2 + kind: SupportBundle + metadata: + name: sb-spec-3 + runHostCollectorsInPod: true + spec: + collectors: + - logs: + collectorName: wg-easy +--- +# Source: wg-easy/templates/replicated-library.yaml kind: PersistentVolumeClaim apiVersion: v1 metadata: