Skip to content

Commit cccc8de

Browse files
feat: implement nebu serve --docker
1 parent a243b99 commit cccc8de

File tree

6 files changed

+148
-10
lines changed

6 files changed

+148
-10
lines changed

deploy/docker/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
```bash
2+
docker compose up
3+
```
4+
5+
6+
You can also launch it through the CLI:
7+
8+
```bash
9+
nebu serve --docker
10+
```

deploy/docker/docker-compose.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
services:
2+
tailscale:
3+
image: tailscale/tailscale:latest
4+
container_name: tailscale
5+
hostname: nebulous
6+
environment:
7+
- TS_STATE_DIR=/var/lib/tailscale
8+
- TS_USERSPACE=true
9+
- TS_AUTH_KEY=
10+
- TS_EXTRA_ARGS=--login-server https://headscale.nebulous.sh
11+
volumes:
12+
- nebu-ts-authkey:/var/lib/tailscale
13+
- /dev/net/tun:/dev/net/tun
14+
cap_add:
15+
- NET_ADMIN
16+
- SYS_MODULE
17+
restart: unless-stopped
18+
19+
nebulous:
20+
image: us-docker.pkg.dev/agentsea-dev/nebulous/server:latest
21+
command: ["sh", "-c", "exec nebu serve --host 0.0.0.0 --port 3000"]
22+
environment:
23+
DATABASE_URL: postgres://postgres:postgres@postgres:5432/postgres
24+
REDIS_URL: redis://localhost:6379
25+
RUST_LOG: debug
26+
NEBU_BUCKET_NAME: nebulous
27+
NEBU_BUCKET_REGION: us-east-1
28+
NEBU_ROOT_OWNER: me
29+
network_mode: service:tailscale
30+
31+
redis:
32+
image: redis:latest
33+
network_mode: service:tailscale
34+
restart: unless-stopped
35+
36+
postgres:
37+
image: postgres:latest
38+
environment:
39+
POSTGRES_PASSWORD: postgres
40+
ports:
41+
- "5432:5432"
42+
restart: unless-stopped
43+
44+
volumes:
45+
nebu-ts-authkey:
46+
driver: local

