Skip to content

Commit 0f0afbb

Browse files
committed
Continue propagating Nexus config through Nexus
1 parent 6502c44 commit 0f0afbb

File tree

9 files changed

+152
-163
lines changed

9 files changed

+152
-163
lines changed

README.adoc

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,36 @@ Supported config properties include:
7777
|Yes
7878
|URL identifying the CockroachDB instance(s) to connect to. CockroachDB is used for all persistent data.
7979

80-
|`external_ip`
81-
|`"127.0.0.1"`
80+
|`dropshot_external`
81+
|
82+
|Yes
83+
|Dropshot configuration for the external server (i.e., the one that operators and developers using the Oxide rack will use). Specific properties are documented below, but see the Dropshot README for details.
84+
| Note that this is an array of external address configurations; multiple may be supplied.
85+
86+
|`dropshot_external.bind_address`
87+
|`"127.0.0.1:12220"`
88+
|Yes
89+
|Specifies that the server should bind to the given IP address and TCP port for the **external** API (i.e., the one that operators and developers using the Oxide rack will use). In general, servers can bind to more than one IP address and port, but this is not (yet?) supported.
90+
91+
|`dropshot_external.request_body_max_bytes`
92+
|`1000`
93+
|Yes
94+
|Specifies the maximum request body size for the **external** API.
95+
96+
|`dropshot_internal`
97+
|
98+
|Yes
99+
|Dropshot configuration for the internal server (i.e., the one used by the sled agent). Specific properties are documented below, but see the Dropshot README for details.
100+
101+
|`dropshot_internal.bind_address`
102+
|`"127.0.0.1:12220"`
82103
|Yes
83-
|Specifies that the server should bind to the given IP address for the **external** API (i.e., the one that operators and developers using the Oxide rack will use).
104+
|Specifies that the server should bind to the given IP address and TCP port for the **internal** API (i.e., the one used by the sled agent). In general, servers can bind to more than one IP address and port, but this is not (yet?) supported.
84105

85-
|`internal_ip`
86-
|`"127.0.0.1"`
106+
|`dropshot_internal.request_body_max_bytes`
107+
|`1000`
87108
|Yes
88-
|Dropshot configuration for the internal server (i.e., the one used by the sled agent).
109+
|Specifies the maximum request body size for the **internal** API.
89110

90111
|`id`
91112
|`"e6bff1ff-24fb-49dc-a54e-c6a350cd4d6c"`

common/src/nexus_config.rs

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
88
use super::address::{Ipv6Subnet, RACK_PREFIX};
99
use super::postgres_config::PostgresConfigWithUrl;
10+
use dropshot::ConfigDropshot;
1011
use serde::{Deserialize, Serialize};
1112
use serde_with::serde_as;
1213
use serde_with::DisplayFromStr;
1314
use std::fmt;
14-
use std::net::IpAddr;
1515
use std::path::{Path, PathBuf};
1616
use uuid::Uuid;
1717

@@ -98,36 +98,19 @@ pub enum Database {
9898
},
9999
}
100100

