Skip to content
This repository was archived by the owner on Sep 9, 2026. It is now read-only.

OSAC-2340: Add defaults field to NetworkClass proto - #882

Merged
openshift-merge-bot[bot] merged 14 commits into
osac-project:mainfrom
danmanor:feat/OSAC-2340-networkclass-defaults
Jul 14, 2026
Merged

openshift-merge-bot[bot] merged 14 commits into
osac-project:mainfrom
danmanor:feat/OSAC-2340-networkclass-defaults

Conversation

@danmanor

@danmanor danmanor commented Jul 12, 2026 •

Copy link
Copy Markdown
Contributor

OSAC-2340: Add defaults field to NetworkClass proto

Jira: https://redhat.atlassian.net/browse/OSAC-2340
Epic: OSAC-2339 — Default Networking

Summary

Adds a NetworkDefaults message and spec.defaults field to NetworkClass in both public and private protos. This is the first task in the Default Networking epic — it defines the contract that downstream tasks (tenant onboarding default resource creation, installer configuration) depend on. Admins configure default CIDRs and security group rules on the NetworkClass; the system uses these to auto-create default networking resources at tenant onboarding.

Changes

Proto definitions:

  • Added NetworkClassSpec wrapper with NetworkDefaults defaults field, following the standard {id, metadata, spec, status} object pattern
  • NetworkDefaults has per-address-family CIDR fields matching VirtualNetwork/Subnet conventions: virtual_network_ipv4_cidr, virtual_network_ipv6_cidr, subnet_ipv4_cidr, subnet_ipv6_cidr, plus repeated SecurityRule ingress_rules/egress_rules
  • Added protovalidate CEL annotations: isIpPrefix(4, true) / isIpPrefix(6, true) for CIDR validation, and per-family subnet-requires-VN message-level constraints
  • spec field on public API is OUTPUT_ONLY — tenants can see defaults but only admins can set them via the private API

Server (private):

  • validateDefaultCIDRPair() — shared helper validating CIDR format, subnet containment within VN CIDR, and the subnet-requires-VN constraint for each address family
  • applyNetworkClassUpdate — handles "spec" and "spec.defaults" field mask paths

Server (public):

  • spec field added to AddIgnoredFields on inMapper

Bug fix (unrelated):

No database migration needed — the JSONB data column stores new proto fields automatically.

Testing

  • Unit tests: 20 new tests covering CRUD with defaults, all validation error paths (invalid CIDR, subnet containment, invalid rules, subnet without VN), field mask update, update-path validation, OUTPUT_ONLY behavior via public API
  • Integration tests: N/A — no new component interactions
  • Coverage: All behavioral paths through public interfaces covered

Acceptance Criteria

  • AC-1: NetworkClass proto includes spec.defaults with per-family CIDRs and security group rules
  • AC-2: NetworkClass can be created and updated with defaults configuration via API
  • AC-3: The spec.defaults field is persisted and retrievable via Get/List operations
  • AC-4: Server-side validation enforces CIDR format (via protovalidate CEL + Go), subnet containment, subnet-requires-VN, and rule validity
  • AC-5: Field mask support for updating defaults via spec.defaults

Summary by CodeRabbit

  • New Features
    • Added network class defaults for tenant onboarding, including IPv4/IPv6 virtual networks and subnets, security rules, and optional NAT gateway creation.
    • Defaults are available as output-only information through the public API.
  • Bug Fixes
    • Improved validation for CIDRs, subnet relationships, and security rules.
    • Prevented public requests from modifying protected network class defaults.
    • Added support for replacing or clearing defaults through private API updates.

@openshift-ci-robot

openshift-ci-robot commented Jul 12, 2026 •

Copy link
Copy Markdown

@danmanor: This pull request references OSAC-2340 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the task to target the "5.0.0" version, but no target version was set.

Details

In response to this:

OSAC-2340: Add defaults field to NetworkClass proto

Jira: https://redhat.atlassian.net/browse/OSAC-2340
Epic: OSAC-2339 — Default Networking

Summary

Adds a NetworkDefaults message and defaults field to NetworkClass in both public and private protos. This is the first task in the Default Networking epic — it defines the contract that downstream tasks (tenant onboarding default resource creation, installer configuration) depend on. Admins configure virtualNetworkCIDR, subnetCIDR, and security group rules on the NetworkClass; the system uses these to auto-create default networking resources at tenant onboarding.

Changes