src/cli.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,25 @@ pub enum Commands {
4343

4444
/// Serve the API.
4545
Serve {
46-
/// The address to bind to.
46+
/// The address to bind to. Ignored when launching through docker.
4747
#[arg(long, default_value = "127.0.0.1")]
4848
host: String,
4949

5050
/// The port to bind to.
5151
#[arg(long, default_value_t = 3000)]
5252
port: u16,
5353

54-
/// Disable internal auth server
55-
#[arg(long, default_value_t = true)]
56-
internal_auth: bool,
54+
/// Disable the internal auth server
55+
#[arg(long, default_value_t = false)]
56+
disable_internal_auth: bool,
5757

5858
/// The port to bind the internal auth server to.
5959
#[arg(long, default_value_t = 8080)]
6060
auth_port: u16,
61+
62+
/// Launch through Docker
63+
#[arg(long, default_value_t = false)]
64+
docker: bool,
6165
},
6266

6367
/// Proxy services.

src/commands/login_cmd.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ When you're running nebulous on Kubernetes, use:
8787

8888
let response = server_request("/v1/users/me", reqwest::Method::GET).await?;
8989
let profile: V1UserProfile = response.json().await?;
90-
format!(
90+
println!(
9191
"\nSuccessfully logged into '{}' as '{}'",
9292
config.current_server.clone().unwrap(),
9393
profile.email

src/commands/serve_cmd.rs

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@ use nebulous::create_app_state;
55
use nebulous::proxy::server::start_proxy;
66
use nebulous::resources::v1::containers::controller::ContainerController;
77
use nebulous::resources::v1::processors::controller::ProcessorController;
8+
use serde_json::Value;
89
use std::error::Error;
10+
use std::io::Write;
11+
use std::ops::Add;
12+
use std::process::{Command, Stdio};
913

10-
pub async fn execute(
14+
pub async fn launch_server(
1115
host: String,
1216
port: u16,
13-
internal_auth: bool,
17+
disable_internal_auth: bool,
1418
auth_port: u16,
1519
) -> Result<(), Box<dyn Error>> {
1620
let app_state = create_app_state().await?;
@@ -37,7 +41,7 @@ pub async fn execute(
3741
});
3842
println!("Proxy server started in background");
3943

40-
if internal_auth {
44+
if !disable_internal_auth {
4145
println!("Starting auth server");
4246
tokio::spawn({
4347
let auth_state = app_state.clone();
@@ -63,3 +67,71 @@ pub async fn execute(
6367

6468
Ok(())
6569
}
70+
71+
const BASE_COMPOSE: &str = include_str!("../../deploy/docker/docker-compose.yml");
72+
73+
pub async fn launch_docker(
74+
port: u16,
75+
disable_internal_auth: bool,
76+
auth_port: u16,
77+
) -> Result<(), Box<dyn Error>> {
78+
Command::new("docker")
79+
.args(&["compose", "version"])
80+
.output()
81+
.expect(
82+
"Did not find docker compose. Please ensure that docker is installed and in your PATH.",
83+
);
84+
85+
// Ask the user for the tailscale login server and auth key
86+
let tailscale_login_server = std::env::var("TS_LOGIN_SERVER").unwrap_or_else(|_| {
87+
println!("Please enter the Tailscale login server URL (or leave blank for default):");
88+
let mut input = String::new();
89+
std::io::stdin().read_line(&mut input).unwrap();
90+
input.trim().to_string()
91+
});
92+
let tailscale_auth_key = std::env::var("TS_AUTH_KEY").unwrap_or_else(|_| {
93+
println!("Please enter the Tailscale auth key:");
94+
let mut input = String::new();
95+
std::io::stdin().read_line(&mut input).unwrap();
96+
input.trim().to_string()
97+
});
98+
99+
let mut doc: Value = serde_yaml::from_str(BASE_COMPOSE)?;
100+
101+
doc["services"]["tailscale"]["environment"][2] =
102+
Value::String(format!("TS_AUTH_KEY={}", tailscale_auth_key));
103+
if !tailscale_login_server.is_empty() {
104+
doc["services"]["tailscale"]["environment"][3] = Value::String(format!(
105+
"TS_EXTRA_ARGS=--login-server {}",
106+
tailscale_login_server
107+
));
108+
}
109+
110+
let mut command = format!(
111+
"exec nebu serve --host 0.0.0.0 --port {} --auth-port {}",
112+
port, auth_port
113+
);
114+
if disable_internal_auth {
115+
command = command.add(" --disable-internal-auth");
116+
};
117+
doc["services"]["nebulous"]["command"][2] = Value::String(command);
118+
119+
let yaml = serde_yaml::to_string(&doc)?;
120+
121+
let mut child = Command::new("docker")
122+
.args(&["compose", "-f", "-", "up"])
123+
.stdin(Stdio::piped())
124+
.spawn()?;
125+
126+
{
127+
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
128+
stdin.write_all(yaml.as_bytes())?;
129+
}
130+
131+
let status = child.wait()?;
132+
if !status.success() {
133+
Err("docker compose failed".into())
134+
} else {
135+
Ok(())
136+
}
137+
}

src/main.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,16 @@ async fn main() -> Result<(), Box<dyn Error>> {
2929
Commands::Serve {
3030
host,
3131
port,
32-
internal_auth,
32+
disable_internal_auth,
3333
auth_port,
34+
docker,
3435
} => {
35-
commands::serve_cmd::execute(host, port, internal_auth, auth_port).await?;
36+
if docker {
37+
commands::serve_cmd::launch_docker(port, disable_internal_auth, auth_port).await?;
38+
} else {
39+
commands::serve_cmd::launch_server(host, port, disable_internal_auth, auth_port)
40+
.await?;
41+
}
3642
}
3743
Commands::Sync { command } => match command {
3844
SyncCommands::Volumes {

0 commit comments

Comments
 (0)