Skip to content

Commit a378cda

Browse files
authored
add background tasks for propagating DNS (#2797)
1 parent ae0cc5b commit a378cda

File tree

26 files changed

+2590
-58
lines changed

26 files changed

+2590
-58
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ signal-hook-tokio = { version = "0.3", features = [ "futures-v0_3" ] }
277277
sled = "0.34"
278278
sled-agent-client = { path = "sled-agent-client" }
279279
sled-hardware = { path = "sled-hardware" }
280-
slog = { version = "2.7", features = [ "max_level_trace", "release_max_level_debug" ] }
280+
slog = { version = "2.7", features = [ "dynamic-keys", "max_level_trace", "release_max_level_debug" ] }
281281
slog-async = "2.7"
282282
slog-dtrace = "0.2"
283283
slog-envlogger = "2.2"

common/src/nexus_config.rs

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ use serde::{Deserialize, Serialize};
1414
use serde_with::serde_as;
1515
use serde_with::DeserializeFromStr;
1616
use serde_with::DisplayFromStr;
17+
use serde_with::DurationSeconds;
1718
use serde_with::SerializeDisplay;
1819
use std::fmt;
1920
use std::net::SocketAddr;
2021
use std::path::{Path, PathBuf};
22+
use std::time::Duration;
2123
use uuid::Uuid;
2224

2325
#[derive(Debug)]
@@ -263,6 +265,37 @@ fn default_https_port() -> u16 {
263265
443
264266
}
265267

268+
/// Background task configuration
269+
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
270+
pub struct BackgroundTaskConfig {
271+
/// configuration for internal DNS background tasks
272+
pub dns_internal: DnsTasksConfig,
273+
/// configuration for external DNS background tasks
274+
pub dns_external: DnsTasksConfig,
275+
}
276+
277+
#[serde_as]
278+
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
279+
pub struct DnsTasksConfig {
280+
/// period (in seconds) for periodic activations of the background task that
281+
/// reads the latest DNS configuration from the database
282+
#[serde_as(as = "DurationSeconds<u64>")]
283+
pub period_secs_config: Duration,
284+
285+
/// period (in seconds) for periodic activations of the background task that
286+
/// reads the latest list of DNS servers from the database
287+
#[serde_as(as = "DurationSeconds<u64>")]
288+
pub period_secs_servers: Duration,
289+
290+
/// period (in seconds) for periodic activations of the background task that
291+
/// propagates the latest DNS configuration to the latest set of DNS servers
292+
#[serde_as(as = "DurationSeconds<u64>")]
293+
pub period_secs_propagation: Duration,
294+
295+
/// maximum number of concurrent DNS server updates
296+
pub max_concurrent_server_updates: usize,
297+
}
298+
266299
/// Configuration for a nexus server
267300
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
268301
pub struct PackageConfig {
@@ -278,8 +311,8 @@ pub struct PackageConfig {
278311
/// Timeseries database configuration.
279312
#[serde(default)]
280313
pub timeseries_db: TimeseriesDbConfig,
281-
/// Updates-related configuration. Updates APIs return 400 Bad Request when this is
282-
/// unconfigured.
314+
/// Updates-related configuration. Updates APIs return 400 Bad Request when
315+
/// this is unconfigured.
283316
#[serde(default)]
284317
pub updates: Option<UpdatesConfig>,
285318
/// Tunable configuration for testing and experimentation
@@ -288,6 +321,8 @@ pub struct PackageConfig {
288321
/// `Dendrite` dataplane daemon configuration
289322
#[serde(default)]
290323
pub dendrite: DpdConfig,
324+
/// Background task configuration
325+
pub background_tasks: BackgroundTaskConfig,
291326
}
292327

293328
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
@@ -361,7 +396,8 @@ mod test {
361396
};
362397
use crate::address::{Ipv6Subnet, RACK_PREFIX};
363398
use crate::nexus_config::{
364-
Database, DeploymentConfig, DpdConfig, LoadErrorKind,
399+
BackgroundTaskConfig, Database, DeploymentConfig, DnsTasksConfig,
400+
DpdConfig, LoadErrorKind,
365401
};
366402
use dropshot::ConfigDropshot;
367403
use dropshot::ConfigLogging;
@@ -373,6 +409,7 @@ mod test {
373409
use std::path::Path;
374410
use std::path::PathBuf;
375411
use std::str::FromStr;
412+
use std::time::Duration;
376413

377414
/// Generates a temporary filesystem path unique for the given label.
378415
fn temp_path(label: &str) -> PathBuf {
@@ -494,6 +531,15 @@ mod test {
494531
type = "from_dns"
495532
[dendrite]
496533
address = "[::1]:12224"
534+
[background_tasks]
535+
dns_internal.period_secs_config = 1
536+
dns_internal.period_secs_servers = 2
537+
dns_internal.period_secs_propagation = 3
538+
dns_internal.max_concurrent_server_updates = 4
539+
dns_external.period_secs_config = 5
540+
dns_external.period_secs_servers = 6
541+
dns_external.period_secs_propagation = 7
542+
dns_external.max_concurrent_server_updates = 8
497543
"##,
498544
)
499545
.unwrap();
@@ -548,6 +594,20 @@ mod test {
548594
SocketAddr::from_str("[::1]:12224").unwrap()
549595
)
550596
},
597+
background_tasks: BackgroundTaskConfig {
598+
dns_internal: DnsTasksConfig {
599+
period_secs_config: Duration::from_secs(1),
600+
period_secs_servers: Duration::from_secs(2),
601+
period_secs_propagation: Duration::from_secs(3),
602+
max_concurrent_server_updates: 4,
603+
},
604+
dns_external: DnsTasksConfig {
605+
period_secs_config: Duration::from_secs(5),
606+
period_secs_servers: Duration::from_secs(6),
607+
period_secs_propagation: Duration::from_secs(7),
608+
max_concurrent_server_updates: 8,
609+
},
610+
},
551611
},
552612
}
553613
);
@@ -584,6 +644,15 @@ mod test {
584644
type = "from_dns"
585645
[dendrite]
586646
address = "[::1]:12224"
647+
[background_tasks]
648+
dns_internal.period_secs_config = 1
649+
dns_internal.period_secs_servers = 2
650+
dns_internal.period_secs_propagation = 3
651+
dns_internal.max_concurrent_server_updates = 4
652+
dns_external.period_secs_config = 5
653+
dns_external.period_secs_servers = 6
654+
dns_external.period_secs_propagation = 7
655+
dns_external.max_concurrent_server_updates = 8
587656
"##,
588657
)
589658
.unwrap();

nexus/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ oximeter-producer.workspace = true
9090
[dev-dependencies]
9191
assert_matches.workspace = true
9292
criterion.workspace = true
93+
dns-server.workspace = true
9394
expectorate.workspace = true
9495
hyper-rustls.workspace = true
9596
itertools.workspace = true

nexus/db-model/src/service_kind.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ impl_enum_type!(
1212
#[diesel(postgres_type(name = "service_kind"))]
1313
pub struct ServiceKindEnum;
1414

15-
#[derive(Clone, Copy, Debug, AsExpression, FromSqlRow, Serialize, Deserialize, PartialEq)]
15+
#[derive(Clone, Copy, Debug, Eq, AsExpression, FromSqlRow, Serialize, Deserialize, PartialEq)]
1616
#[diesel(sql_type = ServiceKindEnum)]
1717
pub enum ServiceKind;
1818

nexus/db-queries/src/authz/context.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ impl Authz {
6262
/// This is the primary external interface for the authorization subsystem,
6363
/// through which Nexus at-large makes authorization checks. This is almost
6464
/// always done through [`OpContext::authorize()`].
65+
#[derive(Clone)]
6566
pub struct Context {
6667
authn: Arc<authn::Context>,
6768
authz: Arc<Authz>,

nexus/db-queries/src/context.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub struct OpContext {
4646
kind: OpKind,
4747
}
4848

49+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4950
pub enum OpKind {
5051
/// Handling an external API request
5152
ExternalApiRequest,
@@ -200,6 +201,34 @@ impl OpContext {
200201
}
201202
}
202203

204+
/// Creates a new `OpContext` with extra metadata (including log metadata)
205+
///
206+
/// This is intended for cases where you want an OpContext that's
207+
/// functionally the same as one that you already have, but where you want
208+
/// to provide extra debugging information (in the form of key-value pairs)
209+
/// in both the OpContext itself and its logger.
210+
pub fn child(&self, new_metadata: BTreeMap<String, String>) -> OpContext {
211+
let created_instant = Instant::now();
212+
let created_walltime = SystemTime::now();
213+
let mut metadata = self.metadata.clone();
214+
let mut log = self.log.clone();
215+
216+
for (k, v) in new_metadata {
217+
metadata.insert(k.clone(), v.clone());
218+
log = log.new(o!(k => v));
219+
}
220+
221+
OpContext {
222+
log,
223+
authn: self.authn.clone(),
224+
authz: self.authz.clone(),
225+
created_instant,
226+
created_walltime,
227+
metadata,
228+
kind: self.kind,
229+
}
230+
}
231+
203232
/// Check whether the actor performing this request is authorized for
204233
/// `action` on `resource`.
205234
pub async fn authorize<Resource>(
@@ -254,6 +283,7 @@ mod test {
254283
use nexus_test_utils::db::test_setup_database;
255284
use omicron_common::api::external::Error;
256285
use omicron_test_utils::dev;
286+
use std::collections::BTreeMap;
257287
use std::sync::Arc;
258288

259289
#[tokio::test]
@@ -308,4 +338,51 @@ mod test {
308338
db.cleanup().await.unwrap();
309339
logctx.cleanup_successful();
310340
}
341+
342+
#[tokio::test]
343+
async fn test_child_context() {
344+
let logctx = dev::test_setup_log("test_child_context");
345+
let mut db = test_setup_database(&logctx.log).await;
346+
let (_, datastore) =
347+
crate::db::datastore::datastore_test(&logctx, &db).await;
348+
let opctx = OpContext::for_background(
349+
logctx.log.new(o!()),
350+
Arc::new(authz::Authz::new(&logctx.log)),
351+
authn::Context::internal_unauthenticated(),
352+
datastore,
353+
);
354+
355+
let child_opctx = opctx.child(BTreeMap::from([
356+
(String::from("one"), String::from("two")),
357+
(String::from("three"), String::from("four")),
358+
]));
359+
let grandchild_opctx = opctx.child(BTreeMap::from([
360+
(String::from("one"), String::from("seven")),
361+
(String::from("five"), String::from("six")),
362+
]));
363+
364+
// Verify they're the same "kind".
365+
assert_eq!(opctx.kind, child_opctx.kind);
366+
assert_eq!(opctx.kind, grandchild_opctx.kind);
367+
368+
// Verify that both descendants have metadata from the root.
369+
for (k, v) in opctx.metadata.iter() {
370+
assert_eq!(v, &child_opctx.metadata[k]);
371+
assert_eq!(v, &grandchild_opctx.metadata[k]);
372+
}
373+
374+
// The child opctx ought to have its own metadata and not any of its
375+
// child's metadata.
376+
assert_eq!(child_opctx.metadata["one"], "two");
377+
assert_eq!(child_opctx.metadata["three"], "four");
378+
assert!(!child_opctx.metadata.contains_key("five"));
379+
380+
// The granchild opctx ought to have its own metadata, one key of which
381+
// overrides its parent's.
382+
assert_eq!(grandchild_opctx.metadata["one"], "seven");
383+
assert_eq!(grandchild_opctx.metadata["five"], "six");
384+
385+
db.cleanup().await.unwrap();
386+
logctx.cleanup_successful();
387+
}
311388
}

nexus/db-queries/src/db/datastore/dns.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const NMAX_DNS_ZONES: u32 = 10;
3636

3737
impl DataStore {
3838
/// List all DNS zones in a DNS group (paginated)
39-
async fn dns_zones_list(
39+
pub async fn dns_zones_list(
4040
&self,
4141
opctx: &OpContext,
4242
dns_group: DnsGroup,
@@ -53,7 +53,7 @@ impl DataStore {
5353
}
5454

5555
/// Get the latest version for a given DNS group
56-
async fn dns_group_latest_version(
56+
pub async fn dns_group_latest_version(
5757
&self,
5858
opctx: &OpContext,
5959
dns_group: DnsGroup,

0 commit comments

Comments
 (0)