Proto definitions:

  • Added NetworkDefaults message with virtual_network_cidr, subnet_cidr, repeated SecurityRule ingress_rules, repeated SecurityRule egress_rules
  • Added defaults field (field 12) to NetworkClass in both public (OUTPUT_ONLY) and private protos
  • Reuses existing SecurityRule message for consistency with SecurityGroupSpec

Server (private):

  • validateNetworkDefaults() — validates CIDR format, subnet containment within VN CIDR, and security rule validity (reuses existing validateSecurityRule)
  • applyNetworkClassUpdate — added "defaults" field mask case

Server (public):

  • defaults field added to AddIgnoredFields on inMapper — tenants can see defaults (OUTPUT_ONLY) but only admins can set them via the private API

No database migration needed — the JSONB data column stores new proto fields automatically.

Testing

  • Unit tests: 18 new tests covering CRUD with defaults, all validation error paths (invalid CIDR, subnet containment, invalid rules), field mask update, OUTPUT_ONLY behavior via public API
  • Integration tests: N/A — no new component interactions
  • Coverage: All behavioral paths through public interfaces covered

Acceptance Criteria

  • AC-1: NetworkClass proto includes a defaults field with virtual_network_cidr, subnet_cidr, and security group rules
  • AC-2: NetworkClass can be created and updated with defaults configuration via API
  • AC-3: The defaults field is persisted and retrievable via Get/List operations
  • AC-4: Server-side validation enforces CIDR format, subnet containment, and rule validity
  • AC-5: Field mask support for updating defaults

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci

openshift-ci Bot commented Jul 12, 2026

Copy link
Copy Markdown

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@coderabbitai

coderabbitai Bot commented Jul 12, 2026 •

Copy link
Copy Markdown

Review Change Stack

Walkthrough

NetworkClass now supports tenant onboarding defaults through spec.defaults, including CIDRs, security rules, and NAT configuration. Private APIs validate and update defaults, while public APIs expose them as output-only. Tests cover persistence, listing, clearing, validation, and public update behavior. An unrelated dependency and validation test are updated.

Changes

Network class defaults

Layer / File(s) Summary
Defaults protobuf contract
proto/private/.../network_class_type.proto, proto/public/.../network_class_type.proto
Adds NetworkClass.spec.defaults with CIDRs, ingress/egress security rules, NAT configuration, and protobuf/CEL validation declarations.
Private validation and updates
internal/servers/private_network_classes_server.go
Validates default CIDRs and security rules, enforces subnet containment, and applies spec.defaults field-mask updates.
Public output-only behavior
internal/servers/network_classes_server.go, internal/servers/network_classes_server_test.go
Ignores spec and is_default on public input while returning defaults; tests persistence, listing, replacement, clearing, validation, and API boundaries.

Module dependency cleanup

Layer / File(s) Summary
Direct module requirement cleanup
go.mod
Removes the direct github.com/neilotoole/jsoncolor requirement.

Catalog validation test

Layer / File(s) Summary
Field rejection assertion
internal/servers/catalog_item_validation_test.go
Updates the unlisted-field test to use run_strategy and assert that field name in the error.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant PublicAPI
  participant PrivateAPI
  participant Validator
  Client->>PublicAPI: Submit NetworkClass request
  PublicAPI->>PrivateAPI: Forward request without spec
  PrivateAPI->>Validator: Validate spec.defaults
  Validator-->>PrivateAPI: Validation result
  PrivateAPI-->>PublicAPI: Persisted NetworkClass
  PublicAPI-->>Client: NetworkClass with output-only defaults
Loading

Suggested labels: lgtm

Suggested reviewers: jhernand, tzvatot, eliorerz, ybettan, ori-amizur

