Rewrite/resource verification - #336
Open
Tanker2020 wants to merge 31 commits into
Open
Conversation
- Node: name, optional NodeFunc, directed edges to upstream deps - Edge: carries optional EdgeFunc to gate dependent start - ResourceNode: embeds Node, adds Manifest/VerifyFunc/DeployMethod - Graph: root-anchored container; AddNode, AddDependency, Topology - Cycle detection on every AddChild call (DFS reachability) - Topology() returns DFS post-order (dependency-first deploy order) Files: rewrite/dag/node.go, rewrite/dag/graph.go Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
- CompletionState: Verified/Unverified/Failed/Unstarted node buckets
- DeployCompleted(), VerifyCompleted(), AnyFailed() predicates
- HaltError{Fatal bool}: returned by NodeFunc to signal runner halt
Fatal=true → node lands in Failed, downstreams become Unstarted
Fatal=false → node lands in Unverified (deployed, not yet ready)
Files: rewrite/dag/completion_state.go
Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
Python used ThreadPoolExecutor + time.sleep(0.05) busy-poll loop. Go port uses goroutines + buffered results channel; scheduler blocks on select — zero CPU busy-polling. - NewRunner(graph, opts...) with functional options - WithConcurrency(0): serial topology walk, no goroutines (dry-run/test) - WithConcurrency(n): semaphore-capped parallel execution - WithVerifyUpstream(bool): gate dependent start on EdgeFunc result - context.Context cancellation: drains in-flight, marks rest Unstarted - Independent graph branches continue executing after sibling failure (matches Python oper8 intended behaviour; Python had a bug where the serial loop broke early on fatalErr) - stateMap protected by sync.Mutex; scheduler is single writer Files: rewrite/dag/runner.go Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
23 test cases covering:
Graph/Node: empty graph, duplicate node, empty name, cycle detection,
self-loop, topology order, String()
Runner serial: all succeed, empty graph, fatal halt (independent branch
still runs), unverified halt, disabled node, execution order
Runner concurrent: all succeed, fatal halt, independent nodes verified
parallel via start-time spread, race detector stress test
(20 nodes, atomic counter), context cancellation
EdgeFunc: blocks dependent when returns false, allows when true
CompletionState: all predicate combinations
ResourceNode: construction and field access
Concurrency test uses start-time recording rather than wall-clock
total elapsed — CI-safe on slow runners.
Files: rewrite/dag/runner_test.go
Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
- Matrix: Go 1.22 and 1.23 - go test -race -count=1 -timeout=60s ./dag/... - go build ./... and go vet ./dag/... - golangci-lint on dag/ package - Triggered on push to rewrite/DAG_Runner and PRs targeting main - working-directory: rewrite (module root) Files: ./.github/workflows/pr1-dag-runner.yml Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
Defines the core abstraction all cluster interactions go through. Python (bool, bool) return tuples → Go (changed bool, err error): - success bool dropped; errors are returned as error values - callers use idiomatic `if err != nil` instead of checking two booleans watch_objects Python generator → Go channel: - Watch() returns <-chan WatchEvent; caller ranges over it - Cancelled via context.Context; channel is closed on cancel New types vs Python: - ListOptions struct (replaces positional label_selector/field_selector args) - EventType string constants (ADDED/MODIFIED/DELETED) - WatchEvent struct with Timestamp Files: rewrite/deploymanager/deploymanager.go Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
Ports deploy_manager/owner_references.py. - OwnerRef(ownerCR) builds a single ownerReference map entry - ApplyOwnerRef(owner, child) stamps the reference onto child.metadata - No-op when owner == child (same UID) - No-op for cross-namespace references (K8s does not support them) - Idempotent: will not add duplicate entries - blockOwnerDeletion: true; controller field intentionally omitted (matches Python behaviour and StackOverflow rationale in source) Files: rewrite/deploymanager/ownerref.go Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
Ports deploy_manager/dry_run_deploy_manager.py. Primary use: unit-testing controllers without a live cluster. Key differences from Python: - Python used nested defaultdict; Go uses typed clusterStore (map[ns][kind][apiVersion][name] → object) - Python RLock on class level; Go sync.RWMutex per instance - Python watch callbacks were registered functions; Go uses buffered channels — consumers range over the channel, cancel via context - Watch channel is closed when ctx is cancelled (no explicit Unregister) - deepCopy via JSON marshal/unmarshal (simple, correct for map[string]any) - matchSelector implements = == != existence operators (sufficient for dry-run tests; full set-based selector is future work) Extra test helpers (not in Python): - GetStored(ns, kind, av, name) — direct store access for assertions - ObjectCount() — total objects in store Files: rewrite/deploymanager/dryrun.go Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
19 test cases covering:
Deploy: create, idempotent re-deploy, field update, owner ref stamping
Get: not found returns nil, found returns deep copy (mutation check)
Delete: existing object, non-existent no-op
List: all objects, label selector filtering
SetStatus: sets status, returns changed=true; error on missing object
Watch: receives ADDED on deploy, DELETED on delete, channel closes
on context cancel (race-detector safe)
OwnerRef: stamps reference, idempotent, cross-namespace skipped
Files: rewrite/deploymanager/dryrun_test.go
Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
- Matrix: Go 1.22 and 1.23 - go test -race -count=1 -timeout=60s ./deploymanager/... - go build ./... and go vet ./deploymanager/... - golangci-lint on deploymanager/ package - Triggered on push to rewrite/Deploy_Manager and PRs targeting main Files: .github/workflows/pr2-deploy-manager.yml Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
…licationStatus Ports oper8 Python status.py to Go. Reason types: ReadyReason, UpdatingReason, ServiceStatus string constants. MakeApplicationStatus(Options) builds a complete status map: - Ready + Updating conditions from reason/message pairs - External conditions preserved alongside oper8-managed ones - ComponentStatus block from dag.CompletionState (sorted node names) - versions.reconciled / versions.available.versions (IBM CloudPak paths) - <kind>Status field (e.g. customerStatus) when Kind is set UpdateApplicationStatus merges new Options onto existing status, carrying forward current reasons and external conditions when not overridden. GetCondition, GetVersion, StatusChanged helper functions included. Python translation notes: - deepdiff library dropped; StatusChanged uses recursive JSON comparison after stripping lastTransactionTime keys — zero external dependencies - **kwargs replaced by Options struct (compile-time field checking) - aconfig nested_set/nested_get replaced by nestedSet/nestedGet dot-path helpers Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
MakeApplicationStatus: Ready/Updating condition status values for all reason combinations, empty options, external conditions, version fields, componentStatus deployed/verified counts and dependencyGraph, IBM CloudPak <kind>Status (Completed/Failed/InProgress/custom preserved) UpdateApplicationStatus: preserves existing reasons when not overridden, overrides when provided, preserves external conditions and top-level fields StatusChanged: same content + different timestamps not changed, different reason changed, nil inputs, added field GetCondition, GetVersion: found/missing cases UpdatingReason active/inactive matrix (all 6 reasons) ComponentStatus node names sorted alphabetically Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
Matrix Go 1.22 and 1.23, race detector, golangci-lint v1.64.8 Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
…load config Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
…ondition Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
…rity tests Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
Tanker2020
force-pushed
the
rewrite/Resource_Verification
branch
from
August 11, 2026 08:11
56f1901 to
f643b60
Compare
…r, ReconcileManager
Ports oper8 Python session.py / component.py / controller.py /
rollout_manager.py / reconcile.py to Go.
New packages:
rewrite/session/ per-reconcile context (CR, DAG, status fetch)
rewrite/component/ Component interface — Name/Setup/Deploy/Verify
rewrite/controller/ Controller interface + BaseController no-op embed
rewrite/rolloutmanager/ 4-phase loop: deploy→after_deploy→verify→after_verify
rewrite/reconcilemanager/ top-level orchestrator: ID gen, session init,
preconditions, rollout, status writes, finalizers
dag/node.go: add Node.SetFunc / SetData / Data (no unused fields)
gofmt: dag/runner.go, dag/runner_test.go, deploymanager/dryrun.go
reconcilemanager tests (9 cases, -race clean):
EmptyGraph, SingleComponentVerified, SetupError, DeployError,
VerifyNotReady, Precondition, TwoComponentsOrdered, InvalidCR, Finalizer
CI: .github/workflows/pr5-reconcile.yml
Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
…r, ReconcileManager
Ports oper8 Python session.py / component.py / controller.py /
rollout_manager.py / reconcile.py to Go.
New packages:
rewrite/session/ per-reconcile context (CR, DAG, status fetch)
rewrite/component/ Component interface — Name/Setup/Deploy/Verify
rewrite/controller/ Controller interface + BaseController no-op embed
rewrite/rolloutmanager/ 4-phase loop: deploy→after_deploy→verify→after_verify
rewrite/reconcilemanager/ top-level orchestrator: ID gen, session init,
preconditions, rollout, status writes, finalizers
dag/node.go: add Node.SetFunc / SetData / Data (no unused fields — fixes
previous lint failure for unused nishanthk ttys002 Tue Aug 11 12:37 - 12:37 (00:00)
nishanthk ttys002 Thu Jul 30 11:30 - 11:30 (00:00)
nishanthk ttys002 Thu Jul 30 11:30 - 11:30 (00:00)
nishanthk ttys002 Thu Jul 30 11:30 - 11:30 (00:00)
nishanthk ttys002 Thu Jul 30 11:30 - 11:30 (00:00)
nishanthk ttys002 Thu Jul 30 11:29 - 11:29 (00:00)
nishanthk ttys002 Thu Jul 30 11:29 - 11:29 (00:00)
nishanthk ttys002 Thu Jul 30 11:28 - 11:28 (00:00)
nishanthk ttys002 Thu Jul 30 11:23 - 11:23 (00:00)
nishanthk ttys001 Thu Jul 30 11:19 still logged in
nishanthk ttys000 Thu Jul 30 11:19 still logged in
nishanthk console Thu Jul 30 11:19 still logged in
reboot time Thu Jul 30 11:00
shutdown time Thu Jul 30 10:59
nishanthk ttys001 Fri Jul 24 15:26 - 15:26 (00:00)
nishanthk ttys000 Fri Jul 24 15:26 - 15:26 (00:00)
nishanthk console Fri Jul 24 15:26 - 10:59 (5+19:33)
reboot time Fri Jul 24 15:25
shutdown time Fri Jul 24 15:24
nishanthk ttys001 Wed Jul 22 00:37 - 00:37 (00:00)
nishanthk ttys000 Wed Jul 22 00:37 - 00:37 (00:00)
nishanthk console Wed Jul 22 00:37 - 15:24 (2+14:47)
reboot time Wed Jul 22 00:35
shutdown time Wed Jul 22 00:31
root console Wed Jul 22 00:30 - shutdown (00:00)
nishanthk ttys001 Fri Jul 17 16:28 - 16:28 (00:00)
nishanthk ttys001 Thu Jul 9 13:25 - 13:25 (00:00)
nishanthk ttys001 Mon Jul 6 14:14 - 14:14 (00:00)
nishanthk ttys005 Mon Jul 6 14:14 - 14:14 (00:00)
nishanthk ttys006 Mon Jul 6 14:14 - 14:14 (00:00)
nishanthk ttys003 Mon Jun 29 14:47 - 14:47 (00:00)
nishanthk ttys002 Mon Jun 29 14:47 - 14:47 (00:00)
nishanthk ttys001 Mon Jun 29 14:47 - 14:47 (00:00)
nishanthk ttys006 Mon Jun 29 14:21 - 14:21 (00:00)
nishanthk ttys006 Mon Jun 29 14:21 - 14:21 (00:00)
nishanthk ttys003 Mon Jun 29 13:36 - 13:36 (00:00)
nishanthk ttys005 Mon Jun 22 02:32 - 02:32 (00:00)
nishanthk ttys004 Mon Jun 22 02:30 - 02:30 (00:00)
nishanthk ttys003 Mon Jun 22 01:16 - 01:16 (00:00)
nishanthk ttys002 Thu Jun 18 16:23 - 16:23 (00:00)
nishanthk ttys001 Thu Jun 18 16:23 - 16:23 (00:00)
nishanthk ttys000 Thu Jun 18 16:23 - 16:23 (00:00)
nishanthk console Thu Jun 18 16:23 - 00:30 (33+08:07)
reboot time Thu Jun 18 16:23
nishanthk ttys002 Wed Jun 17 12:32 - crash (1+03:50)
nishanthk ttys001 Mon Jun 15 13:30 - crash (3+02:52)
nishanthk ttys000 Mon Jun 15 13:30 - crash (3+02:53)
nishanthk ttys003 Mon Jun 15 13:17 - 13:17 (00:00)
nishanthk ttys003 Mon Jun 15 13:17 - 13:17 (00:00)
nishanthk ttys002 Mon Jun 15 13:16 - 13:16 (00:00)
nishanthk ttys003 Mon Jun 15 13:14 - 13:14 (00:00)
nishanthk ttys002 Mon Jun 15 13:12 - 13:12 (00:00)
nishanthk ttys002 Mon Jun 15 13:11 - 13:11 (00:00)
nishanthk ttys003 Mon Jun 15 13:07 - 13:07 (00:00)
nishanthk ttys002 Fri Jun 12 13:02 - 13:02 (00:00)
nishanthk ttys002 Thu Jun 11 12:44 - 12:44 (00:00)
nishanthk ttys004 Mon Jun 8 13:56 - 13:56 (00:00)
nishanthk ttys002 Mon Jun 8 13:56 - 13:56 (00:00)
nishanthk ttys003 Mon Jun 8 13:56 - 13:56 (00:00)
nishanthk ttys004 Fri Jun 5 09:34 - 09:34 (00:00)
nishanthk ttys002 Fri Jun 5 09:34 - 09:34 (00:00)
nishanthk ttys003 Fri Jun 5 09:34 - 09:34 (00:00)
nishanthk ttys020 Thu Jun 4 11:20 - 11:20 (00:00)
nishanthk ttys004 Wed Jun 3 16:42 - 16:42 (00:00)
nishanthk ttys003 Wed Jun 3 00:06 - 00:06 (00:00)
nishanthk ttys002 Wed Jun 3 00:06 - 00:06 (00:00)
nishanthk ttys001 Wed Jun 3 00:06 - 00:06 (00:00)
nishanthk ttys000 Wed Jun 3 00:06 - 00:06 (00:00)
nishanthk console Wed Jun 3 00:06 - crash (15+16:17)
reboot time Wed Jun 3 00:04
shutdown time Tue Jun 2 23:59
root console Tue Jun 2 23:57 - shutdown (00:02)
nishanthk ttys003 Mon Jun 1 21:50 - 21:50 (00:00)
nishanthk ttys002 Mon Jun 1 21:50 - 21:50 (00:00)
nishanthk ttys001 Mon Jun 1 21:50 - 21:50 (00:00)
nishanthk ttys000 Mon Jun 1 21:50 - 21:50 (00:00)
nishanthk console Mon Jun 1 21:50 - 23:57 (1+02:07)
reboot time Mon Jun 1 21:49
shutdown time Mon Jun 1 21:49
root console Mon Jun 1 21:49 - shutdown (00:00)
nishanthk ttys003 Mon Jun 1 10:57 - 10:57 (00:00)
nishanthk ttys009 Mon Jun 1 10:21 - 10:21 (00:00)
nishanthk ttys000 Mon Jun 1 10:15 - 10:15 (00:00)
nishanthk ttys004 Fri May 29 15:11 - 15:11 (00:00)
nishanthk ttys003 Fri May 29 12:56 - 12:56 (00:00)
nishanthk ttys002 Wed May 27 16:07 - 16:07 (00:00)
nishanthk ttys001 Wed May 27 16:05 - 16:05 (00:00)
nishanthk ttys001 Wed May 27 11:31 - 11:32 (00:00)
nishanthk ttys001 Wed May 27 11:31 - 11:31 (00:00)
nishanthk ttys002 Tue May 26 17:36 - 17:36 (00:00)
nishanthk ttys001 Tue May 26 17:24 - 17:24 (00:00)
nishanthk ttys001 Tue May 26 15:48 - 15:48 (00:00)
nishanthk ttys001 Tue May 26 15:47 - 15:47 (00:00)
nishanthk ttys000 Tue May 26 15:46 - 15:46 (00:00)
nishanthk ttys001 Tue May 26 15:42 - 15:42 (00:00)
nishanthk ttys001 Tue May 26 15:31 - 15:31 (00:00)
nishanthk ttys000 Tue May 26 14:44 - 14:44 (00:00)
nishanthk ttys002 Tue May 26 14:43 - 14:43 (00:00)
nishanthk ttys002 Tue May 26 14:43 - 14:43 (00:00)
nishanthk ttys002 Tue May 26 14:43 - 14:43 (00:00)
nishanthk ttys002 Tue May 26 14:43 - 14:43 (00:00)
nishanthk ttys002 Tue May 26 14:43 - 14:43 (00:00)
nishanthk ttys002 Tue May 26 14:43 - 14:43 (00:00)
nishanthk ttys002 Tue May 26 14:43 - 14:43 (00:00)
nishanthk ttys002 Tue May 26 14:43 - 14:43 (00:00)
nishanthk ttys002 Tue May 26 14:43 - 14:43 (00:00)
nishanthk ttys002 Tue May 26 14:43 - 14:43 (00:00)
nishanthk ttys002 Tue May 26 14:43 - 14:43 (00:00)
nishanthk ttys001 Tue May 26 14:42 - 14:42 (00:00)
nishanthk ttys000 Tue May 26 14:31 - 14:31 (00:00)
nishanthk console Tue May 26 13:42 - 21:49 (6+08:07)
_mbsetupuser console Tue May 26 13:27 - 13:42 (00:14)
root console Tue May 26 13:27 - 13:27 (00:00)
reboot time Tue May 26 13:26
shutdown time Thu May 21 02:12
reboot time Thu May 21 02:03
reboot time Tue Mar 3 22:23
reboot time Tue Mar 3 22:18
wtmp begins Tue Mar 3 22:18:14 CST 2026 field on Runner)
gofmt: dag/runner.go, dag/runner_test.go, deploymanager/dryrun.go
Tests (-race, all packages):
session_test.go 20 cases: construction, CR validation, accessor
fields, current version, component DAG helpers,
scoped/truncate name, uniqueness
controller_test.go 14 cases: GVK, HookResult, BaseController defaults,
override behaviour
rolloutmanager_test.go 24 cases: happy path, setup/deploy errors, verify
incomplete, downstream blocking, after-deploy/verify
hooks (called/not-called/error), independent branches,
concurrent execution
reconcilemanager_test.go 27 cases: basic reconcile, requeue control,
preconditions (fail/multi/pass), finalizer add/error/
stamped-on-object, invalid CR variants, multi-component
ordering, failed-blocks-dependent, status management
(stable/error/verify-wait), ReconcileResult defaults
CI: .github/workflows/pr5-reconcile.yml — go test -race ./... + golangci-lint
Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
The struct field on Runner was never read or written — the concurrent scheduler uses a local variable instead. golangci-lint (unused linter) correctly flagged this on the PR-5 CI run. Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
Signed-off-by: Nishanth Kolakalapudi <Nishanth.Kol@ibm.com>
Tanker2020
marked this pull request as ready for review
August 13, 2026 16:34
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ports the Python
oper8resource verification layer to Go. ProvidesVerifyResource— the single entry point that fetches an object's current state and decides whether it is ready — along with built-in verifiers for the four standard Kubernetes workload kinds and a subsystem verifier for nested oper8-managed CRs.Files changed:
rewrite/verify/verify.go—VerifyFunc,VerifyOptions,VerifyResource, internal condition helpersrewrite/verify/builtin.go—VerifyPod,VerifyJob,VerifyDeployment,VerifyStatefulSet,VerifySubsystemrewrite/verify/verify_test.go— 42 table-driven tests.github/workflows/pr4-verify.yml— CIDepends on: PR-2 (
deploymanager/) forDeployManager.Get(), PR-3 (status/) forConditionReady,ConditionUpdating,TimestampKey,GetVersion. This PR is stacked on the PR-3 branch but all four packages compile and test cleanly when merged in any order.What was ported
verify_resources.py—verify_resource()verify.VerifyResource(ctx, dm, apiVersion, kind, name, opts)verify_resources.py—verify_pod()verify.VerifyPod(objectState)verify_resources.py—verify_job()verify.VerifyJob(objectState)verify_resources.py—verify_deployment()verify.VerifyDeployment(objectState)verify_resources.py—verify_statefulset()verify.VerifyStatefulSet(objectState)verify_resources.py—verify_subsystem()verify.VerifySubsystem(objectState, desiredVersion)_resource_verifiersmodule dictkindVerifierspackage map +Register()_verify_conditionverifyCondition()_check_conditioncheckCondition()Design decisions
session.get_object_current_state()→dm.Get(ctx, ...)Python's
verify_resourcetook asessionobject and calledsession.get_object_current_state(kind, name, api_version, namespace). Thesessionabstraction doesn't exist yet in the Go port (PR-5).VerifyResourcetakes aDeployManagerdirectly and callsdm.Get()— it is context-aware and testable withDryRunDeployManagerin isolation._SESSION_NAMESPACEsentinel → explicitNamespacefieldPython used a
_SESSION_NAMESPACEsentinel default to distinguish "use session namespace" from "non-namespaced resource (pass None)". Go replaces this with an explicitopts.Namespacestring. Callers pass the namespace they want; the zero value ("") means cluster-scoped (non-namespaced)._resource_verifiersmodule dict →Register()+init()Python populated a module-level dict at import time. Go uses an explicit
Register(kind, fn)function called frombuiltin.go'sinit(). This is safe under-race(init runs once beforemain), makes the registry extensible by callers without package modification, and avoids hidden global state mutation.dateutil.parser→time.Parse(time.RFC3339)Python used
dateutil.parser.parse()for timestamp sorting, which accepts many formats. Go usestime.Parse(time.RFC3339)— the only format Kubernetes actually writes. Invalid or missing timestamps fall back totime.Unix(0, 0)(epoch), matching Python'sdatetime.fromtimestamp(0)fallback.Bug fix: raw
boolstatus valuesPython's
_check_conditionhandled three cases for the conditionstatusfield:"True"/"False"(standard Kubernetes)bool—bool(obj_status) == expected_statusThe initial Go port did
condition["status"].(string)which silently returns""for a rawboolvalue, causing the function to incorrectly returnfalse. Fixed with a type switch:case bool→ direct compare,case string→strings.EqualFold(v, "true") == expectedStatus.Test coverage (42 tests)
VerifyResourceintegration (viaDryRunDeployManager)VerifyPodVerifyJobVerifyDeploymentVerifyStatefulSetVerifySubsystemAll tests run with
-race.