Skip to content

Commit

Permalink
Finish pure store/load functionality for runtime configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
mbr committed Jan 6, 2024
1 parent 15def0b commit bebd3ea
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 19 deletions.
46 changes: 46 additions & 0 deletions src/container_orchestrator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ use crate::{

use anyhow::Context;
use axum::async_trait;
use axum::body::Body;
use axum::http::header::CONTENT_TYPE;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use sec::Secret;
use serde::{Deserialize, Deserializer, Serialize};
use tracing::{debug, error, info};
Expand Down Expand Up @@ -61,6 +65,21 @@ pub(crate) struct RuntimeConfig {
http_access: HashMap<String, String>,
}

impl IntoResponse for RuntimeConfig {
fn into_response(self) -> axum::response::Response {
toml::to_string_pretty(&self)
.ok()
.and_then(|config_toml| {
Response::builder()
.status(StatusCode::OK)
.header(CONTENT_TYPE, "application/toml")
.body(Body::from(config_toml))
.ok()
})
.unwrap_or_else(|| StatusCode::INTERNAL_SERVER_ERROR.into_response())
}
}

impl ContainerOrchestrator {
pub(crate) fn new<P: AsRef<Path>, Q: AsRef<Path>>(
podman_path: P,
Expand Down Expand Up @@ -116,6 +135,33 @@ impl ContainerOrchestrator {
toml::from_str(&raw).context("could not parse configuration")
}

pub(crate) async fn save_config(
&self,
manifest_reference: &ManifestReference,
config: &RuntimeConfig,
) -> anyhow::Result<RuntimeConfig> {
let config_path = self.config_path(manifest_reference);
let parent_dir = config_path
.parent()
.context("could not determine parent path")?;

if !parent_dir.exists() {
tokio::fs::create_dir_all(parent_dir)
.await
.context("could not create parent path")?;
}

let toml = toml::to_string_pretty(config).context("could not serialize new config")?;

// TODO: Do atomic replace.
tokio::fs::write(config_path, toml)
.await
.context("failed to write new toml config")?;

// Read back to verify.
self.load_config(manifest_reference).await
}

async fn fetch_managed_containers(&self, all: bool) -> anyhow::Result<Vec<PublishedContainer>> {
debug!("refreshing running containers");

Expand Down
38 changes: 19 additions & 19 deletions src/reverse_proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use axum::{
body::Body,
extract::{Request, State},
http::{
header::{CONTENT_TYPE, HOST},
header::HOST,
uri::{Authority, Parts, PathAndQuery, Scheme},
Method, StatusCode, Uri,
},
Expand All @@ -22,7 +22,7 @@ use tokio::sync::RwLock;
use tracing::{trace, warn};

use crate::{
container_orchestrator::{ContainerOrchestrator, PublishedContainer},
container_orchestrator::{ContainerOrchestrator, PublishedContainer, RuntimeConfig},
registry::{
storage::ImageLocation, AuthProvider, ManifestReference, Reference, UnverifiedCredentials,
},
Expand Down Expand Up @@ -176,6 +176,7 @@ enum AppError {
AssertionFailed(&'static str),
NonUtf8Header,
AuthFailure(StatusCode),
InvalidPayload,
Internal(anyhow::Error),
}

Expand All @@ -188,6 +189,7 @@ impl Display for AppError {
AppError::AssertionFailed(msg) => f.write_str(msg),
AppError::NonUtf8Header => f.write_str("a header contained non-utf8 data"),
AppError::AuthFailure(_) => f.write_str("authentication missing or not present"),
AppError::InvalidPayload => f.write_str("invalid payload"),
AppError::Internal(err) => Display::fmt(err, f),
}
}
Expand All @@ -212,6 +214,7 @@ impl IntoResponse for AppError {
AppError::AssertionFailed(msg) => (StatusCode::NOT_FOUND, msg).into_response(),
AppError::NonUtf8Header => StatusCode::BAD_REQUEST.into_response(),
AppError::AuthFailure(status) => status.into_response(),
AppError::InvalidPayload => StatusCode::BAD_REQUEST.into_response(),
AppError::Internal(err) => {
(StatusCode::INTERNAL_SERVER_ERROR, err.to_string()).into_response()
}
Expand Down Expand Up @@ -281,10 +284,10 @@ async fn route_request(
let method = request.method().clone();
// Note: The auth functionality has been lifted from `registry`. It may need to be
// refactored out because of that.
let creds = request
.extract::<UnverifiedCredentials, _>()
let (creds, opt_body) = request
.extract::<(UnverifiedCredentials, Option<String>), _>()
.await
.map_err(AppError::AuthFailure)?;
.map_err(|_| AppError::AuthFailure(StatusCode::UNAUTHORIZED))?;

// Any internal URL is subject to requiring auth through the master key.
if !rp.auth_provider.check_credentials(&creds).await {
Expand Down Expand Up @@ -324,22 +327,19 @@ async fn route_request(
.load_config(&manifest_reference)
.await
.map_err(AppError::Internal)?;
let config_toml = toml::to_string_pretty(&config)
.map_err(|err| AppError::Internal(err.into()))?;

Response::builder()
.status(StatusCode::OK)
.header(CONTENT_TYPE, "application/toml")
.body(Body::from(config_toml))
.map_err(|_| AppError::AssertionFailed("should not fail to build response"))

Ok(config.into_response())
}
Method::PUT => {
todo!("handle PUT");
Response::builder()
.status(StatusCode::OK)
// .header(CONTENT_TYPE, "application/toml")
.body(Body::from("TODO: Replace"))
.map_err(|_| AppError::AssertionFailed("should not fail to build response"))
let raw = dbg!(opt_body.ok_or(AppError::InvalidPayload)?);
let new_config: RuntimeConfig =
toml::from_str(&raw).map_err(|_| AppError::InvalidPayload)?;
let stored = orchestrator
.save_config(&manifest_reference, &new_config)
.await
.map_err(AppError::Internal)?;

Ok(stored.into_response())
}
_ => Err(AppError::InternalUrlInvalid),
};
Expand Down

0 comments on commit bebd3ea

Please sign in to comment.