🚥 Pre-merge checks | ✅ 11
✅ Passed checks (11 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a defaults field to NetworkClass proto.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
No-Hardcoded-Secrets ✅ Passed Changed hunks add proto fields and validation only; secret-pattern scans found no API keys, tokens, passwords, private keys, URLs with creds, or long base64 blobs.
No-Weak-Crypto ✅ Passed No MD5/SHA1/DES/RC4/3DES/Blowfish/ECB, custom crypto, or secret compares appear in the touched code; changes are proto/server validation only.
No-Injection-Vectors ✅ Passed Targeted scan of the touched files found no shell=True/eval/exec/pickle/yaml/os.system/dangerouslySetInnerHTML or SQL concatenation patterns.
Container-Privileges ✅ Passed No changed files are container/K8s manifests, and the diff contains no privileged/hostNetwork/hostPID/SYS_ADMIN/allowPrivilegeEscalation settings.
No-Sensitive-Data-In-Logs ✅ Passed Changed files are proto/generated code only; no new log statements or sensitive-data strings appear in the patch.
Ai-Attribution ✅ Passed PR commits use proper Red Hat AI attribution trailers: multiple commits include "Assisted-by: Claude Code" and no AI-related Co-Authored-By was found.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@danmanor
danmanor marked this pull request as ready for review July 12, 2026 08:17
@openshift-ci
openshift-ci Bot requested review from eliorerz and tzvatot July 12, 2026 08:18
@danmanor danmanor changed the title OSAC-2340: Add defaults field to NetworkClass proto WIP: OSAC-2340: Add defaults field to NetworkClass proto Jul 12, 2026
@danmanor danmanor changed the title WIP: OSAC-2340: Add defaults field to NetworkClass proto OSAC-2340: Add defaults field to NetworkClass proto Jul 12, 2026
@danmanor

Copy link
Copy Markdown
Contributor Author

/retest

@github-actions

Copy link
Copy Markdown

Re-triggered failed runs:

  • E2E VMaaS Full Install (#29185607780)

"defaults.virtual_network_cidr: %v", err)
}
vnPrefix, _ = netip.ParsePrefix(parsed)
_ = vnPrefix

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think this is not needed.

