mirror of https://github.com/vee1e/runtimeclass-debugger - Edge-node diagnostic tool for the KubeEdge RuntimeClass path
Find a file
lakshit verma 0c927117c5
ci: use golangci-lint-action v7 for golangci-lint v2
golangci-lint-action v6 rejects v2 version strings; v7 supports
golangci-lint v2.
2026-08-08 01:08:01 +05:30
.github/workflows ci: use golangci-lint-action v7 for golangci-lint v2 2026-08-08 01:08:01 +05:30
cmd/runtimeclass-debugger runtimeclass-debugger: edge-node diagnostic tool for the KubeEdge RuntimeClass path 2026-08-08 00:35:22 +05:30
pkg diag: gofmt the injected resolver fixtures 2026-08-08 01:04:50 +05:30
.gitignore runtimeclass-debugger: edge-node diagnostic tool for the KubeEdge RuntimeClass path 2026-08-08 00:35:22 +05:30
go.mod runtimeclass-debugger: edge-node diagnostic tool for the KubeEdge RuntimeClass path 2026-08-08 00:35:22 +05:30
go.sum runtimeclass-debugger: edge-node diagnostic tool for the KubeEdge RuntimeClass path 2026-08-08 00:35:22 +05:30
Makefile runtimeclass-debugger: edge-node diagnostic tool for the KubeEdge RuntimeClass path 2026-08-08 00:35:22 +05:30
README.md diag: correct the config key name in the events-check advice 2026-08-08 01:04:50 +05:30

runtimeclass-debugger

runtimeclass-debugger is an edge-node diagnostic tool for the KubeEdge RuntimeClass path. The tool does not implement RuntimeClass support. It finds which layer of the path is broken on a given edge node. You run it on the node, and it answers four questions in dependency order. The report names the layer that failed.

The project reference is https://github.com/kubeedge/kubeedge/issues/7106

The tool is built for the LFX mentorship program, CNCF Term 3 2026, KubeEdge project, "Enable RuntimeClass and Confidential Containers on KubeEdge": https://github.com/cncf/mentoring/blob/main/programs/lfx-mentorship/2026/03-Sep-Nov/README.md#kubeedge

Why it exists

Edged bridges only three API groups from the kubelet client to the edge-local metaclient. They are CoreV1, StorageV1, and CoordinationV1. The bridge code lives in kubeclientbridge. NodeV1 is the group where RuntimeClass lives. Edged does not bridge NodeV1. The kubelet RuntimeClass manager therefore reads from an empty fake store. The manager always returns NotFound.

The objects may still reach the edge. Containerd may still have the right handler. A pod that declares a runtime class still never starts. The failure looks like an infrastructure problem, not a KubeEdge bug. This tool walks the path top-down and shows where the gap is.

The four questions

  1. transport: do RuntimeClass objects exist on the edge at all?
  2. bridge: can Edged resolve a class name to a handler?
  3. cri: is the handler configured in the container runtime?
  4. events: would the user see the failure?

Install and build

The tool needs Go 1.23 or later and a C toolchain. The local store uses the mattn/go-sqlite3 driver.

make build creates the binary at bin/runtimeclass-debugger. You can also build it directly:

go build -o runtimeclass-debugger ./cmd/runtimeclass-debugger

Usage

runtimeclass-debugger diagnose [class-name]   # run all four checks, print report
runtimeclass-debugger check transport         # run only check 1
runtimeclass-debugger check bridge            # run only check 2
runtimeclass-debugger check cri               # run only check 3
runtimeclass-debugger check events            # run only check 4
runtimeclass-debugger version

diagnose takes an optional class name. When you give a class name, checks 2 and 3 use that class only. When you omit it, the tool uses the classes that MetaServer serves.

Flags

Flag Default Meaning
metaserver-address 127.0.0.1:10550 Address of the MetaServer. You may include a scheme.
containerd-config /etc/containerd/config.toml Path of the containerd config file.
edgecore-config /etc/kubeedge/config/edgecore.yaml Path of the edgecore config file.
db-file /var/lib/kubeedge/edgecore.db Path of the KubeEdge local SQLite store.
cert-file Client certificate for MetaServer auth. Optional.
key-file Client key for MetaServer auth. Optional.
ca-file CA certificate for MetaServer auth. Optional.
timeout 5s HTTP timeout for MetaServer queries.
output table Output format: table or json. The default is table.

Example

$ runtimeclass-debugger diagnose kata
RuntimeClass Edge Diagnostics
=============================
[PASS]  transport  1 RuntimeClass object(s) served by MetaServer (kata)
[FAIL]  bridge     class "kata" is in the local store but the kubelet-bridge lookup cannot resolve it (NotFound): edge bridge gap (NodeV1 not wired to metaclient)
[PASS]  cri        handler "kata" configured in containerd
[WARN]  events     reportEvent is false: pod failures may not surface as events. Enable edged.reportEvent

Result: 2 pass, 1 fail, 1 warn

Pass --output json for machine-readable output. The exit code works in scripts and CI. A zero exit means all checks passed. One means at least one check failed. Two means warnings only. Three means the tool failed to run.

The four checks

1. transport: do RuntimeClass objects exist on the edge at all?

The tool sends a request to the local MetaServer at 127.0.0.1:10550. The request asks for RuntimeClass objects. MetaServer returns at least one object, and the check passes. MetaServer returns an empty list, and the check fails. The reason is a transport gap. The sync from cloud to edge has not reached the node. Pull request 7141 covers this sync. MetaServer is not reachable, and the check warns. MetaServer should always listen on an edge node.

2. bridge: can Edged resolve a class name to a handler?

The tool reproduces the path that Edged uses to resolve a class name. It runs two lookups. The naive lookup uses a client built the same way as kubeclientbridge. NodeV1 reads from an empty fake store. This is the current kubelet behavior. The lookup always returns NotFound. The wired lookup uses the classes from the local SQLite store. It shows what the kubelet would see once NodeV1 connects to the metaclient. The report shows both outcomes. You see the gap with evidence instead of an assumption.

The resolution rules match upstream kubelet. No class name gives an empty handler, the default runtime. A known class gives its handler string. An unknown class gives NotFound.

3. cri: is the handler configured in the runtime?

The tool parses the containerd config file. It looks for CRI plugin runtime entries under the containerd runtimes section. It also accepts the legacy plugins.cri spelling. Every class handler from check 1 must have a matching entry. Kata uses handler names such as kata, kata-clh, kata-qemu, and kata-qemu-tdx. A handler with no entry keeps the pod Pending. The error is FailedCreatePodSandBox.

4. events: would the user see the failure?

The tool reads the value of edged.reportEvent from the edgecore config file. The default path is /etc/kubeedge/config/edgecore.yaml. When reportEvent is false, which is the default, edged creates no event client. Events never leave the node. A pod stuck in Pending may show no event at all. You would not see the failure.

Reading the report

The tool exists to expose two root causes.

  • Objects never reach the edge. This is the transport gap. Pull request 7141 fixes this part.
  • NodeV1 is not wired to the metaclient. This is the bridge gap. kubeclientbridge does not bridge NodeV1.

When the transport and bridge checks both pass, check 3 confirms the runtime side. Check 4 confirms that you would see the failure. A failed bridge with a passed cri shows the classic KubeEdge symptom. The handler is ready. The class never resolves.

Development

make fmt checks the formatting. make build builds the tool. make vet runs go vet. make lint runs golangci-lint. make test runs the unit tests.

The tool reads local state only, it never writes to the node. It opens the local SQLite store read-only. It tolerates both store schemas: the meta_v2 table and the legacy meta table.