101-
/// Describes how ports are selected for dropshot's HTTP servers.
102-
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
103-
#[serde(rename_all = "snake_case")]
104-
pub enum PortPicker {
105-
/// Use default values for ports, defined by Nexus.
106-
NexusChoice,
107-
/// Use port zero - this is avoids conflicts during tests,
108-
/// by letting the OS pick free ports.
109-
Zero,
110-
}
111-
112-
impl Default for PortPicker {
113-
fn default() -> Self {
114-
PortPicker::NexusChoice
115-
}
116-
}
117-
118101
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
119102
pub struct DeploymentConfig {
120103
/// Uuid of the Nexus instance
121104
pub id: Uuid,
122105
/// Uuid of the Rack where Nexus is executing.
123106
pub rack_id: Uuid,
124-
/// External address of Nexus.
125-
pub external_ip: IpAddr,
126-
/// Internal address of Nexus.
127-
pub internal_ip: IpAddr,
128-
/// Decides how ports are selected
129-
#[serde(default)]
130-
pub port_picker: PortPicker,
107+
/// Dropshot configurations for external API server.
108+
///
109+
/// Multiple configurations may be supplied to request
110+
/// combinations of HTTP / HTTPS servers.
111+
pub dropshot_external: Vec<ConfigDropshot>,
112+
/// Dropshot configuration for internal API server.
113+
pub dropshot_internal: ConfigDropshot,
131114
/// Portion of the IP space to be managed by the Rack.
132115
pub subnet: Ipv6Subnet<RACK_PREFIX>,
133116
/// DB configuration.

nexus/examples/config.toml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,17 @@ address = "[::1]:8123"
3737
# Identifier for this instance of Nexus
3838
id = "e6bff1ff-24fb-49dc-a54e-c6a350cd4d6c"
3939
rack_id = "c19a698f-c6f9-4a17-ae30-20d711b8f7dc"
40-
external_ip = "127.0.0.1"
41-
internal_ip = "127.0.0.1"
40+
41+
[[deployment.dropshot_external]]
42+
# IP Address and TCP port on which to listen for the external API
43+
bind_address = "127.0.0.1:12220"
44+
# Allow larger request bodies (1MiB) to accomodate firewall endpoints (one
45+
# rule is ~500 bytes)
46+
request_body_max_bytes = 1048576
47+
48+
[deployment.dropshot_internal]
49+
# IP Address and TCP port on which to listen for the internal API
50+
bind_address = "127.0.0.1:12221"
4251

4352
[deployment.subnet]
4453
net = "fd00:1122:3344:0100::/56"

nexus/src/config.rs

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -215,16 +215,17 @@ mod test {
215215
AuthnConfig, Config, ConsoleConfig, LoadError, PackageConfig,
216216
SchemeName, TimeseriesDbConfig, UpdatesConfig,
217217
};
218+
use dropshot::ConfigDropshot;
218219
use dropshot::ConfigLogging;
219220
use dropshot::ConfigLoggingIfExists;
220221
use dropshot::ConfigLoggingLevel;
221222
use libc;
222223
use omicron_common::address::{Ipv6Subnet, RACK_PREFIX};
223224
use omicron_common::nexus_config::{
224-
Database, DeploymentConfig, LoadErrorKind, PortPicker,
225+
Database, DeploymentConfig, LoadErrorKind,
225226
};
226227
use std::fs;
227-
use std::net::{IpAddr, Ipv6Addr};
228+
use std::net::{Ipv6Addr, SocketAddr};
228229
use std::path::Path;
229230
use std::path::PathBuf;
230231

@@ -335,8 +336,12 @@ mod test {
335336
[deployment]
336337
id = "28b90dc4-c22a-65ba-f49a-f051fe01208f"
337338
rack_id = "38b90dc4-c22a-65ba-f49a-f051fe01208f"
338-
external_ip = "10.1.2.3"
339-
internal_ip = "10.1.2.4"
339+
[[deployment.dropshot_external]]
340+
bind_address = "10.1.2.3:4567"
341+
request_body_max_bytes = 1024
342+
[deployment.dropshot_internal]
343+
bind_address = "10.1.2.3:4568"
344+
request_body_max_bytes = 1024
340345
[deployment.subnet]
341346
net = "::/56"
342347
[deployment.database]
@@ -353,9 +358,18 @@ mod test {
353358
rack_id: "38b90dc4-c22a-65ba-f49a-f051fe01208f"
354359
.parse()
355360
.unwrap(),
356-
external_ip: "10.1.2.3".parse::<IpAddr>().unwrap(),
357-
internal_ip: "10.1.2.4".parse::<IpAddr>().unwrap(),
358-
port_picker: PortPicker::default(),
361+
dropshot_external: vec![ConfigDropshot {
362+
bind_address: "10.1.2.3:4567"
363+
.parse::<SocketAddr>()
364+
.unwrap(),
365+
..Default::default()
366+
},],
367+
dropshot_internal: ConfigDropshot {
368+
bind_address: "10.1.2.3:4568"
369+
.parse::<SocketAddr>()
370+
.unwrap(),
371+
..Default::default()
372+
},
359373
subnet: Ipv6Subnet::<RACK_PREFIX>::new(Ipv6Addr::LOCALHOST),
360374
database: Database::FromDns,
361375
},
@@ -404,8 +418,12 @@ mod test {
404418
[deployment]
405419
id = "28b90dc4-c22a-65ba-f49a-f051fe01208f"
406420
rack_id = "38b90dc4-c22a-65ba-f49a-f051fe01208f"
407-
external_ip = "10.1.2.3"
408-
internal_ip = "10.1.2.4"
421+
[[deployment.dropshot_external]]
422+
bind_address = "10.1.2.3:4567"
423+
request_body_max_bytes = 1024
424+
[deployment.dropshot_internal]
425+
bind_address = "10.1.2.3:4568"
426+
request_body_max_bytes = 1024
409427
[deployment.subnet]
410428
net = "::/56"
411429
[deployment.database]
@@ -442,8 +460,12 @@ mod test {
442460
[deployment]
443461
id = "28b90dc4-c22a-65ba-f49a-f051fe01208f"
444462
rack_id = "38b90dc4-c22a-65ba-f49a-f051fe01208f"
445-
external_ip = "10.1.2.3"
446-
internal_ip = "10.1.2.4"
463+
[[deployment.dropshot_external]]
464+
bind_address = "10.1.2.3:4567"
465+
request_body_max_bytes = 1024
466+
[deployment.dropshot_internal]
467+
bind_address = "10.1.2.3:4568"
468+
request_body_max_bytes = 1024
447469
[deployment.subnet]
448470
net = "::/56"
449471
[deployment.database]
@@ -494,8 +516,12 @@ mod test {
494516
[deployment]
495517
id = "28b90dc4-c22a-65ba-f49a-f051fe01208f"
496518
rack_id = "38b90dc4-c22a-65ba-f49a-f051fe01208f"
497-
external_ip = "10.1.2.3"
498-
internal_ip = "10.1.2.4"
519+
[[deployment.dropshot_external]]
520+
bind_address = "10.1.2.3:4567"
521+
request_body_max_bytes = 1024
522+
[deployment.dropshot_internal]
523+
bind_address = "10.1.2.3:4568"
524+
request_body_max_bytes = 1024
499525
[deployment.subnet]
500526
net = "::/56"
501527
[deployment.database]

nexus/src/lib.rs

Lines changed: 29 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ pub use crucible_agent_client;
3434
use external_api::http_entrypoints::external_api;
3535
use internal_api::http_entrypoints::internal_api;
3636
use slog::Logger;
37-
use std::net::SocketAddr;
38-
use std::path::PathBuf;
3937
use std::sync::Arc;
4038

4139
#[macro_use]
@@ -73,10 +71,8 @@ pub fn run_openapi_internal() -> Result<(), String> {
7371
pub struct Server {
7472
/// shared state used by API request handlers
7573
pub apictx: Arc<ServerContext>,
76-
/// dropshot server for external API (encrypted)
77-
pub https_server_external: Option<dropshot::HttpServer<Arc<ServerContext>>>,
78-
/// dropshot server for external API (unencrypted)
79-
pub http_server_external: dropshot::HttpServer<Arc<ServerContext>>,
74+
/// dropshot servers for external API
75+
pub http_servers_external: Vec<dropshot::HttpServer<Arc<ServerContext>>>,
8076
/// dropshot server for internal API
8177
pub http_server_internal: dropshot::HttpServer<Arc<ServerContext>>,
8278
}
@@ -96,98 +92,36 @@ impl Server {
9692
ServerContext::new(config.deployment.rack_id, ctxlog, &config)
9793
.await?;
9894

99-
// Determine port choices
100-
101-
let (external_http_port, external_https_port, internal_http_port) =
102-
match config.deployment.port_picker {
103-
omicron_common::nexus_config::PortPicker::NexusChoice => {
104-
(80, 443, omicron_common::address::NEXUS_INTERNAL_PORT)
105-
}
106-
omicron_common::nexus_config::PortPicker::Zero => (0, 0, 0),
107-
};
108-
10995
// Launch the internal server.
110-
111-
let dropshot_internal_config = dropshot::ConfigDropshot {
112-
bind_address: SocketAddr::new(
113-
config.deployment.internal_ip,
114-
internal_http_port,
115-
),
116-
request_body_max_bytes: 1048576,
117-
..Default::default()
118-
};
119-
let http_server_starter_internal = dropshot::HttpServerStarter::new(
120-
&dropshot_internal_config,
96+
let server_starter_internal = dropshot::HttpServerStarter::new(
97+
&config.deployment.dropshot_internal,
12198
internal_api(),
12299
Arc::clone(&apictx),
123100
&log.new(o!("component" => "dropshot_internal")),
124101
)
125102
.map_err(|error| format!("initializing internal server: {}", error))?;
126-
let http_server_internal = http_server_starter_internal.start();
103+
let http_server_internal = server_starter_internal.start();
127104

128105
// Launch the external server(s).
129-
//
130-
// - The HTTP server is unconditionally started.
131-
// - The HTTPS server is started if the necessary certificate files
132-
// exist.
133-
//
134-
// TODO: Consider changing this disposition, making "HTTPS" the default,
135-
// and returning an error if the certificates don't exist. Doing so
136-
// would be the more secure long-term plan, but would make gradual
137-
// deployment of this feature more difficult.
138-
139-
let cert_file = PathBuf::from("/var/nexus/certs/cert.pem");
140-
let key_file = PathBuf::from("/var/nexus/certs/key.pem");
141-
142-
let https_server_external = if cert_file.exists() && key_file.exists() {
143-
let dropshot_external_https_config = dropshot::ConfigDropshot {
144-
bind_address: SocketAddr::new(
145-
config.deployment.external_ip,
146-
external_https_port,
147-
),
148-
request_body_max_bytes: 1048576,
149-
tls: Some(dropshot::ConfigTls { cert_file, key_file }),
150-
};
151-
let https_server_starter_external =
152-
dropshot::HttpServerStarter::new(
153-
&dropshot_external_https_config,
106+
let http_servers_external = config
107+
.deployment
108+
.dropshot_external
109+
.iter()
110+
.map(|cfg| {
111+
let server_starter_external = dropshot::HttpServerStarter::new(
112+
&cfg,
154113
external_api(),
155114
Arc::clone(&apictx),
156-
&log.new(
157-
o!("component" => "dropshot_external (encrypted)"),
158-
),
115+
&log.new(o!("component" => "dropshot_external")),
159116
)
160117
.map_err(|error| {
161118
format!("initializing external server: {}", error)
162119
})?;
163-
Some(https_server_starter_external.start())
164-
} else {
165-
None
166-
};
167-
168-
let dropshot_external_http_config = dropshot::ConfigDropshot {
169-
bind_address: SocketAddr::new(
170-
config.deployment.external_ip,
171-
external_http_port,
172-
),
173-
request_body_max_bytes: 1048576,
174-
tls: None,
175-
};
176-
let http_server_starter_external = dropshot::HttpServerStarter::new(
177-
&dropshot_external_http_config,
178-
external_api(),
179-
Arc::clone(&apictx),
180-
&log.new(o!("component" => "dropshot_external (unencrypted)")),
181-
)
182-
.map_err(|error| format!("initializing external server: {}", error))?;
183-
let http_server_external = http_server_starter_external.start();
184-
185-
Ok(Server {
186-
apictx,
187-
https_server_external,
188-
http_server_external,
189-
http_server_internal,
190-
})
120+
Ok(server_starter_external.start())
121+
})
122+
.collect::<Result<Vec<dropshot::HttpServer<_>>, String>>()?;
123+
124+
Ok(Server { apictx, http_servers_external, http_server_internal })
191125
}
192126

193127
/// Wait for the given server to shut down
@@ -196,18 +130,20 @@ impl Server {
196130
/// immediately after calling `start()`, the program will block indefinitely
197131
/// or until something else initiates a graceful shutdown.
198132
pub async fn wait_for_finish(self) -> Result<(), String> {
199-
let errors = vec![
200-
self.http_server_external
201-
.await
202-
.map_err(|e| format!("external: {}", e)),
133+
let mut errors = vec![];
134+
for server in self.http_servers_external {
135+
errors.push(server.await.map_err(|e| format!("external: {}", e)));
136+
}
137+
errors.push(
203138
self.http_server_internal
204139
.await
205140
.map_err(|e| format!("internal: {}", e)),
206-
]
207-
.into_iter()
208-
.filter(Result::is_err)
209-
.map(|r| r.unwrap_err())
210-
.collect::<Vec<String>>();
141+
);
142+
let errors = errors
143+
.into_iter()
144+
.filter(Result::is_err)
145+
.map(|r| r.unwrap_err())
146+
.collect::<Vec<String>>();
211147

212148
if errors.len() > 0 {
213149
let msg = format!("errors shutting down: ({})", errors.join(", "));

0 commit comments

Comments
 (0)