@danmanor
danmanor requested a review from ori-amizur July 12, 2026 17:11
@danmanor
danmanor force-pushed the feat/OSAC-2340-networkclass-defaults branch from 27aba83 to 5587c34 Compare July 12, 2026 17:16

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/servers/network_classes_server_test.go`:
- Around line 1094-1206: Add an Update-path validation test alongside the
existing negative cases, using an existing valid NetworkClass, an invalid
defaults value such as an invalid CIDR, and an UpdateMask containing “defaults”.
Invoke the server’s Update operation and assert it returns an error, verifying
validateNetworkClass validates merged defaults during updates.

In `@internal/servers/private_network_classes_server.go`:
- Around line 395-408: Handle the error returned by netip.ParsePrefix in
validateNetworkDefaults instead of discarding it. If reparsing parsed fails,
return an appropriate InvalidArgument grpcstatus error for
defaults.virtual_network_cidr; only assign vnPrefix after successful parsing so
subsequent Contains checks never receive an invalid zero-value prefix.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: osac-project/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: fdef774b-dcb8-4ad2-9379-fc1c1b1ee2d8

📥 Commits

Reviewing files that changed from the base of the PR and between da39c1d and 27aba83.

⛔ Files ignored due to path filters (5)
  • go.sum is excluded by !**/*.sum
  • internal/api/osac/private/v1/network_class_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/network_class_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/public/v1/network_class_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/public/v1/network_class_type_protoopaque.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (6)
  • go.mod
  • internal/servers/network_classes_server.go
  • internal/servers/network_classes_server_test.go
  • internal/servers/private_network_classes_server.go
  • proto/private/osac/private/v1/network_class_type.proto
  • proto/public/osac/public/v1/network_class_type.proto
💤 Files with no reviewable changes (1)
  • go.mod

Comment thread internal/servers/network_classes_server_test.go Outdated
Comment thread internal/servers/private_network_classes_server.go

if vnCIDR != "" {
subnetPrefix, _ := netip.ParsePrefix(subnetCIDR)
if !vnPrefix.Contains(subnetPrefix.Addr()) || subnetPrefix.Bits() < vnPrefix.Bits() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nit: the discarded error (_) on netip.ParsePrefix is safe here since parseAndValidateCIDR already validated the string above, but it's fragile if someone refactors this later without noticing the precondition. Consider either assigning and checking, or adding a short comment noting that validation already happened.

Also, the condition !Contains(addr) || Bits() < vnBits is correct but reads a bit inside-out — the < vnPrefix.Bits() part means "subnet is wider than the VN". Flipping to Bits() >= vnPrefix.Bits() with adjusted logic would make the intent slightly more self-documenting ("subnet must be equal or narrower than VN").

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.

Fair point on both. I'll handle the error instead of discarding it — will return grpccodes.Internal since a re-parse failure after validation would indicate an internal inconsistency rather than bad input. And I'll flip the containment condition to make it read more naturally.

"defaults.subnet_cidr: %v", err)
}

if vnCIDR != "" {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If subnet_cidr is set without virtual_network_cidr, the containment check is skipped and the defaults are persisted with a subnet CIDR but no parent VN CIDR. When the tenant-onboarding code later tries to use these defaults, it would have a subnet to create but no VirtualNetwork to put it in.

Should this be rejected here (i.e., if subnet_cidr is set, require virtual_network_cidr too)? Or is the onboarding code expected to handle partial defaults gracefully?

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.

Good catch — you're right, this is a real gap. The default-networking EP treats defaults as a bundle: tenant onboarding (OSAC-2341) creates all three resources (VN + Subnet + SG) together, and Subnet is a child of VirtualNetwork in the resource hierarchy — it can't exist without a parent.

I'll add validation to reject subnet_cidr when virtual_network_cidr is empty.

@danmanor
danmanor requested a review from ybettan July 13, 2026 08:12
// defaults to auto-create a VirtualNetwork, Subnet, and SecurityGroup for the tenant.
NetworkDefaults defaults = 12 [
(google.api.field_behavior) = OUTPUT_ONLY
];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This looks like a candidate for a spec.defaults field.

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.

Done — moved defaults under a new NetworkClassSpec wrapper so it's now spec.defaults, following the standard {id, metadata, spec, status} pattern.

message NetworkDefaults {
// Default CIDR for auto-created tenant VirtualNetwork. Must be valid IPv4 CIDR notation.
// Example: "10.0.0.0/16"
string virtual_network_cidr = 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Done — added buf.validate CIDR format regex on both virtual_network_cidr and subnet_cidr, plus a message-level CEL constraint enforcing that subnet_cidr requires virtual_network_cidr to be set. Deeper semantic validation (valid octets, prefix lengths, subnet containment) stays in the Go server since CEL can't do CIDR arithmetic.

// Default CIDR for auto-created tenant Subnet. Must be valid IPv4 CIDR notation and fall within the
// virtual_network_cidr range.
// Example: "10.0.1.0/24"
string subnet_cidr = 2;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Consider using protovalidate.

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.

Addressed in the same commit — see reply above.

danmanor added 10 commits July 14, 2026 10:35
Assisted-by: Claude Code <noreply@anthropic.com>
Signed-off-by: Dan Manor <dmanor@redhat.com>
…erver

Assisted-by: Claude Code <noreply@anthropic.com>
Signed-off-by: Dan Manor <dmanor@redhat.com>
Assisted-by: Claude Code <noreply@anthropic.com>
Signed-off-by: Dan Manor <dmanor@redhat.com>
Assisted-by: Claude Code <noreply@anthropic.com>
Signed-off-by: Dan Manor <dmanor@redhat.com>
Assisted-by: Claude Code <noreply@anthropic.com>
Signed-off-by: Dan Manor <dmanor@redhat.com>
Reject subnet_cidr when virtual_network_cidr is empty (subnet requires a
parent VN per the resource hierarchy), handle netip.ParsePrefix errors
instead of discarding them, improve containment condition readability,
and add tests for subnet-without-VN and update-path validation.

Assisted-by: Claude Code <noreply@anthropic.com>
Signed-off-by: Dan Manor <dmanor@redhat.com>
Move defaults from a top-level field to spec.defaults following the
standard {id, metadata, spec, status} object pattern. Add protovalidate
CIDR format annotations and a CEL constraint requiring virtual_network_cidr
when subnet_cidr is set.

Assisted-by: Claude Code <noreply@anthropic.com>
Signed-off-by: Dan Manor <dmanor@redhat.com>
Assisted-by: Claude Code <noreply@anthropic.com>
Signed-off-by: Dan Manor <dmanor@redhat.com>
Replace the regex pattern that accepted invalid octets (e.g. 999.999.999.999/99)
with protovalidate's isIpPrefix(4, true) CEL function for proper IPv4 CIDR
validation. IPv6 is not supported for defaults per the default-networking EP.

Assisted-by: Claude Code <noreply@anthropic.com>
Signed-off-by: Dan Manor <dmanor@redhat.com>
Rename virtual_network_cidr/subnet_cidr to virtual_network_ipv4_cidr/
subnet_ipv4_cidr and add ipv6 counterparts to match the VirtualNetwork
and Subnet proto patterns. Use isIpPrefix CEL for proper CIDR validation
instead of loose regex. Add per-family subnet-requires-VN constraints.

Assisted-by: Claude Code <noreply@anthropic.com>
Signed-off-by: Dan Manor <dmanor@redhat.com>
@danmanor
danmanor force-pushed the feat/OSAC-2340-networkclass-defaults branch from 73097e6 to c15e48f Compare July 14, 2026 07:37
PR osac-project#866 removed the Cores field from ComputeInstanceSpec but didn't
update the test added by PR osac-project#783 in catalog_item_validation_test.go.
Replace with RunStrategy to test the same unlisted-field rejection.

Assisted-by: Claude Code <noreply@anthropic.com>
Signed-off-by: Dan Manor <dmanor@redhat.com>
@danmanor

Copy link
Copy Markdown
Contributor Author

/override ci/prow/unit

@openshift-ci

openshift-ci Bot commented Jul 14, 2026

Copy link
Copy Markdown

@danmanor: Overrode contexts on behalf of danmanor: ci/prow/unit

Details

In response to this:

/override ci/prow/unit

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

Tenant onboarding now creates five default resources: VN, IPv4 Subnet,
IPv6 Subnet, SecurityGroup, and optionally a NATGateway. The new boolean
field signals whether to auto-allocate an ExternalIP and create a
NATGateway for SNAT on the default VirtualNetwork.

Assisted-by: Claude Code <noreply@anthropic.com>
Signed-off-by: Dan Manor <dmanor@redhat.com>
@openshift-ci openshift-ci Bot added the lgtm label Jul 14, 2026
@openshift-ci

openshift-ci Bot commented Jul 14, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: danmanor, jhernand

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/servers/network_classes_server_test.go`:
- Around line 956-1241: Add IPv6 validation coverage alongside the existing IPv4
cases in the Defaults tests, using NetworkDefaults fields VirtualNetworkIpv6Cidr
and SubnetIpv6Cidr. Cover invalid IPv6 CIDR format, a subnet outside the virtual
network, and a subnet specified without its virtual network; assert creation
fails with the corresponding validation messages.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: osac-project/coderabbit/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: c1ffa6d2-ca05-4bae-8e61-e8c690752a5b

