Skip to content

patch(v2.0): Decode DPU network policy overrides#4070

Open
kfelternv wants to merge 4 commits into
NVIDIA:release/v2.0from
kfelternv:codex/backport-3866-release-v2.0
Open

patch(v2.0): Decode DPU network policy overrides#4070
kfelternv wants to merge 4 commits into
NVIDIA:release/v2.0from
kfelternv:codex/backport-3866-release-v2.0

Conversation

@kfelternv

Copy link
Copy Markdown
Contributor

Backports #3866 to prevent attached DPU detail requests from returning HTTP 500 when network security policy overrides are present. The release implementation preserves those protobuf fields through Temporal while retaining compatibility with legacy workflow payloads.

Related issues

Backport of #3866.

Type of Change

  • Add - New feature or capability
  • Change - Changes in existing functionality
  • Fix - Bug fixes
  • Remove - Removed features or deprecated functionality
  • Internal - Internal changes (refactoring, tests, docs, etc.)

Breaking Changes

  • This PR contains breaking changes

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing performed
  • No testing required (docs, internal refactor, etc.)

Passed focused handler decode-fallback and Temporal workflow tests, plus OpenAPI lint.

Additional Notes

Conflict resolution uses release/v2.0's workflow-schema proto package instead of main's newer Core proto package.

Fixes an HTTP 500 when retrieving attached DPU details for machines with
network security policy overrides by preserving protobuf fields through
the Temporal workflow. The REST response stays the same, and the API
documentation now makes clear that only selected DPU network
configuration fields are exposed.

None.

- [ ] **Add** - New feature or capability
- [ ] **Change** - Changes in existing functionality
- [x] **Fix** - Bug fixes
- [ ] **Remove** - Removed features or deprecated functionality
- [ ] **Internal** - Internal changes (refactoring, tests, docs, etc.)

- [ ] **This PR contains breaking changes**

- [x] Unit tests added/updated
- [ ] Integration tests added/updated
- [x] Manual testing performed
- [ ] No testing required (docs, internal refactor, etc.)

Signed-off-by: Kyle Felter <kfelter@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Jul 24, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: cd6db016-64f0-4605-883b-2025fbcf1a26

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@kfelternv

Copy link
Copy Markdown
Contributor Author

What changed

This backport carries the DPU list through Temporal in a protobuf wrapper so network-policy oneof values decode correctly. The REST handler also accepts the previous top-level array returned by older site-agent workers.

Scenario and setup

I tested head 4e9867e63ff8fbea8626a10ba59cdc53f6d83afb against pre-fix release/v2.0 revision a14089b7fce2c79858e398ea9de17b7537b026fb in an isolated local Kind cluster. The API and site-agent images were built from those revisions and labeled with verification.revision. The final exact-head pods were ready before the endpoint checks.

The local site contained a simulated host with an attached DPU. For the regression case, Core was configured with both source and destination network-policy variants. The public REST model intentionally omits those internal fields, so the Core read below corroborates setup; the REST call is the behavior under test. The rollout-compatibility row used the exact API with the pre-fix site-agent and a network configuration without policy overrides, matching the compatibility contract from the original review.

The commands below use local test values and require the caller to export short-lived tokens and simulated machine IDs:

API_URL="${API_URL:?set to the local API URL}"
ORG="test-org"
HOST_ID="${HOST_ID:?set to the simulated host ID}"
DPU_ID="${DPU_ID:?set to its attached DPU ID}"
ADMIN_TOKEN="${ADMIN_TOKEN:?set from the local provider-admin login}"
TENANT_TOKEN="${TENANT_TOKEN:?set from the local unprivileged-tenant login}"
ROUTE="$API_URL/v2/org/$ORG/nico/machine/$HOST_ID/dpu"

Verification

Step 1: Reproduce the pre-fix decode failure

Why this step exists: It establishes the original failure with both REST images built from the pre-fix release revision and a policy-bearing Core response.

Runnable command:

curl -sS -o pre-fix.json -w 'status=%{http_code}\n' "$ROUTE" \
  -H "Authorization: Bearer $ADMIN_TOKEN"
