Wire node-policy delete, replace-on-rename for rules, safe defaults, API resync and field alignment, CRUD test harness - #56
Conversation
…s; remove leaked curl.txt - make proto now also syncs hpa_connector.proto/pb.go (recommendation.proto imports it upstream). - Remove workloadRule hpaRule.targetUtilization / targetMemoryUtilization / primaryMetric: dakr reserved HPARuleConfig fields 4/5/12 on 2026-07-07 and dropped the columns, so the server ignored and never echoed them. Use hpaRule.metrics (CPU/Memory/external triggers) instead. BREAKING for programs setting them. - Delete the untracked curl.txt scratch file that contained a live bearer token and gitignore the pattern. ROTATE THAT TOKEN.
…ts, NotFound handling, strict enums - NodePolicy.Delete now calls DeleteNodePolicy (the RPC exists and cascades targets server-side); it was a state-only no-op that left Karpenter NodePools running after 'pulumi destroy'. NodePolicy.Read also skips read-only source=="cluster" virtual policies that ListNodePolicies mixes in. - NodePolicyTarget.Delete disables the target via UpdateNodePolicyTarget (no delete RPC exists server-side) instead of silently leaving it enabled; clusterIds is validated to exactly one entry (the server rejects more). - WorkloadRule implements CustomDiff: changing clusterId/namespace/kind/name now replaces the resource. Update previously upserted a second rule server-side (the API keys on that 4-tuple) while state kept the old ID. - enabled on NodePolicyTarget and WorkloadPolicyTarget is now *bool with a default of true — omitting it used to silently create a DISABLED target while the docs said 'default true'. - Every Read returns an empty response on CodeNotFound (or not-found-in-list) so out-of-band deletions drop from state instead of failing refresh; every Delete tolerates CodeNotFound. - String enums (actionTriggers, detectionTriggers, kindFilter, selector operators, primaryMetric) are validated up front — the API silently drops unknown values, which previously produced policies with no triggers or targets matching every kind. Selector operators gain Gt/Lt mappings. - The retry interceptor now retries only idempotent (Get*/List*) RPCs: a transient failure on a Create whose first attempt landed server-side produced duplicate resources. - float32 values read back from the API convert via shortest-round-trip formatting (0.7 stays 0.7, not 0.699999988079071) — kills the perpetual diff after 'pulumi refresh'. enablePmaxProtection no longer materializes as &false when omitted. - Cluster.Update no longer silently rotates the cluster token when state has none (post-import): rotating on an unrelated change invalidated the running agent's credential. Imported clusters keep an empty token. Update also uses the same CustomName->Name fallback as Read.
… policies with the current API workloadPolicy (v1) new inputs: enableInPlaceVerticalScaling, allowInPlaceMemoryLimitDecrease, pdbEnabled, cpu/memory (limit) floor/ceiling percents, 9 JVM heap knobs, vertical requestUseRss/limitUseRss (memory only), horizontal networkTargetThroughputBytesPerSec, targetMemoryUtilization, compositeFormula, scaleDownCooldownSeconds. workloadPolicyTarget: annotationSelector, workloadNamesNotIn, kindFilterNotIn (validated); nodeGroupNames marked deprecated (unused upstream). workloadRule (v2): disabled (create via upsert; updates go through the ToggleWorkloadRuleDisabled RPC since the upsert rejects it), lookbackPeriodSeconds, per-axis initialRequest/floorPercent/ceilingPercent/ initialLimit/limitFloorPercent/limitCeilingPercent/requestUseRss/limitUseRss (also on container rules), hpaRule.metrics connectorId; metric metadata strips the serverAddress/query keys the backend folds in. nodePolicy: startupTaints, zonalShift (AWS only), instanceLocalNvme (AWS only), cloudProviderId, masterOverrideRoleName, azure.imageVersion (operator >= 1.8.4).
In-memory connect server (httptest + generated handlers with Unimplemented* embeds) mimicking the dakr semantics that matter to CRUD correctness: upsert keys, NotFound codes, node-policy-target invariants (empty target_id on create, at most one cluster), fields.disabled rejection on workload-rule update, and the virtual source=="cluster" policies mixed into ListNodePolicies. Lifecycle tests drive each resource's real Create/Read/Update/Delete against it and assert: create round-trips (ids, nested blocks, taints, exact float percentiles), refresh after out-of-band deletion drops from state, node policy delete actually deletes and is idempotent, node policy target destroy disables the target, flipping workloadRule.disabled goes through ToggleWorkloadRuleDisabled, and omitting enabled creates an ENABLED target.
… fallback - Makefile VERSION falls back to 0.0.0-dev instead of dev — 'pulumi package get-schema' rejects non-semver versions, which broke gen-schema/gen-sdk in any clone without tags (including the gen-sdk workflow's fetch-depth:1 checkout). - schema.json + nodejs/python/go SDKs regenerated: picks up the new workloadPolicy/workloadRule/nodePolicy/target fields, the enabled defaults, and drops the removed hpaRule.targetUtilization/targetMemoryUtilization/ primaryMetric fields.
… skip, retry guard, kind validation, TS 5.2 consumer fix
- emergencyResponse float32 fields (oomMemoryMultiplier, cpuThrottling*)
read back via shortest-round-trip conversion like every other float — the
raw float64 cast produced 0.800000011920929-style refresh drift.
- NodePolicy.Read skips exactly the known-virtual source ("cluster")
instead of any unknown source, so a future backend source tag cannot
silently orphan a managed policy.
- retryInterceptor passes straight through when maxAttempts < 1 instead of
returning (nil, nil).
- workloadRule.kind validated against the supported kinds
(Deployment/StatefulSet/DaemonSet/CronJob/Job) — the API accepts any
string and a typo would create a rule that never matches.
- Stale cluster token docs removed (Describe + Update comment still said
tokens rotate on import).
- NodeJS consumer test pins @types/node to ^18 via npm overrides: the
latest @pulumi/pulumi pulls an @types/node that needs TS >= 5.6, which
has been failing the TS 5.2 matrix on main's nightly since Aug 9.
…arer names - isNotFound(err) collapses the repeated connect.CodeOf NotFound checks across all Read/Delete paths; truePtr() collapses the proto-bool to optional-pointer read-back idiom. - f32 renamed to f32to64 (the old name read as the opposite direction). - fakeIdempotentRequest renamed to fakeProcedureRequest — it also backs newMutatingRequest.
…arget lifecycle - roundtrip_test.go: a reflection-based populator fills EVERY pulumi-tagged field of every args struct (enum-valued strings get valid members), then each resource converts args -> proto -> args -> proto and requires the two protos to be identical — so a newly added field that stops round-tripping in either direction fails the suite. Spot-checks assert the specific new field groups (zonal shift, startup taints, JVM knobs, floor/ceiling bounds, RSS flags, HPA extras, lookback/disabled, connectorId) survive the cycle. A guard test fails if a future field type isn't handled by the populator. - WorkloadRule.Diff unit tests: identity fields (clusterId/namespace/kind/ name) require replacement, non-identity changes update in place, and a no-change plan reports no changes. - Fake server gains the WorkloadPolicyTarget handlers plus a full lifecycle test (create with annotationSelector/notIn filters, enabled default, update propagation, drop-from-state after out-of-band deletion, tolerant delete).
Code Review ✅ Approved 3 resolved / 3 findingsAligns the Pulumi provider with current API semantics, implementing real node-policy deletes, replace-on-rename for rules, safe target defaults, API resyncs, and a CRUD test harness. Addressed findings for NodePolicy.Read source filtering, retryInterceptor maxAttempts guards, and WorkloadRule.Kind validation. ✅ 3 resolved✅ Edge Case: NodePolicy.Read blacklists source, may drop provider-managed policies
✅ Bug: retryInterceptor returns (nil,nil) if maxAttempts <= 0
✅ Quality: WorkloadRule.Kind not validated against valid kinds
Was this helpful? React with 👍 / 👎 | Gitar |
What?
Brings the Pulumi provider back in line with the current DAKR API and fixes every critical found in the provider audit:
pulumi destroynow actually deletes node policies and disables node-policy targets, renaming a workload rule replaces it instead of orphaning it,enableddefaults to true on targets as documented, out-of-band deletions drop from state instead of failing refresh, and string enums are validated instead of silently dropped. Protos are resynced to services HEAD, three deleted HPA fields are removed, the full set of new backend fields (v1 policies, v2 rules, targets, node policies) is exposed, andschema.jsonplus the Node/Python/Go SDKs are regenerated. Also removes a scratch file from the repo root that contained a live bearer token and gitignores the pattern.Why?
The provider had drifted ~2 months behind the backend and had several bugs with real blast radius: destroy left Karpenter NodePools provisioning nodes, an omitted
enabledsilently created a disabled target while the docs said "default true", the retry interceptor could duplicate resources by retrying Creates, andhpaRule.targetUtilization/primaryMetricwere silent no-ops (the backend deleted those fields in July). Terraform got the same treatment in devzero-inc/terraform-provider-devzero#28; this PR gives Pulumi users parity.How?
hpaRule.targetUtilization,targetMemoryUtilization,primaryMetricare removed (already ignored server-side);hpaRule.metricsis the replacement. Cluster updates also no longer auto-rotate the token when state has none (post-import) — rotating on an unrelated rename silently invalidated the running agent's credential.Updateon a renamed rule created a second rule while state kept the old ID. ACustomDiffnow marks those four fields as replace-on-change.disabledon workload rules is create-only in the upsert API; updates route through theToggleWorkloadRuleDisabledRPC.clusterIdsvalidates to exactly one entry because the server rejects more.validation.go): the API silently ignores unknown enum strings (an unknown trigger is dropped, an unknown kind matches every workload), so Create/Update now fail fast with the valid-values list. Selector operators gainGt/Lt.Get*/List*RPCs —CreateNodePolicieswithout a client-set ID always inserts, so a retried Create duplicated resources.&false; HPA metricmetadatastrips theserverAddress/querykeys the backend folds in.source == "cluster"virtual policiesListNodePoliciesmixes in.VERSIONfalls back to0.0.0-dev(valid semver) —pulumi package get-schemarejected the olddevfallback, which brokegen-schema/gen-sdkin any clone without tags, including CI'sfetch-depth: 1checkout.Testing?
fake_server_test.go: an in-memory connect server (httptest + generated handlers) mimicking dakr semantics — upsert keys, NotFound codes, target invariants,fields.disabledrejection on update, virtual cluster policies — driving real Create/Read/Update/Delete lifecycles. Covers out-of-band deletion, delete idempotency, disable-on-destroy, the disabled-toggle path, exact float round-trips, and theenableddefault.go build ./...,go vet ./..., full suite green; the regenerated Go SDK compiles.Anything Else?
The leaked bearer token that was in
curl.txtshould be rotated — deleting the file doesn't invalidate it. Deferred to a follow-up (same as the Terraform PR):gcp/ocinode classes,kedaScaledObject,jvmHeapRule; plus release-workflow publish ordering and stale example version pins.