Skip to content

Commit bc3f12c

Browse files
committed
WIP: implementing NIC update
1 parent 2fe0ed7 commit bc3f12c

File tree

2 files changed

+129
-52
lines changed

2 files changed

+129
-52
lines changed

nexus/tests/integration_tests/instances.rs

Lines changed: 85 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ use nexus_test_utils::wait_for_producer;
4444
use nexus_types::external_api::params::IpAssignment;
4545
use nexus_types::external_api::params::PrivateIpStackCreate;
4646
use nexus_types::external_api::params::PrivateIpv4StackCreate;
47+
use nexus_types::external_api::params::PrivateIpv4StackUpdate;
4748
use nexus_types::external_api::params::PrivateIpv6StackCreate;
4849
use nexus_types::external_api::params::SshKeyCreate;
4950
use nexus_types::external_api::shared::IpKind;
@@ -3568,7 +3569,7 @@ async fn test_instance_update_network_interfaces(
35683569
description: Some(new_description.clone()),
35693570
},
35703571
primary: false,
3571-
transit_ips: vec![],
3572+
..Default::default()
35723573
};
35733574

35743575
// Verify we fail to update the NIC when the instance is running
@@ -3655,7 +3656,7 @@ async fn test_instance_update_network_interfaces(
36553656
description: None,
36563657
},
36573658
primary: true,
3658-
transit_ips: vec![],
3659+
..Default::default()
36593660
};
36603661
let updated_primary_iface1 = NexusRequest::object_put(
36613662
client,
@@ -3753,7 +3754,7 @@ async fn test_instance_update_network_interfaces(
37533754
description: None,
37543755
},
37553756
primary: true,
3756-
transit_ips: vec![],
3757+
..Default::default()
37573758
};
37583759
let new_primary_iface = NexusRequest::object_put(
37593760
client,
@@ -3849,6 +3850,21 @@ async fn test_instance_update_network_interfaces(
38493850
assert_eq!(iface.identity.name, if_params[0].identity.name);
38503851
}
38513852

3853+
#[nexus_test]
3854+
async fn cannot_make_new_primary_nic_lacking_ip_stack_for_external_addresses(
3855+
_cptestctx: &ControlPlaneTestContext,
3856+
) {
3857+
todo!()
3858+
}
3859+
3860+
3861+
#[nexus_test]
3862+
async fn cannot_remove_ip_stack_with_outstanding_external_ips_of_the_same_version(
3863+
_cptestctx: &ControlPlaneTestContext,
3864+
) {
3865+
todo!()
3866+
}
3867+
38523868
#[nexus_test]
38533869
async fn can_add_instance_network_interface_ip_stack(
38543870
_cptestctx: &ControlPlaneTestContext,
@@ -3906,11 +3922,13 @@ async fn test_instance_update_network_interface_transit_ips(
39063922
description: None,
39073923
},
39083924
primary: false,
3909-
transit_ips: vec![
3910-
"10.0.0.0/9".parse().unwrap(),
3911-
"10.128.0.0/9".parse().unwrap(),
3912-
"1.1.1.1/32".parse().unwrap(),
3913-
],
3925+
ipv4: PrivateIpv4StackUpdate::Modify {
3926+
transit_ips: vec![
3927+
"10.0.0.0/9".parse().unwrap(),
3928+
"10.128.0.0/9".parse().unwrap(),
3929+
"1.1.1.1/32".parse().unwrap(),
3930+
],
3931+
},
39143932
};
39153933

39163934
// Verify that a selection of transit IPs (mixture of private and global
@@ -3926,12 +3944,14 @@ async fn test_instance_update_network_interface_transit_ips(
39263944
// Non-canonical form (e.g., host identifier is nonzero) subnets should
39273945
// be rejected.
39283946
let with_extra_bits = params::InstanceNetworkInterfaceUpdate {
3929-
transit_ips: vec![
3930-
"10.0.0.0/9".parse().unwrap(),
3931-
"10.128.0.0/9".parse().unwrap(),
3932-
// Invalid vvv
3933-
"172.30.255.255/24".parse().unwrap(),
3934-
],
3947+
ipv4: PrivateIpv4StackUpdate::Modify {
3948+
transit_ips: vec![
3949+
"10.0.0.0/9".parse().unwrap(),
3950+
"10.128.0.0/9".parse().unwrap(),
3951+
// Invalid vvv
3952+
"172.30.255.255/24".parse().unwrap(),
3953+
],
3954+
},
39353955
..base_update.clone()
39363956
};
39373957
let err = object_put_error(
@@ -3948,11 +3968,13 @@ async fn test_instance_update_network_interface_transit_ips(
39483968

39493969
// Multicast IP blocks should be rejected.
39503970
let with_mc1 = params::InstanceNetworkInterfaceUpdate {
3951-
transit_ips: vec![
3952-
"10.0.0.0/9".parse().unwrap(),
3953-
"10.128.0.0/9".parse().unwrap(),
3954-
"224.0.0.0/4".parse().unwrap(),
3955-
],
3971+
ipv4: PrivateIpv4StackUpdate::Modify {
3972+
transit_ips: vec![
3973+
"10.0.0.0/9".parse().unwrap(),
3974+
"10.128.0.0/9".parse().unwrap(),
3975+
"224.0.0.0/4".parse().unwrap(),
3976+
],
3977+
},
39563978
..base_update.clone()
39573979
};
39583980
let err = object_put_error(
@@ -3968,11 +3990,13 @@ async fn test_instance_update_network_interface_transit_ips(
39683990
);
39693991

39703992
let with_mc2 = params::InstanceNetworkInterfaceUpdate {
3971-
transit_ips: vec![
3972-
"10.0.0.0/9".parse().unwrap(),
3973-
"10.128.0.0/9".parse().unwrap(),
3974-
"230.20.20.128/32".parse().unwrap(),
3975-
],
3993+
ipv4: PrivateIpv4StackUpdate::Modify {
3994+
transit_ips: vec![
3995+
"10.0.0.0/9".parse().unwrap(),
3996+
"10.128.0.0/9".parse().unwrap(),
3997+
"230.20.20.128/32".parse().unwrap(),
3998+
],
3999+
},
39764000
..base_update.clone()
39774001
};
39784002
let err = object_put_error(
@@ -3989,11 +4013,13 @@ async fn test_instance_update_network_interface_transit_ips(
39894013

39904014
// Loopback ranges.
39914015
let with_lo1 = params::InstanceNetworkInterfaceUpdate {
3992-
transit_ips: vec![
3993-
"10.0.0.0/9".parse().unwrap(),
3994-
"10.128.0.0/9".parse().unwrap(),
3995-
"127.42.77.0/24".parse().unwrap(),
3996-
],
4016+
ipv4: PrivateIpv4StackUpdate::Modify {
4017+
transit_ips: vec![
4018+
"10.0.0.0/9".parse().unwrap(),
4019+
"10.128.0.0/9".parse().unwrap(),
4020+
"127.42.77.0/24".parse().unwrap(),
4021+
],
4022+
},
39974023
..base_update.clone()
39984024
};
39994025
let err = object_put_error(
@@ -4009,11 +4035,13 @@ async fn test_instance_update_network_interface_transit_ips(
40094035
);
40104036

40114037
let with_lo2 = params::InstanceNetworkInterfaceUpdate {
4012-
transit_ips: vec![
4013-
"10.0.0.0/9".parse().unwrap(),
4014-
"10.128.0.0/9".parse().unwrap(),
4015-
"127.0.0.1/32".parse().unwrap(),
4016-
],
4038+
ipv4: PrivateIpv4StackUpdate::Modify {
4039+
transit_ips: vec![
4040+
"10.0.0.0/9".parse().unwrap(),
4041+
"10.128.0.0/9".parse().unwrap(),
4042+
"127.0.0.1/32".parse().unwrap(),
4043+
],
4044+
},
40174045
..base_update.clone()
40184046
};
40194047
let err = object_put_error(
@@ -4027,11 +4055,13 @@ async fn test_instance_update_network_interface_transit_ips(
40274055

40284056
// Overlapping IP ranges should be rejected, as should identical ranges.
40294057
let with_dup1 = params::InstanceNetworkInterfaceUpdate {
4030-
transit_ips: vec![
4031-
"10.0.0.0/9".parse().unwrap(),
4032-
"10.128.0.0/9".parse().unwrap(),
4033-
"10.0.0.0/9".parse().unwrap(),
4034-
],
4058+
ipv4: PrivateIpv4StackUpdate::Modify {
4059+
transit_ips: vec![
4060+
"10.0.0.0/9".parse().unwrap(),
4061+
"10.128.0.0/9".parse().unwrap(),
4062+
"10.0.0.0/9".parse().unwrap(),
4063+
],
4064+
},
40354065
..base_update.clone()
40364066
};
40374067
let err = object_put_error(
@@ -4047,11 +4077,13 @@ async fn test_instance_update_network_interface_transit_ips(
40474077
);
40484078

40494079
let with_dup2 = params::InstanceNetworkInterfaceUpdate {
4050-
transit_ips: vec![
4051-
"10.0.0.0/9".parse().unwrap(),
4052-
"10.128.0.0/9".parse().unwrap(),
4053-
"10.128.32.0/24".parse().unwrap(),
4054-
],
4080+
ipv4: PrivateIpv4StackUpdate::Modify {
4081+
transit_ips: vec![
4082+
"10.0.0.0/9".parse().unwrap(),
4083+
"10.128.0.0/9".parse().unwrap(),
4084+
"10.128.32.0/24".parse().unwrap(),
4085+
],
4086+
},
40554087
..base_update.clone()
40564088
};
40574089
let err = object_put_error(
@@ -4068,10 +4100,12 @@ async fn test_instance_update_network_interface_transit_ips(
40684100

40694101
// Verify that we also catch more specific CIDRs appearing sooner in the list.
40704102
let with_dup3 = params::InstanceNetworkInterfaceUpdate {
4071-
transit_ips: vec![
4072-
"10.20.20.0/30".parse().unwrap(),
4073-
"10.0.0.0/8".parse().unwrap(),
4074-
],
4103+
ipv4: PrivateIpv4StackUpdate::Modify {
4104+
transit_ips: vec![
4105+
"10.20.20.0/30".parse().unwrap(),
4106+
"10.0.0.0/8".parse().unwrap(),
4107+
],
4108+
},
40754109
..base_update.clone()
40764110
};
40774111
let err = object_put_error(
@@ -4097,7 +4131,9 @@ async fn test_instance_update_network_interface_transit_ips(
40974131
// As a final sanity test, we can still effectively remove spoof checking
40984132
// using the unspecified network address.
40994133
let allow_all = params::InstanceNetworkInterfaceUpdate {
4100-
transit_ips: vec!["0.0.0.0/0".parse().unwrap()],
4134+
ipv4: PrivateIpv4StackUpdate::Modify {
4135+
transit_ips: vec!["0.0.0.0/0".parse().unwrap()],
4136+
},
41014137
..base_update.clone()
41024138
};
41034139

nexus/types/src/external_api/params.rs

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,44 @@ pub struct InstanceNetworkInterfaceCreate {
957957
pub ip_config: PrivateIpStackCreate,
958958
}
959959

960+
#[derive(Clone, Debug, Default, Deserialize, JsonSchema, Serialize)]
961+
#[serde(rename_all = "snake_case", tag = "type", content = "value")]
962+
pub enum PrivateIpv4StackUpdate {
963+
#[default]
964+
NoChange,
965+
Remove,
966+
Add(PrivateIpv4StackCreate),
967+
Modify { transit_ips: Vec<Ipv4Net> },
968+
}
969+
970+
#[derive(Clone, Debug, Default, Deserialize, JsonSchema, Serialize)]
971+
#[serde(rename_all = "snake_case", tag = "type", content = "value")]
972+
pub enum PrivateIpv6StackUpdate {
973+
#[default]
974+
NoChange,
975+
Remove,
976+
Add(PrivateIpv6StackCreate),
977+
Modify { transit_ips: Vec<Ipv6Net> },
978+
}
979+
980+
// Do nothing to a specific IP stack
981+
// Remove a specific IP stack
982+
// Add a specific IP stack
983+
// Modify the transit IPs of a stack
984+
//
985+
// Independently for IPv6 / IPv4, though we need to do a bunch of validation in
986+
// the database queries for that:
987+
//
988+
// - If adding, don't already have a stack of the same version (UPDATE ... SET
989+
// .. WHERE ip IS NULL, basically)
990+
// - If we're removing, we have a stack of the _other_ version so that the NIC
991+
// is still valid, although the CHECK constraint we have should take care of
992+
// that.
993+
//
994+
// TODO(ben): We need to beef up the check for making a new primary NIC. That
995+
// has to ensure that we have private IP stacks for all the external addresses
996+
// the instance might have.
997+
960998
/// Parameters for updating an `InstanceNetworkInterface`
961999
///
9621000
/// Note that modifying IP addresses for an interface is not yet supported, a
@@ -982,10 +1020,13 @@ pub struct InstanceNetworkInterfaceUpdate {
9821020
#[serde(default)]
9831021
pub primary: bool,
9841022

985-
/// A set of additional networks that this interface may send and
986-
/// receive traffic on.
1023+
/// Update the interface's VPC-private IPv4 stack.
1024+
#[serde(default)]
1025+
pub ipv4: PrivateIpv4StackUpdate,
1026+
1027+
/// Update the interface's VPC-private IPv6 stack.
9871028
#[serde(default)]
988-
pub transit_ips: Vec<IpNet>,
1029+
pub ipv6: PrivateIpv6StackUpdate,
9891030
}
9901031

9911032
/// How a VPC-private IP address is assigned to a network interface.

0 commit comments

Comments
 (0)