jq -c '{message}' pre-fix.json

Observed result:

status=500
{"message":"Failed to retrieve Machine DPU information from Site: unable to decode: json: cannot unmarshal object into Go struct field NetworkSecurityGroupRuleAttributes.dpu_network_config.network_security_policy_overrides.rule.SourceNet ..."}

Why this proves the behavior: The real endpoint reproduced the Temporal JSON decode failure before the backport.

Step 2: Retrieve the policy-bearing DPU at the exact head

Why this step exists: It exercises the changed REST-to-Temporal-to-site-agent-to-Core path with both nested protobuf variants present.

Runnable command:

curl -sS -o exact.json -w 'status=%{http_code}\n' "$ROUTE" \
  -H "Authorization: Bearer $ADMIN_TOKEN"
jq -c --arg dpu "$DPU_ID" --arg host "$HOST_ID" '{
  type: type,
  count: length,
  first_id_matches: (.[0].id == $dpu),
  host_id_matches: (.[0].hostMachineId == $host),
  network_config_present: (.[0].dpuNetworkConfig != null)
}' exact.json

kubectl -n nico-system exec deployment/nico-api -- \
  /opt/carbide/nico-admin-cli --format json dpu network config \
  --machine-id "$DPU_ID" |
  jq -c '{
    oneof_object_count: ([.. | objects | select(has("source_net") or has("destination_net"))] | length),
    oneof_keys: ([.. | objects | keys[] | select(. == "source_net" or . == "destination_net")] | unique)
  }'

Observed result:

status=200
{"type":"array","count":1,"first_id_matches":true,"host_id_matches":true,"network_config_present":true}
{"oneof_object_count":1,"oneof_keys":["destination_net","source_net"]}

Why this proves the behavior: The public endpoint returned the attached DPU instead of HTTP 500 while Core supplied the source and destination oneof values that triggered the bug.

Step 3: Exercise repeat, authentication, authorization, and selector behavior

Why this step exists: It verifies that the changed decode path preserves the route's existing repeat and error behavior.

Runnable command:

curl -sS -o repeat.json -w 'repeat=%{http_code}\n' "$ROUTE" \
  -H "Authorization: Bearer $ADMIN_TOKEN"
cmp -s exact.json repeat.json && echo 'repeat_body_identical=true'
curl -sS -o /dev/null -w 'no_auth=%{http_code}\n' "$ROUTE"
curl -sS -o /dev/null -w 'tenant=%{http_code}\n' "$ROUTE" \
  -H "Authorization: Bearer $TENANT_TOKEN"
curl -sS -o /dev/null -w 'dpu_as_host=%{http_code}\n' \
  "$API_URL/v2/org/$ORG/nico/machine/$DPU_ID/dpu" \
  -H "Authorization: Bearer $ADMIN_TOKEN"

Observed result:

repeat=200
repeat_body_identical=true
no_auth=401
tenant=403
dpu_as_host=404

Why this proves the behavior: Repeating the successful request was stable, and the unchanged access and host-selector cases returned their established statuses.

Step 4: Exercise the legacy workflow-result fallback

Why this step exists: It verifies the rollout path called out in the original review: an exact-head API decoding a simple top-level array from a pre-fix site-agent worker.

Runnable command:

EXACT_API_IMAGE="${EXACT_API_IMAGE:?set to the exact-head API image}"
LEGACY_SITE_AGENT_IMAGE="${LEGACY_SITE_AGENT_IMAGE:?set to the pre-fix site-agent image}"
SITE_AGENT_CONTAINER="$(kubectl -n nico-rest get statefulset nico-rest-site-agent \
  -o jsonpath='{.spec.template.spec.containers[0].name}')"

docker image inspect "$EXACT_API_IMAGE" "$LEGACY_SITE_AGENT_IMAGE" \
  --format '{{index .Config.Labels "verification.revision"}}'
kubectl -n nico-rest set image statefulset/nico-rest-site-agent \
  "$SITE_AGENT_CONTAINER=$LEGACY_SITE_AGENT_IMAGE"
