Skip to content

Commit 2d0d461

Browse files
committed
add support for nodes query
1 parent 49afbad commit 2d0d461

File tree

9 files changed

+1528
-0
lines changed

9 files changed

+1528
-0
lines changed

pkg/kubernetes/nodes.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package kubernetes
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
8+
v1 "k8s.io/api/core/v1"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
11+
"k8s.io/apimachinery/pkg/runtime"
12+
"k8s.io/apimachinery/pkg/runtime/schema"
13+
)
14+
15+
func (k *Kubernetes) ListNodes(ctx context.Context, options ResourceListOptions) (runtime.Unstructured, error) {
16+
return k.ResourcesList(ctx, &schema.GroupVersionKind{
17+
Group: "", Version: "v1", Kind: "Node",
18+
}, "", options)
19+
}
20+
21+
func (k *Kubernetes) GetNodeAddress(ctx context.Context, name string) ([]interface{}, error) {
22+
node, err := k.ResourcesGet(ctx, &schema.GroupVersionKind{
23+
Group: "", Version: "v1", Kind: "Node",
24+
}, "", name)
25+
if err != nil {
26+
return nil, err
27+
}
28+
29+
// Extract addresses from node status
30+
addresses, found, err := unstructured.NestedSlice(node.Object, "status", "addresses")
31+
if err != nil {
32+
return nil, err
33+
}
34+
if !found {
35+
return []interface{}{}, nil
36+
}
37+
38+
return addresses, nil
39+
}
40+
41+
func (k *Kubernetes) NodeCapacity(ctx context.Context, name string) (map[string]interface{}, error) {
42+
node, err := k.ResourcesGet(ctx, &schema.GroupVersionKind{
43+
Group: "", Version: "v1", Kind: "Node",
44+
}, "", name)
45+
if err != nil {
46+
return nil, err
47+
}
48+
49+
// Extract capacity from node status
50+
capacity, found, err := unstructured.NestedMap(node.Object, "status", "capacity")
51+
if err != nil {
52+
return nil, err
53+
}
54+
if !found {
55+
return map[string]interface{}{}, nil
56+
}
57+
58+
return capacity, nil
59+
}
60+
61+
func (k *Kubernetes) NodeLog(ctx context.Context, name string, logPath string, tail int64) (string, error) {
62+
// Use the node proxy API to access logs from the kubelet
63+
// Common log paths:
64+
// - /var/log/kubelet.log - kubelet logs
65+
// - /var/log/kube-proxy.log - kube-proxy logs
66+
// - /var/log/containers/ - container logs
67+
68+
if logPath == "" {
69+
logPath = "kubelet.log"
70+
}
71+
72+
// Build the URL for the node proxy logs endpoint
73+
url := []string{"api", "v1", "nodes", name, "proxy", "logs", logPath}
74+
75+
// Query parameters for tail
76+
params := make(map[string]string)
77+
if tail > 0 {
78+
params["tailLines"] = fmt.Sprintf("%d", tail)
79+
}
80+
81+
req := k.manager.discoveryClient.RESTClient().
82+
Get().
83+
AbsPath(url...)
84+
85+
// Add tail parameter if specified
86+
for key, value := range params {
87+
req.Param(key, value)
88+
}
89+
90+
result := req.Do(ctx)
91+
if result.Error() != nil {
92+
return "", fmt.Errorf("failed to get node logs: %w", result.Error())
93+
}
94+
95+
rawData, err := result.Raw()
96+
if err != nil {
97+
return "", fmt.Errorf("failed to read node log response: %w", err)
98+
}
99+
100+
return string(rawData), nil
101+
}
102+
103+
func (k *Kubernetes) NodeEvents(ctx context.Context, name string) ([]map[string]any, error) {
104+
var eventMap []map[string]any
105+
106+
// Use field selector to filter events for the specific node
107+
raw, err := k.ResourcesList(ctx, &schema.GroupVersionKind{
108+
Group: "", Version: "v1", Kind: "Event",
109+
}, "", ResourceListOptions{
110+
ListOptions: metav1.ListOptions{
111+
FieldSelector: fmt.Sprintf("involvedObject.kind=Node,involvedObject.name=%s", name),
112+
},
113+
})
114+
if err != nil {
115+
return eventMap, err
116+
}
117+
118+
unstructuredList := raw.(*unstructured.UnstructuredList)
119+
if len(unstructuredList.Items) == 0 {
120+
return eventMap, nil
121+
}
122+
123+
for _, item := range unstructuredList.Items {
124+
event := &v1.Event{}
125+
if err = runtime.DefaultUnstructuredConverter.FromUnstructured(item.Object, event); err != nil {
126+
return eventMap, err
127+
}
128+
129+
// Determine the most relevant timestamp
130+
timestamp := event.EventTime.Time
131+
if timestamp.IsZero() && event.Series != nil {
132+
timestamp = event.Series.LastObservedTime.Time
133+
} else if timestamp.IsZero() && event.Count > 1 {
134+
timestamp = event.LastTimestamp.Time
135+
} else if timestamp.IsZero() {
136+
timestamp = event.FirstTimestamp.Time
137+
}
138+
139+
eventMap = append(eventMap, map[string]any{
140+
"Timestamp": timestamp.String(),
141+
"Type": event.Type,
142+
"Reason": event.Reason,
143+
"Message": strings.TrimSpace(event.Message),
144+
"Count": event.Count,
145+
})
146+
}
147+
148+
return eventMap, nil
149+
}

0 commit comments

Comments
 (0)