From 2e42c85d54cc3d3f81c5286b92945acdd1fd38f8 Mon Sep 17 00:00:00 2001 From: Jeff Grunewald Date: Fri, 19 Jan 2024 14:50:32 -0500 Subject: [PATCH] allow reactivating eui pairs, devaddr ranges and skfs (#713) * allow reactivating eui pairs, devaddr ranges and skfs * fix update skf query * test reactivation of lns route records --- iot_config/src/route.rs | 6 +- iot_config/tests/route_service.rs | 107 ++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 3 deletions(-) diff --git a/iot_config/src/route.rs b/iot_config/src/route.rs index 2d39f07f7..6996dc785 100644 --- a/iot_config/src/route.rs +++ b/iot_config/src/route.rs @@ -247,7 +247,7 @@ async fn insert_euis( const EUI_INSERT_VALS: &str = " insert into route_eui_pairs (route_id, app_eui, dev_eui) "; const EUI_INSERT_ON_CONF: &str = - " on conflict (route_id, app_eui, dev_eui) do nothing returning * "; + " on conflict (route_id, app_eui, dev_eui) do update set deleted = false returning * "; let mut query_builder: sqlx::QueryBuilder = sqlx::QueryBuilder::new(EUI_INSERT_VALS); query_builder @@ -358,7 +358,7 @@ async fn insert_devaddr_ranges( const DEVADDR_RANGE_INSERT_VALS: &str = " insert into route_devaddr_ranges (route_id, start_addr, end_addr) "; const DEVADDR_RANGE_INSERT_ON_CONF: &str = - " on conflict (route_id, start_addr, end_addr) do nothing returning * "; + " on conflict (route_id, start_addr, end_addr) do update set deleted = false returning * "; let mut query_builder: sqlx::QueryBuilder = sqlx::QueryBuilder::new(DEVADDR_RANGE_INSERT_VALS); query_builder @@ -799,7 +799,7 @@ async fn insert_skfs(skfs: &[Skf], db: impl sqlx::PgExecutor<'_>) -> anyhow::Res // instead of ignoring. this avoids reconciliation bugs attempting to update fields of an // existing skf from succeeding on the HPRs but being ignored by the Config Service const SKF_INSERT_CONFLICT: &str = - " on conflict (route_id, devaddr, session_key) do update set max_copies = excluded.max_copies returning * "; + " on conflict (route_id, devaddr, session_key) do update set max_copies = excluded.max_copies, deleted = false returning * "; let mut query_builder: sqlx::QueryBuilder = sqlx::QueryBuilder::new(SKF_INSERT_VALS); diff --git a/iot_config/tests/route_service.rs b/iot_config/tests/route_service.rs index 8deca131e..02bac08f5 100644 --- a/iot_config/tests/route_service.rs +++ b/iot_config/tests/route_service.rs @@ -231,6 +231,78 @@ async fn stream_only_sends_data_modified_since(pool: Pool) { .await; } +#[sqlx::test] +async fn stream_updates_with_deactivate_reactivate(pool: Pool) { + let signing_keypair = Arc::new(generate_keypair()); + let admin_keypair = generate_keypair(); + let client_keypair = generate_keypair(); + + let port = get_port(); + + let auth_cache = create_auth_cache( + admin_keypair.public_key().clone(), + client_keypair.public_key().clone(), + &pool, + ) + .await; + + let _handle = start_server(port, signing_keypair, auth_cache, pool.clone()).await; + let mut client = connect_client(port).await; + + let org_res_v1 = create_org(port, &admin_keypair).await; + + let proto::OrgResV1 { org: Some(org), .. } = org_res_v1 else { + panic!("invalid OrgResV1") + }; + + let response = client + .stream(route_stream_req_v1(&client_keypair, 0)) + .await + .expect("stream request"); + let mut response_stream = response.into_inner(); + + let route = create_route(&mut client, &org, &admin_keypair).await; + + create_euis(&mut client, &route, vec![(200, 201)], &admin_keypair).await; + + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + + delete_euis(&mut client, &route, vec![(200, 201)], &admin_keypair).await; + + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + + create_euis(&mut client, &route, vec![(200, 201)], &admin_keypair).await; + + assert_route_received(&mut response_stream, proto::ActionV1::Add, &route.id).await; + + assert_eui_pair( + &mut response_stream, + proto::ActionV1::Add, + &route.id, + 200, + 201, + ) + .await; + + assert_eui_pair( + &mut response_stream, + proto::ActionV1::Remove, + &route.id, + 200, + 201, + ) + .await; + + assert_eui_pair( + &mut response_stream, + proto::ActionV1::Add, + &route.id, + 200, + 201, + ) + .await; +} + async fn assert_route_received( stream: &mut Streaming, expected_action: proto::ActionV1, @@ -511,6 +583,41 @@ async fn create_euis( }; } +async fn delete_euis( + client: &mut RouteClient, + route: &proto::RouteV1, + pairs: Vec<(u64, u64)>, + signing_keypair: &Keypair, +) { + let requests = pairs + .into_iter() + .map(|(a, b)| proto::EuiPairV1 { + route_id: route.id.clone(), + app_eui: a, + dev_eui: b, + }) + .map(|pair| { + let mut request = proto::RouteUpdateEuisReqV1 { + action: proto::ActionV1::Remove as i32, + eui_pair: Some(pair), + timestamp: Utc::now().timestamp() as u64, + signature: vec![], + signer: signing_keypair.public_key().into(), + }; + + request.signature = signing_keypair + .sign(&request.encode_to_vec()) + .expect("sign"); + + request + }) + .collect::>(); + + let Ok(_) = client.update_euis(futures::stream::iter(requests)).await else { + panic!("unable to delete eui pairs") + }; +} + async fn create_devaddr_ranges( client: &mut RouteClient, route: &proto::RouteV1,