kubectl -n nico-rest rollout status statefulset/nico-rest-site-agent --timeout=180s
curl -sS -o legacy.json -w 'legacy_provider=%{http_code}\n' "$ROUTE" \
  -H "Authorization: Bearer $ADMIN_TOKEN"
jq -c '{type:type,count:length,network_config_present:(.[0].dpuNetworkConfig != null)}' legacy.json

Observed result:

4e9867e63ff8fbea8626a10ba59cdc53f6d83afb
a14089b7fce2c79858e398ea9de17b7537b026fb
legacy_provider=200
{"type":"array","count":1,"network_config_present":true}

Why this proves the behavior: The exact API successfully applied its fallback to the legacy worker's top-level array through the real endpoint.

Step 5: Run focused regression and contract checks

Why this step exists: It covers the wrapper, policy variants, legacy payload decode, public model, OpenAPI, and final diff.

Runnable command:

go test ./site-workflow/pkg/workflow -run '^TestGetDpuMachinesTestSuite$' -count=1
go test ./site-workflow/pkg/activity -run '^TestManageMachine_GetDpuMachinesByIDs$' -count=1
go test ./api/pkg/api/handler \
  -run '^(TestGetDpuMachinesLegacyTemporalPayload|TestMachineHandler_GetDpuMachines)$' -count=1
go test ./api/pkg/api/model -count=1
npx @redocly/cli lint --skip-rule=no-ambiguous-paths ./openapi/spec.yaml
git diff --check a14089b7fce2c79858e398ea9de17b7537b026fb...HEAD

Observed result:

workflow package: PASS
activity package: PASS
handler package: PASS
API model package: PASS
OpenAPI specification: valid
git diff --check: no output

Why this proves the behavior: The focused regression paths and generated public contract are consistent with the runtime results, and the final diff has no whitespace errors.

@kfelternv
kfelternv marked this pull request as ready for review July 24, 2026 18:25
@kfelternv
kfelternv requested review from a team, pbreton, polarweasel and thossain-nv July 24, 2026 18:25
@github-actions

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

🔐 TruffleHog Secret Scan

No secrets or credentials found!

Your code has been scanned for 700+ types of secrets and credentials. All clear! 🎉

🔗 View scan details

🕐 Last updated: 2026-07-24 18:28:33 UTC | Commit: 4e9867e

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4e9867e63f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


// GetDpuMachinesByIDs is an activity to retrieve DPU Machines by IDs with network configuration
func (mm *ManageMachine) GetDpuMachinesByIDs(ctx context.Context, dpuMachineIDs []string) ([]*cwssaws.DpuMachine, error) {
func (mm *ManageMachine) GetDpuMachinesByIDs(ctx context.Context, dpuMachineIDs []string) (*cwssaws.DpuMachineList, error) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the DPU activity payload rollout-compatible

When site-agent is rolled with old and new pods on the same Temporal task queue, this keeps registering the same GetDpuMachinesByIDs activity name but changes its result from []*DpuMachine to DpuMachineList. Temporal can route an activity task scheduled by one workflow version to a worker running the other version, so the waiting workflow decodes the activity result into the wrong shape and fails before the API handler's legacy workflow-result fallback runs. That makes /machine/{id}/dpu return 500 during mixed-version rollouts; please version the activity name or keep/decode both result shapes inside the workflow until old workers are drained.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is for a graceful zero down upgrade? I don't think we need to worry about that right now.

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown

🔍 Container Scan Summary

Service Total Critical High Medium Low Other
boot-artifacts-aarch64 3 0 0 3 0 0
boot-artifacts-x86_64 3 0 0 3 0 0
forge-admin-cli-x86_64 260 16 29 79 8 128
machine-validation-runner 548 41 117 191 18 181
machine_validation 548 41 117 191 18 181
machine_validation-aarch64 548 41 117 191 18 181
nvmetal-carbide 548 41 117 191 18 181
TOTAL 2458 180 497 849 80 852

Per-CVE detail lives in the per-service grype-* artifacts (JSON + SARIF). Severity counts only — no CVE IDs published here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant