| 330 | } |
| 331 | |
| 332 | func resolveMemoryUsagePath(procMountsPath, procSelfCgroupPath, procMountInfoPath string) (string, cgroupLayout, error) { |
| 333 | layouts, err := detectCgroupLayouts(procMountsPath, procMountInfoPath) |
| 334 | if err != nil { |
| 335 | return "", cgroupLayout{}, err |
| 336 | } |
| 337 | |
| 338 | // Primary path: derive the container's cgroup from /proc/self/cgroup. |
| 339 | // This works on Linux (including Linux kind clusters) where cgroup namespace |
| 340 | // isolation is in effect and /proc/self/cgroup reports the container-relative |
| 341 | // path rather than "/". |
| 342 | for _, layout := range layouts { |
| 343 | candidate, err := resolveFromSelfCgroup(layout, procSelfCgroupPath) |
| 344 | if err == nil { |
| 345 | return candidate, layout, nil |
| 346 | } |
| 347 | } |
| 348 | |
| 349 | // Secondary path: walk every cgroup.procs file under the mount point and |
| 350 | // find the scope that actually owns this process (matched by its |
| 351 | // root-namespace PID read from NSpid in /proc/self/status). This is |
| 352 | // unambiguous — exactly one cgroup contains our PID — so we prefer it |
| 353 | // over identifier matching, which can fuzzy-match sibling container |
| 354 | // scopes when multiple pod cgroups are visible (kind, nested Docker). |
| 355 | var resolutionErrs []string |
| 356 | for _, layout := range layouts { |
| 357 | candidate, err := findCgroupByOwnPID(layout) |
| 358 | if err == nil { |
| 359 | return candidate, layout, nil |
| 360 | } |
| 361 | resolutionErrs = append(resolutionErrs, fmt.Sprintf("pid-walk: %v", err)) |
| 362 | } |
| 363 | |
| 364 | // Tertiary path: scan identifiers extracted from the environment, hostname, |
| 365 | // /proc/self/cgroup, and /proc/self/mountinfo, then walk the cgroup tree |
| 366 | // looking for a scope directory whose name contains one of those identifiers. |
| 367 | // Used only when PID-based resolution fails (e.g. environments where |
| 368 | // /proc/self/status NSpid is hidden or cgroup.procs is not readable). |
| 369 | identifiers, err := collectContainerIdentifiers(procSelfCgroupPath, procMountInfoPath) |
| 370 | if err != nil { |
| 371 | return "", cgroupLayout{}, err |
| 372 | } |
| 373 | for _, layout := range layouts { |
| 374 | candidate, err := findMemoryUsagePathByIdentifier(layout, identifiers) |
| 375 | if err == nil { |
| 376 | return candidate, layout, nil |
| 377 | } |
| 378 | resolutionErrs = append(resolutionErrs, err.Error()) |
| 379 | } |
| 380 | |
| 381 | return "", cgroupLayout{}, fmt.Errorf("no container-specific cgroup memory file found (%s)", strings.Join(resolutionErrs, "; ")) |
| 382 | } |
| 383 | |
| 384 | func detectCgroupLayouts(procMountsPath, procMountInfoPath string) ([]cgroupLayout, error) { |
| 385 | mountRoots, err := readMountRoots(procMountInfoPath) |