📥 Commits

Reviewing files that changed from the base of the PR and between 1585b4b and 63f5a17.

⛔ Files ignored due to path filters (5)
  • go.sum is excluded by !**/*.sum
  • internal/api/osac/private/v1/network_class_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/private/v1/network_class_type_protoopaque.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/public/v1/network_class_type.pb.go is excluded by !**/*.pb.go
  • internal/api/osac/public/v1/network_class_type_protoopaque.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (7)
  • go.mod
  • internal/servers/catalog_item_validation_test.go
  • internal/servers/network_classes_server.go
  • internal/servers/network_classes_server_test.go
  • internal/servers/private_network_classes_server.go
  • proto/private/osac/private/v1/network_class_type.proto
  • proto/public/osac/public/v1/network_class_type.proto
💤 Files with no reviewable changes (1)
  • go.mod

Comment on lines +956 to +1241
Describe("Defaults", func() {
validDefaults := func() *privatev1.NetworkDefaults {
return privatev1.NetworkDefaults_builder{
VirtualNetworkIpv4Cidr: "10.0.0.0/16",
SubnetIpv4Cidr: "10.0.1.0/24",
IngressRules: []*privatev1.SecurityRule{
privatev1.SecurityRule_builder{
Protocol: privatev1.Protocol_PROTOCOL_TCP,
PortFrom: new(int32(22)),
PortTo: new(int32(22)),
Ipv4Cidr: new("0.0.0.0/0"),
}.Build(),
},
EgressRules: []*privatev1.SecurityRule{
privatev1.SecurityRule_builder{
Protocol: privatev1.Protocol_PROTOCOL_ALL,
Ipv4Cidr: new("0.0.0.0/0"),
}.Build(),
},
}.Build()
}

createNetworkClassWithDefaults := func(defaults *privatev1.NetworkDefaults) *privatev1.NetworkClass {
response, err := privateServer.Create(ctx, privatev1.NetworkClassesCreateRequest_builder{
Object: privatev1.NetworkClass_builder{
Title: "NC with defaults",
ImplementationStrategy: "ovn-kubernetes",
FabricManager: "netris",
Spec: privatev1.NetworkClassSpec_builder{Defaults: defaults}.Build(),
}.Build(),
}.Build())
Expect(err).ToNot(HaveOccurred())
return response.GetObject()
}

It("Create with valid defaults persists and returns them", func() {
nc := createNetworkClassWithDefaults(validDefaults())

Expect(nc.GetSpec().GetDefaults()).ToNot(BeNil())
Expect(nc.GetSpec().GetDefaults().GetVirtualNetworkIpv4Cidr()).To(Equal("10.0.0.0/16"))
Expect(nc.GetSpec().GetDefaults().GetSubnetIpv4Cidr()).To(Equal("10.0.1.0/24"))
Expect(nc.GetSpec().GetDefaults().GetIngressRules()).To(HaveLen(1))
Expect(nc.GetSpec().GetDefaults().GetEgressRules()).To(HaveLen(1))
})

It("Get after create returns defaults", func() {
nc := createNetworkClassWithDefaults(validDefaults())

getResponse, err := privateServer.Get(ctx, privatev1.NetworkClassesGetRequest_builder{
Id: nc.GetId(),
}.Build())
Expect(err).ToNot(HaveOccurred())
retrieved := getResponse.GetObject()
Expect(retrieved.GetSpec().GetDefaults()).ToNot(BeNil())
Expect(retrieved.GetSpec().GetDefaults().GetVirtualNetworkIpv4Cidr()).To(Equal("10.0.0.0/16"))
Expect(retrieved.GetSpec().GetDefaults().GetSubnetIpv4Cidr()).To(Equal("10.0.1.0/24"))
Expect(retrieved.GetSpec().GetDefaults().GetIngressRules()).To(HaveLen(1))
Expect(retrieved.GetSpec().GetDefaults().GetIngressRules()[0].GetProtocol()).To(Equal(privatev1.Protocol_PROTOCOL_TCP))
Expect(retrieved.GetSpec().GetDefaults().GetIngressRules()[0].GetPortFrom()).To(BeNumerically("==", 22))
})

It("List after create returns defaults in items", func() {
createNetworkClassWithDefaults(validDefaults())

listResponse, err := privateServer.List(ctx, privatev1.NetworkClassesListRequest_builder{}.Build())
Expect(err).ToNot(HaveOccurred())
Expect(listResponse.GetItems()).To(HaveLen(1))
Expect(listResponse.GetItems()[0].GetSpec().GetDefaults()).ToNot(BeNil())
Expect(listResponse.GetItems()[0].GetSpec().GetDefaults().GetVirtualNetworkIpv4Cidr()).To(Equal("10.0.0.0/16"))
})

It("Update defaults via field mask replaces entire defaults", func() {
nc := createNetworkClassWithDefaults(validDefaults())

newDefaults := privatev1.NetworkDefaults_builder{
VirtualNetworkIpv4Cidr: "172.16.0.0/12",
SubnetIpv4Cidr: "172.16.1.0/24",
}.Build()

updateResponse, err := privateServer.Update(ctx, privatev1.NetworkClassesUpdateRequest_builder{
Object: privatev1.NetworkClass_builder{
Id: nc.GetId(),
Spec: privatev1.NetworkClassSpec_builder{Defaults: newDefaults}.Build(),
}.Build(),
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"spec.defaults"}},
}.Build())
Expect(err).ToNot(HaveOccurred())
updated := updateResponse.GetObject()
Expect(updated.GetSpec().GetDefaults().GetVirtualNetworkIpv4Cidr()).To(Equal("172.16.0.0/12"))
Expect(updated.GetSpec().GetDefaults().GetSubnetIpv4Cidr()).To(Equal("172.16.1.0/24"))
Expect(updated.GetSpec().GetDefaults().GetIngressRules()).To(BeEmpty())
Expect(updated.GetSpec().GetDefaults().GetEgressRules()).To(BeEmpty())
})

