use crate::state::{
Ec2State, InternetGateway, NetworkAcl, NetworkAclAssoc, NetworkAclEntry, Route, RouteTable,
RouteTableAssociation, SecurityGroup, SecurityGroupRule, Subnet, Vpc,
};
const DEFAULT_VPC_CIDR: &str = "172.31.0.0/16";
const DEFAULT_AZ_SUFFIXES: [&str; 3] = ["a", "b", "c"];
pub(crate) fn deterministic_id(prefix: &str, account: &str, role: &str) -> String {
let seed = format!("{account}/{role}");
let h1 = fnv1a64(seed.as_bytes());
let h2 = fnv1a64(format!("{seed}/salt").as_bytes());
format!("{prefix}-{:016x}{:01x}", h1, h2 & 0xf)
}
fn fnv1a64(bytes: &[u8]) -> u64 {
let mut h: u64 = 0xcbf2_9ce4_8422_2325;
for b in bytes {
h ^= u64::from(*b);
h = h.wrapping_mul(0x0000_0100_0000_01b3);
}
h
}
fn az_id_prefix(region: &str) -> String {
let parts: Vec<&str> = region.split('-').collect();
if parts.len() == 3 && !parts[1].is_empty() {
format!(
"{}{}{}",
parts[0],
parts[1].chars().next().unwrap_or('x'),
parts[2]
)
} else {
region.replace('-', "")
}
}
pub(crate) fn default_vpc_id(account: &str) -> String {
deterministic_id("vpc", account, "default-vpc")
}
pub(crate) fn default_security_group_id(account: &str) -> String {
deterministic_id("sg", account, "default-sg")
}
pub(crate) fn bootstrap_default_network(state: &mut Ec2State) {
let account = state.account_id.clone();
let region = if state.region.is_empty() {
"us-east-1".to_string()
} else {
state.region.clone()
};
let vpc_id = default_vpc_id(&account);
let igw_id = deterministic_id("igw", &account, "default-igw");
let rtb_id = deterministic_id("rtb", &account, "default-rtb");
let acl_id = deterministic_id("acl", &account, "default-acl");
let sg_id = default_security_group_id(&account);
state.vpcs.insert(
vpc_id.clone(),
Vpc {
vpc_id: vpc_id.clone(),
cidr_block: DEFAULT_VPC_CIDR.to_string(),
state: "available".to_string(),
dhcp_options_id: "default".to_string(),
instance_tenancy: "default".to_string(),
is_default: true,
enable_dns_support: true,
enable_dns_hostnames: true,
cidr_associations: Vec::new(),
},
);
state.internet_gateways.insert(
igw_id.clone(),
InternetGateway {
internet_gateway_id: igw_id.clone(),
attachments: vec![(vpc_id.clone(), "available".to_string())],
},
);
let az_prefix = az_id_prefix(®ion);
let mut subnet_ids = Vec::new();
for (idx, suffix) in DEFAULT_AZ_SUFFIXES.iter().enumerate() {
let subnet_id = deterministic_id("subnet", &account, &format!("default-subnet-{suffix}"));
let az = format!("{region}{suffix}");
state.subnets.insert(
subnet_id.clone(),
Subnet {
subnet_id: subnet_id.clone(),
vpc_id: vpc_id.clone(),
cidr_block: format!("172.31.{}.0/20", idx * 16),
availability_zone: az,
availability_zone_id: format!("{az_prefix}-az{}", idx + 1),
state: "available".to_string(),
available_ip_address_count: 4091,
default_for_az: true,
map_public_ip_on_launch: true,
assign_ipv6_address_on_creation: false,
map_customer_owned_ip_on_launch: false,
enable_dns64: false,
private_dns_hostname_type: "ip-name".to_string(),
},
);
subnet_ids.push(subnet_id);
}
let mut associations = vec![RouteTableAssociation {
association_id: deterministic_id("rtbassoc", &account, "default-rtb-main"),
route_table_id: rtb_id.clone(),
subnet_id: None,
gateway_id: None,
main: true,
}];
for sid in &subnet_ids {
associations.push(RouteTableAssociation {
association_id: deterministic_id("rtbassoc", &account, &format!("default-rtb-{sid}")),
route_table_id: rtb_id.clone(),
subnet_id: Some(sid.clone()),
gateway_id: None,
main: false,
});
}
state.route_tables.insert(
rtb_id.clone(),
RouteTable {
route_table_id: rtb_id.clone(),
vpc_id: vpc_id.clone(),
routes: vec![
Route {
destination_cidr_block: Some(DEFAULT_VPC_CIDR.to_string()),
gateway_id: Some("local".to_string()),
..Default::default()
},
Route {
destination_cidr_block: Some("0.0.0.0/0".to_string()),
gateway_id: Some(igw_id.clone()),
..Default::default()
},
],
associations,
},
);
state.security_groups.insert(
sg_id.clone(),
SecurityGroup {
group_id: sg_id.clone(),
group_name: "default".to_string(),
description: "default VPC security group".to_string(),
vpc_id: vpc_id.clone(),
rules: vec![
SecurityGroupRule {
rule_id: deterministic_id("sgr", &account, "default-sg-ingress"),
group_id: sg_id.clone(),
is_egress: false,
ip_protocol: "-1".to_string(),
from_port: -1,
to_port: -1,
cidr_ipv4: None,
cidr_ipv6: None,
prefix_list_id: None,
referenced_group_id: Some(sg_id.clone()),
description: String::new(),
},
SecurityGroupRule {
rule_id: deterministic_id("sgr", &account, "default-sg-egress"),
group_id: sg_id.clone(),
is_egress: true,
ip_protocol: "-1".to_string(),
from_port: -1,
to_port: -1,
cidr_ipv4: Some("0.0.0.0/0".to_string()),
cidr_ipv6: None,
prefix_list_id: None,
referenced_group_id: None,
description: String::new(),
},
],
},
);
let nacl_associations = subnet_ids
.iter()
.map(|sid| NetworkAclAssoc {
association_id: deterministic_id("aclassoc", &account, &format!("default-acl-{sid}")),
subnet_id: sid.clone(),
})
.collect();
state.network_acls.insert(
acl_id.clone(),
NetworkAcl {
network_acl_id: acl_id.clone(),
vpc_id: vpc_id.clone(),
is_default: true,
entries: vec![
allow_all_entry(false),
deny_all_entry(false),
allow_all_entry(true),
deny_all_entry(true),
],
associations: nacl_associations,
},
);
}
fn allow_all_entry(egress: bool) -> NetworkAclEntry {
NetworkAclEntry {
rule_number: 100,
protocol: "-1".to_string(),
rule_action: "allow".to_string(),
egress,
cidr_block: Some("0.0.0.0/0".to_string()),
ipv6_cidr_block: None,
port_range: None,
icmp_type_code: None,
}
}
fn deny_all_entry(egress: bool) -> NetworkAclEntry {
NetworkAclEntry {
rule_number: 32767,
protocol: "-1".to_string(),
rule_action: "deny".to_string(),
egress,
cidr_block: Some("0.0.0.0/0".to_string()),
ipv6_cidr_block: None,
port_range: None,
icmp_type_code: None,
}
}
pub(crate) fn subnet_is_public(state: &Ec2State, subnet_id: &str) -> bool {
let Some(subnet) = state.subnets.get(subnet_id) else {
return false;
};
let explicit = state.route_tables.values().find(|rt| {
rt.associations
.iter()
.any(|a| a.subnet_id.as_deref() == Some(subnet_id))
});
let main = state
.route_tables
.values()
.find(|rt| rt.vpc_id == subnet.vpc_id && rt.associations.iter().any(|a| a.main));
let rt = explicit.or(main);
rt.map(route_table_has_igw_default).unwrap_or(false)
}
fn route_table_has_igw_default(rt: &RouteTable) -> bool {
rt.routes.iter().any(|r| {
r.destination_cidr_block.as_deref() == Some("0.0.0.0/0")
&& r.gateway_id
.as_deref()
.map(|g| g.starts_with("igw-"))
.unwrap_or(false)
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::state::Ec2State;
#[test]
fn deterministic_id_is_stable_and_shaped() {
let a = deterministic_id("vpc", "123456789012", "default-vpc");
let b = deterministic_id("vpc", "123456789012", "default-vpc");
assert_eq!(a, b);
assert!(a.starts_with("vpc-"));
let hex = a.strip_prefix("vpc-").unwrap();
assert_eq!(hex.len(), 17);
assert!(hex.chars().all(|c| c.is_ascii_hexdigit()));
}
#[test]
fn deterministic_id_varies_by_account_and_role() {
let base = deterministic_id("vpc", "111111111111", "default-vpc");
assert_ne!(base, deterministic_id("vpc", "222222222222", "default-vpc"));
assert_ne!(base, deterministic_id("vpc", "111111111111", "default-igw"));
}
#[test]
fn deterministic_id_is_region_independent() {
assert_eq!(
default_vpc_id("111111111111"),
deterministic_id("vpc", "111111111111", "default-vpc")
);
}
#[test]
fn az_id_prefix_matches_aws_shape() {
assert_eq!(az_id_prefix("us-east-1"), "use1");
assert_eq!(az_id_prefix("eu-west-2"), "euw2");
assert_eq!(az_id_prefix("ap-southeast-1"), "aps1");
}
#[test]
fn bootstrap_creates_full_default_topology() {
let state = Ec2State::new("123456789012", "us-east-1");
assert_eq!(state.vpcs.len(), 1);
let vpc = state.vpcs.values().next().unwrap();
assert!(vpc.is_default);
assert_eq!(vpc.cidr_block, "172.31.0.0/16");
assert_eq!(state.subnets.len(), DEFAULT_AZ_SUFFIXES.len());
assert!(state.subnets.values().all(|s| s.default_for_az));
assert!(state.subnets.values().all(|s| s.map_public_ip_on_launch));
assert_eq!(state.internet_gateways.len(), 1);
let igw = state.internet_gateways.values().next().unwrap();
assert_eq!(igw.attachments[0].0, vpc.vpc_id);
let sg = state.security_groups.values().next().unwrap();
assert_eq!(sg.group_name, "default");
assert!(state.network_acls.values().next().unwrap().is_default);
}
#[test]
fn default_subnets_are_public() {
let state = Ec2State::new("123456789012", "us-east-1");
for sid in state.subnets.keys() {
assert!(
subnet_is_public(&state, sid),
"subnet {sid} should be public"
);
}
}
#[test]
fn ids_match_across_fresh_states() {
let a = Ec2State::new("123456789012", "us-east-1");
let b = Ec2State::new("123456789012", "us-east-1");
let a_vpc: Vec<_> = a.vpcs.keys().collect();
let b_vpc: Vec<_> = b.vpcs.keys().collect();
assert_eq!(a_vpc, b_vpc);
assert_eq!(a_vpc[0], &default_vpc_id("123456789012"));
}
#[test]
fn default_vpc_id_agrees_across_regions() {
let read_path = Ec2State::new("123456789012", "eu-west-1");
let persisted = Ec2State::new("123456789012", "us-east-1");
let read_vpc = read_path.vpcs.keys().next().unwrap();
let persisted_vpc = persisted.vpcs.keys().next().unwrap();
assert_eq!(read_vpc, persisted_vpc);
let read_subnets: std::collections::BTreeSet<_> = read_path.subnets.keys().collect();
let persisted_subnets: std::collections::BTreeSet<_> = persisted.subnets.keys().collect();
assert_eq!(read_subnets, persisted_subnets);
}
}