Skip to content

Commit c1c4b61

Browse files
authored
feat: [sc-113128] Create node list file before running remote host collector (#1632)
* create node list
1 parent d60f9a6 commit c1c4b61

File tree

6 files changed

+106
-28
lines changed

6 files changed

+106
-28
lines changed

pkg/analyze/host_os_info.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/pkg/errors"
1111
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
1212
"github.com/replicatedhq/troubleshoot/pkg/collect"
13+
"github.com/replicatedhq/troubleshoot/pkg/constants"
1314
)
1415

1516
type AnalyzeHostOS struct {
@@ -39,8 +40,8 @@ func (a *AnalyzeHostOS) Analyze(
3940
// check if the host os info file exists (local mode)
4041
contents, err := getCollectedFileContents(collect.HostOSInfoPath)
4142
if err != nil {
42-
//check if the node list file exists (remote mode)
43-
contents, err := getCollectedFileContents(collect.NODE_LIST_FILE)
43+
// check if the node list file exists (remote mode)
44+
contents, err := getCollectedFileContents(constants.NODE_LIST_FILE)
4445
if err != nil {
4546
return []*AnalyzeResult{&result}, errors.Wrap(err, "failed to get collected file")
4647
}

pkg/collect/collect.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import (
1212
"k8s.io/client-go/rest"
1313
)
1414

15-
const NODE_LIST_FILE = "host-collectors/system/node_list.json"
16-
1715
var (
1816
// ErrCollectorNotFound is returned when an undefined host collector is
1917
// specified by the user.

pkg/collect/host_os_info.go

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"bytes"
55
"encoding/json"
66
"fmt"
7-
"os"
87

98
"github.com/pkg/errors"
109
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
@@ -95,7 +94,6 @@ func (c *CollectHostOS) RemoteCollect(progressChan chan<- interface{}) (map[stri
9594
}
9695

9796
output := NewResult()
98-
nodes := []string{}
9997

10098
// save the first result we find in the node and save it
10199
for node, result := range results.AllCollectedData {
@@ -114,20 +112,9 @@ func (c *CollectHostOS) RemoteCollect(progressChan chan<- interface{}) (map[stri
114112
if err != nil {
115113
return nil, errors.Wrap(err, "failed to marshal host os info")
116114
}
117-
nodes = append(nodes, node)
118115
output.SaveResult(c.BundlePath, fmt.Sprintf("host-collectors/system/%s/%s", node, HostInfoFileName), bytes.NewBuffer(b))
119116
}
120117
}
121118

122-
// check if NODE_LIST_FILE exists
123-
_, err = os.Stat(NODE_LIST_FILE)
124-
// if it not exists, save the nodes list
125-
if err != nil {
126-
nodesBytes, err := json.MarshalIndent(HostOSInfoNodes{Nodes: nodes}, "", " ")
127-
if err != nil {
128-
return nil, errors.Wrap(err, "failed to marshal host os info nodes")
129-
}
130-
output.SaveResult(c.BundlePath, NODE_LIST_FILE, bytes.NewBuffer(nodesBytes))
131-
}
132119
return output, nil
133120
}

pkg/constants/constants.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,7 @@ const (
109109
OUTCOME_PASS = "pass"
110110
OUTCOME_WARN = "warn"
111111
OUTCOME_FAIL = "fail"
112+
113+
// List of remote nodes to collect data from in a support bundle
114+
NODE_LIST_FILE = "host-collectors/system/node_list.json"
112115
)

pkg/supportbundle/supportbundle.go

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package supportbundle
33
import (
44
"bytes"
55
"context"
6+
"encoding/json"
67
"fmt"
78
"net/http"
89
"os"
@@ -22,6 +23,8 @@ import (
2223
"github.com/replicatedhq/troubleshoot/pkg/convert"
2324
"github.com/replicatedhq/troubleshoot/pkg/version"
2425
"go.opentelemetry.io/otel"
26+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+
"k8s.io/client-go/kubernetes"
2528
"k8s.io/client-go/rest"
2629
"k8s.io/klog/v2"
2730
)
@@ -46,6 +49,11 @@ type SupportBundleResponse struct {
4649
FileUploaded bool
4750
}
4851

52+
// NodeList is a list of remote nodes to collect data from in a support bundle
53+
type NodeList struct {
54+
Nodes []string `json:"nodes"`
55+
}
56+
4957
// CollectSupportBundleFromSpec collects support bundle from start to finish, including running
5058
// collectors, analyzers and after collection steps. Input arguments are specifications.
5159
// if FromCLI option is set to true, the output is the name of the archive on disk in the cwd.
@@ -98,7 +106,7 @@ func CollectSupportBundleFromSpec(
98106
return nil, errors.Wrap(err, "create bundle dir")
99107
}
100108

101-
var result, files, hostFiles collect.CollectorResult
109+
result := make(collect.CollectorResult)
102110

103111
ctx, root := otel.Tracer(constants.LIB_TRACER_NAME).Start(
104112
context.Background(), constants.TROUBLESHOOT_ROOT_SPAN_NAME,
@@ -110,10 +118,32 @@ func CollectSupportBundleFromSpec(
110118
root.End()
111119
}()
112120

121+
// only create a node list if we are running host collectors in a pod
122+
if opts.RunHostCollectorsInPod {
123+
clientset, err := kubernetes.NewForConfig(opts.KubernetesRestConfig)
124+
if err != nil {
125+
return nil, errors.Wrap(err, "failed to create kubernetes clientset to run host collectors in pod")
126+
}
127+
nodeList, err := getNodeList(clientset, opts)
128+
if err != nil {
129+
return nil, errors.Wrap(err, "failed to get remote node list")
130+
}
131+
nodeListBytes, err := json.MarshalIndent(nodeList, "", " ")
132+
if err != nil {
133+
return nil, errors.Wrap(err, "failed to marshal remote node list")
134+
}
135+
err = result.SaveResult(bundlePath, constants.NODE_LIST_FILE, bytes.NewBuffer(nodeListBytes))
136+
if err != nil {
137+
return nil, errors.Wrap(err, "failed to write remote node list")
138+
}
139+
}
140+
113141
// Cache error returned by collectors and return it at the end of the function
114142
// so as to have a chance to run analyzers and archive the support bundle after.
115143
// If both host and in cluster collectors fail, the errors will be wrapped
116144
collectorsErrs := []string{}
145+
var files, hostFiles collect.CollectorResult
146+
117147
if spec.HostCollectors != nil {
118148
// Run host collectors
119149
hostFiles, err = runHostCollectors(ctx, spec.HostCollectors, additionalRedactors, bundlePath, opts)
@@ -130,16 +160,16 @@ func CollectSupportBundleFromSpec(
130160
}
131161
}
132162

133-
if files != nil && hostFiles != nil {
134-
result = files
135-
for k, v := range hostFiles {
136-
result[k] = v
137-
}
138-
} else if files != nil {
139-
result = files
140-
} else if hostFiles != nil {
141-
result = hostFiles
142-
} else {
163+
// merge in-cluster and host collectors results
164+
for k, v := range files {
165+
result[k] = v
166+
}
167+
168+
for k, v := range hostFiles {
169+
result[k] = v
170+
}
171+
172+
if len(result) == 0 {
143173
if len(collectorsErrs) > 0 {
144174
return nil, fmt.Errorf("failed to generate support bundle: %s", strings.Join(collectorsErrs, "\n"))
145175
}
@@ -288,3 +318,18 @@ func ConcatSpec(target *troubleshootv1beta2.SupportBundle, source *troubleshootv
288318
}
289319
return newBundle
290320
}
321+
322+
func getNodeList(clientset kubernetes.Interface, opts SupportBundleCreateOpts) (*NodeList, error) {
323+
// todo: any node filtering on opts?
324+
nodes, err := clientset.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{})
325+
if err != nil {
326+
return nil, errors.Wrap(err, "failed to list nodes")
327+
}
328+
329+
nodeList := NodeList{}
330+
for _, node := range nodes.Items {
331+
nodeList.Nodes = append(nodeList.Nodes, node.Name)
332+
}
333+
334+
return &nodeList, nil
335+
}

pkg/supportbundle/supportbundle_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import (
55
"testing"
66

77
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
8+
corev1 "k8s.io/api/core/v1"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
"k8s.io/client-go/kubernetes"
11+
testclient "k8s.io/client-go/kubernetes/fake"
812
)
913

1014
func Test_LoadAndConcatSpec(t *testing.T) {
@@ -70,3 +74,43 @@ func Test_LoadAndConcatSpec_WithNil(t *testing.T) {
7074
t.Error("concatenating sourceBundle of nil pointer has error.")
7175
}
7276
}
77+
78+
func Test_getNodeList(t *testing.T) {
79+
tests := []struct {
80+
name string
81+
clientset kubernetes.Interface
82+
opts SupportBundleCreateOpts
83+
expected *NodeList
84+
expectError bool
85+
}{
86+
{
87+
name: "successful node list",
88+
clientset: testclient.NewSimpleClientset(
89+
&corev1.NodeList{
90+
Items: []corev1.Node{
91+
{ObjectMeta: metav1.ObjectMeta{Name: "node1"}},
92+
{ObjectMeta: metav1.ObjectMeta{Name: "node2"}},
93+
},
94+
},
95+
),
96+
opts: SupportBundleCreateOpts{},
97+
expected: &NodeList{
98+
Nodes: []string{"node1", "node2"},
99+
},
100+
expectError: false,
101+
},
102+
}
103+
104+
for _, tt := range tests {
105+
t.Run(tt.name, func(t *testing.T) {
106+
nodeList, err := getNodeList(tt.clientset, tt.opts)
107+
if (err != nil) != tt.expectError {
108+
t.Errorf("getNodeList() error = %v, expectError %v", err, tt.expectError)
109+
return
110+
}
111+
if !reflect.DeepEqual(nodeList, tt.expected) {
112+
t.Errorf("getNodeList() = %v, expected %v", nodeList, tt.expected)
113+
}
114+
})
115+
}
116+
}

0 commit comments

Comments
 (0)