It("Update defaults to nil clears them", func() {
nc := createNetworkClassWithDefaults(validDefaults())

updateResponse, err := privateServer.Update(ctx, privatev1.NetworkClassesUpdateRequest_builder{
Object: privatev1.NetworkClass_builder{
Id: nc.GetId(),
}.Build(),
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"spec.defaults"}},
}.Build())
Expect(err).ToNot(HaveOccurred())
Expect(updateResponse.GetObject().GetSpec().GetDefaults()).To(BeNil())
})

It("Create without defaults succeeds", func() {
nc := createNetworkClass()
Expect(nc.GetSpec().GetDefaults()).To(BeNil())
})

It("Defaults with CIDRs only succeeds", func() {
defaults := privatev1.NetworkDefaults_builder{
VirtualNetworkIpv4Cidr: "10.0.0.0/16",
SubnetIpv4Cidr: "10.0.1.0/24",
}.Build()
nc := createNetworkClassWithDefaults(defaults)
Expect(nc.GetSpec().GetDefaults().GetVirtualNetworkIpv4Cidr()).To(Equal("10.0.0.0/16"))
Expect(nc.GetSpec().GetDefaults().GetIngressRules()).To(BeEmpty())
})

It("Defaults with rules only succeeds", func() {
defaults := privatev1.NetworkDefaults_builder{
IngressRules: []*privatev1.SecurityRule{
privatev1.SecurityRule_builder{
Protocol: privatev1.Protocol_PROTOCOL_TCP,
PortFrom: new(int32(443)),
PortTo: new(int32(443)),
Ipv4Cidr: new("0.0.0.0/0"),
}.Build(),
},
}.Build()
nc := createNetworkClassWithDefaults(defaults)
Expect(nc.GetSpec().GetDefaults().GetVirtualNetworkIpv4Cidr()).To(BeEmpty())
Expect(nc.GetSpec().GetDefaults().GetIngressRules()).To(HaveLen(1))
})

