Skip to content

Commit bf21c47

Browse files
ytsssunKCSesh
authored andcommitted
apiserver: add network configure endpoint with netdog integration
Adds POST /actions/network/configure endpoint to write network configuration content directly to /.bottlerocket/net.toml. Includes error handling for UTF-8 validation, directory creation, and file writing operations. The endpoint integrates with netdog commit functionality to apply network configuration changes. Signed-off-by: Yutong Sun <yutongsu@amazon.com> Signed-off-by: Kyle Sessions <kssessio@amazon.com>
1 parent 9789fa7 commit bf21c47

File tree

3 files changed

+79
-1
lines changed

3 files changed

+79
-1
lines changed

sources/api/apiserver/src/server/error.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,18 @@ pub enum Error {
220220

221221
// =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^=
222222

223+
// Network configuration errors
224+
#[snafu(display("Network configuration content is not valid UTF-8"))]
225+
NetworkConfigContent { source: std::string::FromUtf8Error },
226+
227+
#[snafu(display("Failed to validate network configuration: {}", source))]
228+
NetworkConfigValidation { source: std::io::Error },
229+
230+
#[snafu(display("Invalid network configuration: {}", stderr))]
231+
NetworkConfigInvalid { stderr: String },
232+
233+
// =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^= =^..^=
234+
223235
// Update related errors
224236
#[snafu(display("Unable to start the update dispatcher: {} ", source))]
225237
UpdateDispatcher { source: io::Error },

sources/api/apiserver/src/server/mod.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use actix_web::{
1414
use datastore::{serialize_scalar, Committed, FilesystemDataStore, Key, KeyType, Value};
1515
use error::Result;
1616
use http::StatusCode;
17-
use log::info;
17+
use log::{debug, info};
1818
use model::ephemeral_storage::{Bind, Init};
1919
use model::generator::{RawSettingsGenerator, Strength};
2020
use model::{ConfigurationFiles, Model, Report, Services, Settings};
@@ -137,6 +137,7 @@ where
137137
.route("/prepare-update", web::post().to(prepare_update))
138138
.route("/activate-update", web::post().to(activate_update))
139139
.route("/deactivate-update", web::post().to(deactivate_update))
140+
.route("/network/configure", web::post().to(configure_network))
140141
.route(
141142
"/ephemeral-storage/init",
142143
web::post().to(initialize_ephemeral_storage),
@@ -687,6 +688,44 @@ async fn reboot() -> Result<HttpResponse> {
687688
Ok(HttpResponse::NoContent().finish())
688689
}
689690

691+
/// Configures network settings by writing invoking netdog commit.
692+
/// The configuration will be applied at next boot - a reboot is required for changes to take effect.
693+
async fn configure_network(body: web::Bytes) -> Result<HttpResponse> {
694+
debug!("Configuring network settings");
695+
696+
// Convert the body bytes to a UTF-8 string
697+
let content = String::from_utf8(body.to_vec()).context(error::NetworkConfigContentSnafu)?;
698+
699+
info!("Invoking netdog commit to validate and write network configuration");
700+
701+
// Validate and write net.toml using netdog commit
702+
let validation_result = std::process::Command::new("/usr/bin/netdog")
703+
.arg("commit")
704+
.stdin(std::process::Stdio::piped())
705+
.stdout(std::process::Stdio::piped())
706+
.stderr(std::process::Stdio::piped())
707+
.spawn()
708+
.and_then(|mut child| {
709+
use std::io::Write;
710+
if let Some(stdin) = child.stdin.as_mut() {
711+
stdin.write_all(content.as_bytes())?;
712+
}
713+
child.wait_with_output()
714+
})
715+
.context(error::NetworkConfigValidationSnafu)?;
716+
717+
if !validation_result.status.success() {
718+
let stderr = String::from_utf8_lossy(&validation_result.stderr);
719+
error!(
720+
"netdog commit failed: network configuration validation failed: {}",
721+
stderr
722+
);
723+
return error::NetworkConfigInvalidSnafu { stderr }.fail();
724+
}
725+
726+
Ok(HttpResponse::NoContent().finish())
727+
}
728+
690729
/// Gets the set of report types supported by this host.
691730
async fn list_reports() -> Result<ReportListResponse> {
692731
// Add each report to list response when adding a new handler
@@ -965,6 +1004,9 @@ impl ResponseError for error::Error {
9651004
InvalidPrefix { .. } => StatusCode::BAD_REQUEST,
9661005
DeserializeJson { .. } => StatusCode::BAD_REQUEST,
9671006
InvalidKeyPair { .. } => StatusCode::BAD_REQUEST,
1007+
NetworkConfigContent { .. } => StatusCode::BAD_REQUEST,
1008+
NetworkConfigValidation { .. } => StatusCode::BAD_REQUEST,
1009+
NetworkConfigInvalid { .. } => StatusCode::BAD_REQUEST,
9681010

9691011
// 404 Not Found
9701012
MissingData { .. } => StatusCode::NOT_FOUND,

sources/api/openapi.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,30 @@ paths:
722722
423:
723723
description: "Update write lock held. Try again in a moment"
724724

725+
/actions/network/configure:
726+
post:
727+
summary: "Configure network settings from net.toml content"
728+
operationId: "configure_network"
729+
requestBody:
730+
required: true
731+
content:
732+
text/plain:
733+
schema:
734+
type: string
735+
example: |
736+
version = 2
737+
738+
[eth0]
739+
dhcp4 = true
740+
primary = true
741+
responses:
742+
204:
743+
description: "Network configuration successfully written"
744+
400:
745+
description: "Invalid UTF-8 content or malformed configuration"
746+
500:
747+
description: "Server error writing configuration"
748+
725749
/updates/status:
726750
get:
727751
summary: "Get update status"

0 commit comments

Comments
 (0)