Files
wehub-resource-sync 36b3af2e3d
PR Check / Code Quality: Format (push) Failing after 1s
PR Check / Code Quality: Lint (darwin) (push) Failing after 0s
PR Check / Code Quality: Lint (freebsd) (push) Failing after 1s
PR Check / Code Quality: Lint (windows) (push) Failing after 1s
PR Check / Code Quality: Lint (linux) (push) Failing after 1s
PR Check / Security: Vulnerability Scan (push) Failing after 0s
Update Documentation / update-docs (push) Failing after 2s
PR Check / Code Quality: Vendor (push) Failing after 1s
PR Check / Code Quality: Coverage (push) Failing after 0s
PR Check / Tests: Unit (macos-latest) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04-arm) (push) Has been cancelled
PR Check / Tests: Unit (windows-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:22:06 +08:00

140 lines
3.7 KiB
Go

package proc
import (
"context"
"encoding/json"
"os/exec"
"strings"
"time"
"github.com/pranshuparmar/witr/pkg/model"
)
func init() { registerRuntime(crictlRuntime{}) }
type crictlRuntime struct{}
func (crictlRuntime) Name() string { return "k8s" }
func (crictlRuntime) Available() bool { return binAvailable("crictl") }
func (crictlRuntime) List() []*model.ContainerMatch {
ctx, cancel := context.WithTimeout(context.Background(), runtimeQueryTimeout)
defer cancel()
out, err := exec.CommandContext(ctx, "crictl", "ps", "-o", "json").Output()
if err != nil {
return nil
}
var payload struct {
Containers []struct {
ID string `json:"id"`
Image struct{ Image string } `json:"image"`
ImageRef string `json:"imageRef"`
Metadata struct{ Name string } `json:"metadata"`
Labels map[string]string `json:"labels"`
State string `json:"state"`
CreatedAt string `json:"createdAt"`
} `json:"containers"`
}
if err := json.Unmarshal(out, &payload); err != nil {
return nil
}
var matches []*model.ContainerMatch
for _, c := range payload.Containers {
started, _ := time.Parse(time.RFC3339Nano, c.CreatedAt)
matches = append(matches, &model.ContainerMatch{
Runtime: "k8s",
ID: c.ID,
Name: c.Metadata.Name,
Image: c.Image.Image,
State: strings.TrimPrefix(c.State, "CONTAINER_"),
Status: strings.TrimPrefix(c.State, "CONTAINER_"),
StartedAt: started,
})
}
return matches
}
func (crictlRuntime) HostPID(id string) int {
info, _ := crictlInspect(id)
return info.Info.Pid
}
// Enrich populates Command, Mounts, and a more precise StartedAt by calling
// `crictl inspect` for the resolved container. Skips fields the inspect
// payload doesn't carry; partial enrichment is fine.
func (crictlRuntime) Enrich(match *model.ContainerMatch) {
payload, ok := crictlInspect(match.ID)
if !ok {
return
}
if args := payload.Info.RuntimeSpec.Process.Args; len(args) > 0 {
match.Command = strings.Join(args, " ")
}
if len(payload.Status.Mounts) > 0 {
var parts []string
for _, m := range payload.Status.Mounts {
entry := m.HostPath + " → " + m.ContainerPath
if m.Readonly {
entry += " (ro)"
}
parts = append(parts, entry)
}
match.Mounts = strings.Join(parts, ", ")
}
if payload.Status.StartedAt != "" {
if t, err := time.Parse(time.RFC3339Nano, payload.Status.StartedAt); err == nil {
match.StartedAt = t
}
}
}
type crictlInspectPayload struct {
Status struct {
StartedAt string `json:"startedAt"`
Mounts []struct {
ContainerPath string `json:"containerPath"`
HostPath string `json:"hostPath"`
Readonly bool `json:"readonly"`
} `json:"mounts"`
} `json:"status"`
Info struct {
Pid int `json:"pid"`
RuntimeSpec struct {
Process struct {
Args []string `json:"args"`
} `json:"process"`
} `json:"runtimeSpec"`
} `json:"info"`
}
func crictlInspect(id string) (crictlInspectPayload, bool) {
var p crictlInspectPayload
ctx, cancel := context.WithTimeout(context.Background(), runtimeQueryTimeout)
defer cancel()
out, err := exec.CommandContext(ctx, "crictl", "inspect", id).Output()
if err != nil {
return p, false
}
if err := json.Unmarshal(out, &p); err == nil {
return p, true
}
// Older crictl versions wrap the `info` field as a JSON-encoded string;
// unwrap and try again.
var wrapper struct {
Status json.RawMessage `json:"status"`
Info string `json:"info"`
}
if json.Unmarshal(out, &wrapper) != nil {
return p, false
}
if len(wrapper.Status) > 0 {
_ = json.Unmarshal(wrapper.Status, &p.Status)
}
if wrapper.Info != "" {
_ = json.Unmarshal([]byte(wrapper.Info), &p.Info)
}
return p, true
}