It("Invalid virtual_network_ipv4_cidr fails validation", func() {
defaults := privatev1.NetworkDefaults_builder{
VirtualNetworkIpv4Cidr: "not-a-cidr",
}.Build()
_, err := privateServer.Create(ctx, privatev1.NetworkClassesCreateRequest_builder{
Object: privatev1.NetworkClass_builder{
Title: "NC invalid VN CIDR",
ImplementationStrategy: "ovn-kubernetes",
FabricManager: "netris",
Spec: privatev1.NetworkClassSpec_builder{Defaults: defaults}.Build(),
}.Build(),
}.Build())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("virtual_network_ipv4_cidr"))
})

It("Invalid subnet_ipv4_cidr fails validation", func() {
defaults := privatev1.NetworkDefaults_builder{
VirtualNetworkIpv4Cidr: "10.0.0.0/16",
SubnetIpv4Cidr: "invalid",
}.Build()
_, err := privateServer.Create(ctx, privatev1.NetworkClassesCreateRequest_builder{
Object: privatev1.NetworkClass_builder{
Title: "NC invalid subnet CIDR",
ImplementationStrategy: "ovn-kubernetes",
FabricManager: "netris",
Spec: privatev1.NetworkClassSpec_builder{Defaults: defaults}.Build(),
}.Build(),
}.Build())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("subnet_ipv4_cidr"))
})

It("Subnet CIDR not within virtual_network_ipv4_cidr fails", func() {
defaults := privatev1.NetworkDefaults_builder{
VirtualNetworkIpv4Cidr: "10.0.0.0/16",
SubnetIpv4Cidr: "192.168.1.0/24",
}.Build()
_, err := privateServer.Create(ctx, privatev1.NetworkClassesCreateRequest_builder{
Object: privatev1.NetworkClass_builder{
Title: "NC subnet outside VN",
ImplementationStrategy: "ovn-kubernetes",
FabricManager: "netris",
Spec: privatev1.NetworkClassSpec_builder{Defaults: defaults}.Build(),
}.Build(),
}.Build())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("not within"))
})

It("Subnet CIDR without virtual_network_ipv4_cidr fails", func() {
defaults := privatev1.NetworkDefaults_builder{
SubnetIpv4Cidr: "10.0.1.0/24",
}.Build()
_, err := privateServer.Create(ctx, privatev1.NetworkClassesCreateRequest_builder{
Object: privatev1.NetworkClass_builder{
Title: "NC subnet without VN",
ImplementationStrategy: "ovn-kubernetes",
FabricManager: "netris",
Spec: privatev1.NetworkClassSpec_builder{Defaults: defaults}.Build(),
}.Build(),
}.Build())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("subnet_ipv4_cidr requires"))
Expect(err.Error()).To(ContainSubstring("virtual_network_ipv4_cidr"))
})

