Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add optional auto-approve config on no approver #24

Merged
merged 2 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
PHANTASM_SECRET=xxx
PHANTASM_AUTO_APPROVE=xxx
16 changes: 16 additions & 0 deletions docs/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,19 @@ communicate with the Receiver Server.
from phantasmpy import Phantasm
phantasm = Phantasm(secret="secretkey")
```

## Auto Approval

There are times when an approver is not available to approve an action call. By
default, Phantasm will return an error response with unavailable status code. If
you want to automatically approve all actions when no approver is available, you
can enable the auto-approval feature by setting the `PHANTASM_AUTO_APPROVAL`
environment variable to true.

```bash
docker run -e PHANTASM_AUTO_APPROVAL=true ...
```

!!! warning

Enabling auto-approval is not recommended for production environments.
14 changes: 12 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ mod types;
use clap::{arg, ArgMatches, Command};
use futures::{SinkExt, StreamExt};
use protos::receiver_server::ReceiverServer;
use services::Phantasm;
use services::{Configuration, Phantasm};
use std::env;
use std::net::{IpAddr, Ipv4Addr};
use std::sync::Arc;
Expand Down Expand Up @@ -56,12 +56,22 @@ fn start_command() -> Command {
.arg(arg_coordinator_port)
}

fn configuration() -> Configuration {
let auto_approve = env::var("PHANTASM_AUTO_APPROVE")
.map(|value| value.parse::<bool>().unwrap_or_default())
.unwrap_or_default();

Configuration { auto_approve }
}

async fn start_handler(args: &ArgMatches) {
// Unwrapping is safe here because the arguments are validated by Clap.
let receiver_port = *args.get_one::<u16>("receiver-port").unwrap();
let coordinator_port = *args.get_one::<u16>("coordinator-port").unwrap();

let service = Arc::new(Phantasm::open().expect("Failed to open Phantasm"));
let config = configuration();
let phantasm = Phantasm::open(&config).expect("Failed to open Phantasm");
let service = Arc::new(phantasm);

let receiver_service = service.clone();
let receiver_server = tokio::spawn(async move {
Expand Down
19 changes: 17 additions & 2 deletions src/services/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,29 @@ use tokio::sync::oneshot::Sender;
use tokio_tungstenite::tungstenite::Message;
use tonic::{Request, Response, Status};

#[derive(Debug, Clone)]
pub struct Configuration {
pub auto_approve: bool,
}

#[cfg(test)]
impl Default for Configuration {
fn default() -> Self {
Configuration { auto_approve: false }
}
}

#[derive(Debug)]
pub struct Phantasm {
config: Configuration,
connections: Mutex<HashMap<ConnectionID, Connection>>,
approvals: Mutex<HashMap<ApprovalID, Sender<ApprovalResponse>>>,
}

impl Phantasm {
pub fn open() -> Result<Self, Box<dyn Error>> {
pub fn open(config: &Configuration) -> Result<Self, Box<dyn Error>> {
Ok(Phantasm {
config: config.clone(),
connections: Mutex::new(HashMap::new()),
approvals: Mutex::new(HashMap::new()),
})
Expand Down Expand Up @@ -112,7 +126,8 @@ mod tests {

#[tokio::test]
async fn test_receive_message() {
let phantasm = Phantasm::open().unwrap();
let config = Configuration::default();
let phantasm = Phantasm::open(&config).unwrap();

// Simulate queueing a pending approval request.
let approval_id = ApprovalID::new();
Expand Down
17 changes: 13 additions & 4 deletions src/services/receiver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,25 @@ impl Receiver for Arc<Phantasm> {
&self,
request: Request<protos::GetApprovalRequest>,
) -> Result<Response<protos::GetApprovalResponse>, Status> {
let request = request.into_inner();
let connection_id = match self.get_lightest_connection() {
Some(id) => id,
None => {
let message = "No approver is available at the moment";
return Err(Status::unavailable(message));
if self.config.auto_approve {
return Ok(Response::new(protos::GetApprovalResponse {
approved: true,
parameters: request.parameters.clone(),
}));
} else {
let message = "No approver is available at the moment";
return Err(Status::unavailable(message));
}
},
};

// Unwrap is safe because the connection ID is guaranteed to exist.
let connection = self.get_connection(&connection_id).unwrap();
let message = ApprovalRequest::from(request.into_inner());
let message = ApprovalRequest::from(request);
let approval_id = message.id;
tracing::info!("An approval request is created: {approval_id}");

Expand Down Expand Up @@ -72,7 +80,8 @@ mod tests {

#[tokio::test]
async fn test_heartbeat() {
let phantasm = Phantasm::open().unwrap();
let config = Configuration::default();
let phantasm = Phantasm::open(&config).unwrap();
let service = Arc::new(phantasm);

let request = Request::new(());
Expand Down