Skip to content

upcoming: [M3-9424] - Review nodebalancers validation schemas #11910

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

harsh-akamai
Copy link
Contributor

@harsh-akamai harsh-akamai commented Mar 24, 2025

Description 📝

Update validation schemas for the changes in POST endpoints in /v4/nodebalancers (& /v4beta/nodebalancers) for NB-VPC Integration

How to test 🧪

Verification steps

  • Verify the changes match the APISpec linked in the parent ticket
Author Checklists

As an Author, to speed up the review process, I considered 🤔

👀 Doing a self review
❔ Our contribution guidelines
🤏 Splitting feature into small PRs
➕ Adding a changeset
🧪 Providing/improving test coverage
🔐 Removing all sensitive information from the code and PR description
🚩 Using a feature flag to protect the release
👣 Providing comprehensive reproduction steps
📑 Providing or updating our documentation
🕛 Scheduling a pair reviewing session
📱 Providing mobile support
♿ Providing accessibility support


  • I have read and considered all applicable items listed above.

As an Author, before moving this PR from Draft to Open, I confirmed ✅

  • All unit tests are passing
  • TypeScript compilation succeeded without errors
  • Code passes all linting rules

@harsh-akamai harsh-akamai marked this pull request as ready for review March 24, 2025 12:51
@harsh-akamai harsh-akamai requested a review from a team as a code owner March 24, 2025 12:51
@harsh-akamai harsh-akamai requested review from cpathipa and coliu-akamai and removed request for a team March 24, 2025 12:51
@harsh-akamai harsh-akamai added the NB-VPC Relating to NodeBalancer-VPC integration label Mar 24, 2025
@@ -97,6 +97,7 @@ export const vpcsValidateIP = ({
}

if (isIPv6) {
// @todo update the IPv6 prefix if required for NB-VPC integration
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// @todo update the IPv6 prefix if required for NB-VPC integration
// @TODO NB-VPC: update the IPv6 prefix if required for NB-VPC integration

ipv6_range: string() //TBD
.notRequired()
.nullable()
.matches(PRIVATE_IP_REGEX, 'Must be a valid private IPv6 address.')
Copy link
Contributor

Choose a reason for hiding this comment

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

IPv6 has a different format than IPv4, so an IPv6-specific regex will need to be used and we should revert the name change to the existing regex variable

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Corrected!

.required('Subnet ID is required.'),
ipv4_range: string()
.typeError('IPv4 address is required.')
.required('IPv4 address is required.')
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this the case? I would imagine it could have either an IPv4 range or IPv6 range, or both, but not neither. Assuming what I described is accurate, we could use similar logic as what was done for createSubnetSchemaWithIPv6 in vpcs.schema.ts

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds right. I've made changes to adapt to the logic present in createSubnetSchemaWithIPv6

@dwiley-akamai
Copy link
Contributor

Can we also get a validation changeset?

…dpoints in /v4/nodebalancers (& /v4beta/nodebalancers) for NB-VPC Integration

const PORT_WARNING = 'Port must be between 1 and 65535.';
const LABEL_WARNING = 'Label must be between 3 and 32 characters.';

export const PRIVATE_IPv4_REGEX = /^10\.|^172\.1[6-9]\.|^172\.2[0-9]\.|^172\.3[0-1]\.|^192\.168\.|^fd/;
export const PRIVATE_IPv6_REGEX = /^(fc|fd)\./;
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this regex will match valid IPv6 addresses. We should test the regex expressions with a tool like regex101.com to ensure it works as intended. We'll also want it to account for the mask since it's an IPv6 range

Copy link
Contributor

Choose a reason for hiding this comment

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

The sample IPv6 range fails the regex pattern:

Screenshot 2025-04-10 at 4 38 18 PM

It may be that a comprehensive regex for IPv6 may be challenging or too complex for our purposes, but if that is the case we should note in a comment that we're only testing the beginning letters of it rather than the entire IP.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Noted. I've made changes to the regex and I've also added a comment. Thanks for catching this 🙌

then: (schema) =>
schema
.required(IP_EITHER_BOTH_NOT_NEITHER)
.matches(PRIVATE_IPv4_REGEX, 'Must be a valid private IPv4 address.')
Copy link
Contributor

Choose a reason for hiding this comment

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

let's store the 'Must be a valid private IPv4 address.' string in a constant since we use it several times

Comment on lines 264 to 332
const createNodeBalancerVPCsSchema = object({
subnet_id: number()
.typeError('Subnet ID must be a number.')
.required('Subnet ID is required.'),
ipv4_range: string().when('ipv6_range', {
is: (value: unknown) =>
value === '' || value === null || value === undefined,
then: (schema) =>
schema
.required(IP_EITHER_BOTH_NOT_NEITHER)
.matches(PRIVATE_IPv4_REGEX, 'Must be a valid private IPv4 address.')
.test({
name: 'IPv4 CIDR format',
message: 'The IPv4 range must be in CIDR format.',
test: (value) =>
vpcsValidateIP({
value,
shouldHaveIPMask: true,
mustBeIPMask: false,
}),
}),
otherwise: (schema) =>
lazy((value: string | undefined) => {
switch (typeof value) {
case 'undefined':
return schema.notRequired().nullable();

case 'string':
return schema
.notRequired()
.matches(
PRIVATE_IPv4_REGEX,
'Must be a valid private IPv4 address.'
)
.test({
name: 'IPv4 CIDR format',
message: 'The IPv4 range must be in CIDR format.',
test: (value) =>
vpcsValidateIP({
value,
shouldHaveIPMask: true,
mustBeIPMask: false,
}),
});

default:
return schema.notRequired().nullable();
}
}),
}),
ipv6_range: string().when('ipv6_range', {
is: (value: unknown) =>
value === '' || value === null || value === undefined,
then: (schema) =>
schema
.required(IP_EITHER_BOTH_NOT_NEITHER)
.matches(PRIVATE_IPv6_REGEX, 'Must be a valid private IPv6 address.')
.test({
name: 'valid-ipv6-range',
message: 'Must be a valid IPv6 range, e.g. 2001:db8:abcd:0012::0/64.',
test: (value) =>
vpcsValidateIP({
value,
shouldHaveIPMask: true,
mustBeIPMask: false,
}),
}),
}),
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Right now, Cloud Manager doesn't load and we get the following console error:

Screenshot 2025-04-04 at 1 20 29 PM

Suggested change
const createNodeBalancerVPCsSchema = object({
subnet_id: number()
.typeError('Subnet ID must be a number.')
.required('Subnet ID is required.'),
ipv4_range: string().when('ipv6_range', {
is: (value: unknown) =>
value === '' || value === null || value === undefined,
then: (schema) =>
schema
.required(IP_EITHER_BOTH_NOT_NEITHER)
.matches(PRIVATE_IPv4_REGEX, 'Must be a valid private IPv4 address.')
.test({
name: 'IPv4 CIDR format',
message: 'The IPv4 range must be in CIDR format.',
test: (value) =>
vpcsValidateIP({
value,
shouldHaveIPMask: true,
mustBeIPMask: false,
}),
}),
otherwise: (schema) =>
lazy((value: string | undefined) => {
switch (typeof value) {
case 'undefined':
return schema.notRequired().nullable();
case 'string':
return schema
.notRequired()
.matches(
PRIVATE_IPv4_REGEX,
'Must be a valid private IPv4 address.'
)
.test({
name: 'IPv4 CIDR format',
message: 'The IPv4 range must be in CIDR format.',
test: (value) =>
vpcsValidateIP({
value,
shouldHaveIPMask: true,
mustBeIPMask: false,
}),
});
default:
return schema.notRequired().nullable();
}
}),
}),
ipv6_range: string().when('ipv6_range', {
is: (value: unknown) =>
value === '' || value === null || value === undefined,
then: (schema) =>
schema
.required(IP_EITHER_BOTH_NOT_NEITHER)
.matches(PRIVATE_IPv6_REGEX, 'Must be a valid private IPv6 address.')
.test({
name: 'valid-ipv6-range',
message: 'Must be a valid IPv6 range, e.g. 2001:db8:abcd:0012::0/64.',
test: (value) =>
vpcsValidateIP({
value,
shouldHaveIPMask: true,
mustBeIPMask: false,
}),
}),
}),
});
const createNodeBalancerVPCsSchema = object().shape(
{
subnet_id: number()
.typeError('Subnet ID must be a number.')
.required('Subnet ID is required.'),
ipv4_range: string().when('ipv6_range', {
is: (value: unknown) =>
value === '' || value === null || value === undefined,
then: (schema) =>
schema
.required(IP_EITHER_BOTH_NOT_NEITHER)
.matches(PRIVATE_IPv4_REGEX, 'Must be a valid private IPv4 address.')
.test({
name: 'IPv4 CIDR format',
message: 'The IPv4 range must be in CIDR format.',
test: (value) =>
vpcsValidateIP({
value,
shouldHaveIPMask: true,
mustBeIPMask: false,
}),
}),
otherwise: (schema) =>
lazy((value: string | undefined) => {
switch (typeof value) {
case 'undefined':
return schema.notRequired().nullable();
case 'string':
return schema
.notRequired()
.matches(
PRIVATE_IPv4_REGEX,
'Must be a valid private IPv4 address.'
)
.test({
name: 'IPv4 CIDR format',
message: 'The IPv4 range must be in CIDR format.',
test: (value) =>
vpcsValidateIP({
value,
shouldHaveIPMask: true,
mustBeIPMask: false,
}),
});
default:
return schema.notRequired().nullable();
}
}),
}),
ipv6_range: string().when('ipv4_range', {
is: (value: unknown) =>
value === '' || value === null || value === undefined,
then: (schema) =>
schema
.required(IP_EITHER_BOTH_NOT_NEITHER)
.matches(PRIVATE_IPv6_REGEX, 'Must be a valid private IPv6 address.')
.test({
name: 'valid-ipv6-range',
message:
'Must be a valid IPv6 range, e.g. 2001:db8:abcd:0012::0/64.',
test: (value) =>
vpcsValidateIP({
value,
shouldHaveIPMask: true,
mustBeIPMask: false,
}),
}),
}),
},
[
['ipv4_range', 'ipv6_range'],
['ipv6_range', 'ipv4_range'],
]
);

The array at the end prevents the cyclic dependency error.

@github-project-automation github-project-automation bot moved this from Review to Changes Requested in Cloud Manager Apr 4, 2025
Copy link
Contributor

@cpathipa cpathipa left a comment

Choose a reason for hiding this comment

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

@harsh-akamai LGTM! Can you resolve the ESLint warning.

@@ -1,9 +1,12 @@
import { array, boolean, mixed, number, object, string } from 'yup';
import { array, boolean, lazy, mixed, number, object, string } from 'yup';
Copy link
Contributor

Choose a reason for hiding this comment

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

@harsh-akamai Can you look into this ESLint warnings.

@harsh-akamai harsh-akamai added the Add'tl Approval Needed Waiting on another approval! label Apr 14, 2025
@harsh-akamai harsh-akamai requested a review from a team as a code owner April 16, 2025 06:43
@harsh-akamai harsh-akamai requested review from dmcintyr-akamai and removed request for a team April 16, 2025 06:43
@harsh-akamai harsh-akamai force-pushed the M3-9424-review-nb-vpc-validation-schema branch from 211feb4 to 291f89d Compare April 16, 2025 06:48
@harsh-akamai harsh-akamai removed the Add'tl Approval Needed Waiting on another approval! label Apr 16, 2025
.test(
'unique subnet IDs',
'Subnet IDs must be unique.',
function (value?: any[] | null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

No need to include a type annotation as yup will infer one for you!

Suggested change
function (value?: any[] | null) {
function (value) {

@harsh-akamai harsh-akamai requested a review from cpathipa April 17, 2025 11:35
Copy link
Contributor

@dwiley-akamai dwiley-akamai left a comment

Choose a reason for hiding this comment

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

Cyclic dependency error mentioned previously is resolved ✅
No issues observed creating NBs ✅
Code review ✅

.matches(PRIVATE_IPV4_REGEX, PRIVATE_IPV4_WARNING),

subnet_id: number().when('vpcs', {
is: (vpcs: typeof createNodeBalancerVPCsSchema) => vpcs !== undefined,
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need this to be:

Suggested change
is: (vpcs: typeof createNodeBalancerVPCsSchema) => vpcs !== undefined,
is: (vpcs: typeof createNodeBalancerVPCsSchema[]) => vpcs !== undefined,

since the vpcs field is an array?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops! Missed this. Thanks!

@github-project-automation github-project-automation bot moved this from Changes Requested to Approved in Cloud Manager Apr 24, 2025
@hkhalil-akamai hkhalil-akamai added the Approved Multiple approvals and ready to merge! label Apr 24, 2025
@linode-gh-bot
Copy link
Collaborator

Cloud Manager UI test results

🎉 567 passing tests on test run #13 ↗︎

❌ Failing✅ Passing↪️ Skipped🕐 Duration
0 Failing567 Passing5 Skipped104m 45s

@harsh-akamai harsh-akamai merged commit ea6c55e into linode:develop Apr 25, 2025
35 checks passed
@github-project-automation github-project-automation bot moved this from Approved to Merged in Cloud Manager Apr 25, 2025
@harsh-akamai harsh-akamai deleted the M3-9424-review-nb-vpc-validation-schema branch April 25, 2025 09:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Approved Multiple approvals and ready to merge! NB-VPC Relating to NodeBalancer-VPC integration
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

5 participants