It("Ingress rule with invalid protocol fails", func() {
defaults := privatev1.NetworkDefaults_builder{
IngressRules: []*privatev1.SecurityRule{
privatev1.SecurityRule_builder{
Protocol: privatev1.Protocol_PROTOCOL_UNSPECIFIED,
Ipv4Cidr: new("0.0.0.0/0"),
}.Build(),
},
}.Build()
_, err := privateServer.Create(ctx, privatev1.NetworkClassesCreateRequest_builder{
Object: privatev1.NetworkClass_builder{
Title: "NC invalid rule protocol",
ImplementationStrategy: "ovn-kubernetes",
FabricManager: "netris",
Spec: privatev1.NetworkClassSpec_builder{Defaults: defaults}.Build(),
}.Build(),
}.Build())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("protocol is required"))
})

It("TCP rule without port range fails", func() {
defaults := privatev1.NetworkDefaults_builder{
IngressRules: []*privatev1.SecurityRule{
privatev1.SecurityRule_builder{
Protocol: privatev1.Protocol_PROTOCOL_TCP,
PortFrom: new(int32(22)),
Ipv4Cidr: new("0.0.0.0/0"),
}.Build(),
},
}.Build()
_, err := privateServer.Create(ctx, privatev1.NetworkClassesCreateRequest_builder{
Object: privatev1.NetworkClass_builder{
Title: "NC TCP missing port_to",
ImplementationStrategy: "ovn-kubernetes",
FabricManager: "netris",
Spec: privatev1.NetworkClassSpec_builder{Defaults: defaults}.Build(),
}.Build(),
}.Build())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("port"))
})

It("Rule with invalid CIDR fails", func() {
defaults := privatev1.NetworkDefaults_builder{
EgressRules: []*privatev1.SecurityRule{
privatev1.SecurityRule_builder{
Protocol: privatev1.Protocol_PROTOCOL_ALL,
Ipv4Cidr: new("not-a-cidr"),
}.Build(),
},
}.Build()
_, err := privateServer.Create(ctx, privatev1.NetworkClassesCreateRequest_builder{
Object: privatev1.NetworkClass_builder{
Title: "NC invalid rule CIDR",
ImplementationStrategy: "ovn-kubernetes",
FabricManager: "netris",
Spec: privatev1.NetworkClassSpec_builder{Defaults: defaults}.Build(),
}.Build(),
}.Build())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("CIDR"))
})

It("Update with invalid defaults via field mask fails validation", func() {
nc := createNetworkClassWithDefaults(validDefaults())

invalidDefaults := privatev1.NetworkDefaults_builder{
VirtualNetworkIpv4Cidr: "not-a-cidr",
}.Build()
_, err := privateServer.Update(ctx, privatev1.NetworkClassesUpdateRequest_builder{
Object: privatev1.NetworkClass_builder{
Id: nc.GetId(),
Spec: privatev1.NetworkClassSpec_builder{Defaults: invalidDefaults}.Build(),
}.Build(),
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"spec.defaults"}},
}.Build())
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("virtual_network_ipv4_cidr"))
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No test coverage for the IPv6 CIDR-pair validation branch.

All defaults tests exercise only virtual_network_ipv4_cidr/subnet_ipv4_cidr. validateNetworkDefaults also runs the IPv6 pair through validateDefaultCIDRPair (invalid format, containment, missing-VN cases) but none of that is asserted here — a regression in the IPv6 branch wouldn't be caught by this suite.

Add IPv6 counterparts to at least the "invalid CIDR", "not within", and "missing VN" cases (e.g. VirtualNetworkIpv6Cidr: "fd00::/48", SubnetIpv6Cidr: "fd00:0:0:1::/64").

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/servers/network_classes_server_test.go` around lines 956 - 1241, Add
IPv6 validation coverage alongside the existing IPv4 cases in the Defaults
tests, using NetworkDefaults fields VirtualNetworkIpv6Cidr and SubnetIpv6Cidr.
Cover invalid IPv6 CIDR format, a subnet outside the virtual network, and a
subnet specified without its virtual network; assert creation fails with the
corresponding validation messages.

@danmanor

Copy link
Copy Markdown
Contributor Author

/override ci/prow/unit

@openshift-ci

openshift-ci Bot commented Jul 14, 2026

Copy link
Copy Markdown

@danmanor: Overrode contexts on behalf of danmanor: ci/prow/unit

Details

In response to this:

/override ci/prow/unit

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@openshift-merge-bot
openshift-merge-bot Bot merged commit 751044e into osac-project:main Jul 14, 2026
12 checks passed

This branch was previously deployed

1 inactive deployment
e2e-test — 63f5a173 Deployed Jul 14, 2026 by danmanor via e2e-vmaas-full-install / e2e #445
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants