diff --git a/cli/src/main.rs b/cli/src/main.rs index 5058ace8..19508425 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -48,6 +48,10 @@ enum Command { /// instead of spawning a new one. #[clap(long)] lock: Option, + + /// Optional volume to mount, in the form `host_path:container_path`. + #[clap(long)] + mount: Option, }, Status { backend: Option, @@ -353,6 +357,7 @@ async fn main() -> Result<()> { port, env, lock, + mount, } => { let env: Result> = env .iter() @@ -366,6 +371,12 @@ async fn main() -> Result<()> { .collect(); let env = env?; + let volume_mounts = if let Some(mount) = mount { + vec![mount] + } else { + Vec::new() + }; + let result = nats .request(&ScheduleRequest { backend_id: None, @@ -379,7 +390,7 @@ async fn main() -> Result<()> { resource_limits: ResourceLimits::default(), pull_policy: Default::default(), port, - volume_mounts: Vec::new(), + volume_mounts, }, require_bearer_token: false, lock: lock.map(|lock| lock.try_into().unwrap()), diff --git a/controller/src/http_server.rs b/controller/src/http_server.rs index 968b77a8..8bf17c12 100644 --- a/controller/src/http_server.rs +++ b/controller/src/http_server.rs @@ -24,7 +24,6 @@ use plane_core::{ Never, }; use serde::{Deserialize, Serialize}; -use serde_json::Value; use std::{collections::HashMap, net::SocketAddr, sync::Arc, time::Duration}; use tower_http::cors::{Any, CorsLayer}; @@ -56,7 +55,7 @@ struct HttpSpawnRequest { #[serde(default = "HashMap::default")] env: HashMap, #[serde(default = "Vec::default")] - volume_mounts: Vec, + volume_mounts: Vec, } impl HttpSpawnRequest { diff --git a/core/src/messages/agent.rs b/core/src/messages/agent.rs index a5e16a2c..7751ef68 100644 --- a/core/src/messages/agent.rs +++ b/core/src/messages/agent.rs @@ -9,7 +9,6 @@ use bollard::container::{LogOutput, MemoryStatsStats, Stats}; use chrono::{DateTime, Utc}; use plane_core_nats_macros::{self, TypedMessage}; use serde::{Deserialize, Serialize}; -use serde_json::Value; use serde_with::serde_as; use serde_with::{DurationMicroSeconds, DurationSeconds}; use std::{ @@ -316,10 +315,9 @@ pub struct DockerExecutableConfig { /// Port to serve on. If this is not set, Plane uses port 8080. pub port: Option, - /// Volume mounts to add to the container. - /// Schema: https://pkg.go.dev/github.com/docker/docker@v20.10.22+incompatible/api/types/mount#Mount + /// Volume mounts to add to the container, as a list of : strings. #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub volume_mounts: Vec, + pub volume_mounts: Vec, } impl DockerExecutableConfig { diff --git a/dev/tests/agent.rs b/dev/tests/agent.rs index aff0e9c5..ac87471e 100644 --- a/dev/tests/agent.rs +++ b/dev/tests/agent.rs @@ -26,7 +26,6 @@ use plane_dev::{ }; use plane_drone::{agent::AgentOptions, database::DroneDatabase, ip::IpSource}; use plane_drone::{agent::AgentSupervisors, config::DockerConfig}; -use serde_json::json; use std::net::IpAddr; use std::time::Duration; use tokio::time::Instant; @@ -766,10 +765,7 @@ async fn attempt_to_spawn_with_disallowed_volume_mount() { let mut request = base_spawn_request().await; request.drone_id = drone_id.clone(); - request.executable.volume_mounts = vec![json!({ - "source": "/foo", - "target": "/bar", - })]; + request.executable.volume_mounts = vec!["foo:bar".to_string()]; let mut state_subscription = BackendStateSubscription::new(&connection, &request.backend_id) .await @@ -806,10 +802,7 @@ async fn attempt_to_spawn_with_allowed_volume_mount() { let mut request = base_spawn_request().await; request.drone_id = drone_id.clone(); - request.executable.volume_mounts = vec![json!({ - "Type": "tmpfs", - "Target": "/bar", - })]; + request.executable.volume_mounts = vec!["/tmp/foo:/bar".to_string()]; let mut state_subscription = BackendStateSubscription::new(&connection, &request.backend_id) .await diff --git a/drone/src/agent/engines/docker/mod.rs b/drone/src/agent/engines/docker/mod.rs index 1c26f587..42407db9 100644 --- a/drone/src/agent/engines/docker/mod.rs +++ b/drone/src/agent/engines/docker/mod.rs @@ -19,7 +19,7 @@ use bollard::{ }, image::CreateImageOptions, models::{HostConfig, ResourcesUlimits}, - service::{DeviceRequest, HostConfigLogConfig, Mount}, + service::{DeviceRequest, HostConfigLogConfig}, system::EventsOptions, Docker, API_DEFAULT_VERSION, }; @@ -190,19 +190,12 @@ impl DockerInterface { None }; - let mounts = if self.allow_volume_mounts { - let c: std::result::Result, serde_json::Error> = executable_config - .volume_mounts - .iter() - .map(|value| serde_json::from_value(value.clone())) - .collect(); - - Some(c?) + let binds = if self.allow_volume_mounts { + Some(executable_config.volume_mounts.clone()) } else { if !executable_config.volume_mounts.is_empty() { return Err(anyhow::anyhow!("Spawn attempt named volume mounts, but these are not enabled on this drone. Set `allow_volume_mounts` to true in the `docker` section of the Plane config to enable.")); } - None }; @@ -226,6 +219,7 @@ impl DockerInterface { .collect(), ), host_config: Some(HostConfig { + binds, network_mode: self.network.clone(), runtime: self.runtime.clone(), memory: executable_config.resource_limits.memory_limit_bytes, @@ -264,7 +258,6 @@ impl DockerInterface { .collect() }), device_requests, - mounts, log_config: self.syslog.as_ref().map(|d| HostConfigLogConfig { typ: Some("syslog".to_string()), diff --git a/sample-config/auth/ca-cert.pem b/sample-config/auth/ca-cert.pem new file mode 100644 index 00000000..5d9531fb --- /dev/null +++ b/sample-config/auth/ca-cert.pem @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFoTCCA4mgAwIBAgIUQQ0ocElIKKyXUAUGX9ZeKKAlu68wDQYJKoZIhvcNAQEL +BQAwYDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMREwDwYDVQQHDAhOZXcgWW9y +azEOMAwGA1UECgwFUGxhbmUxDDAKBgNVBAsMA09yZzETMBEGA1UEAwwKcGxhbmUu +dGVzdDAeFw0yNDAxMjExMzQ2NTFaFw0zNDAxMTgxMzQ2NTFaMGAxCzAJBgNVBAYT +AlVTMQswCQYDVQQIDAJOWTERMA8GA1UEBwwITmV3IFlvcmsxDjAMBgNVBAoMBVBs +YW5lMQwwCgYDVQQLDANPcmcxEzARBgNVBAMMCnBsYW5lLnRlc3QwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQDtxqX3Z5cNJkS/3iFuNx56pKR8H+qAEMSj +S3Bln1SrOSUd3E5xRev1udoY0lmETpaMfjOcy4A4sojfYCLrVmqZfw8J1t3RlGrJ +kmH1oOu5t+G0wMIx8l6QsBWCst+cHfRQRbYxfTO1Q14XcDR0xmxeFBkrPt0/Kv0P +zjXCQJE+tLfoo8L2ZaHjqbYR9xHuN21UhA8re6C144ldulJ/a4lTZNA+DP7rDa+R +UZcKLEcahPcNMpabdT1VGsQoCYOPhrOu8ZqEdzHL4kiuLSSi1LKUs/UNxtiAYTlC +N+ytTGlVII1Xg6Q/ZTArbAcfj8M669SUGdkA+UzuwH3bkpx2euRgLVoi3b/j39h1 +cBkyefD1gPlZ6aEXW2t4yBMePTtdeYpgPa7EiCRgHyqESDvu2CilvgWMuRg5rpTE +Or57deiXSfnOcDX0TfGv5WtZo0fcYmpN8MxVnSZeon15XY4XgS9hSpJysy/kP6OB +mm3FESEKdyAmpq9LNZJoF3evcpE2s6pUlTpaRdc3h9yQfeKFfeiZWuPWqXN9WcAU +pFLEytuHsWDhHUOeNxt/MRp17ha/lhih0/6PWctbcRmrxp5diqJ9/38WcgjaLPws +xm9vHWCAMaJXgnFMCVdR1mSNNWDmbBy2L5Q95QqP44bnxXdZgkD7PU/pN7n54Emb +cC5ODL4ZXQIDAQABo1MwUTAdBgNVHQ4EFgQUQbUgamW3bAvEAKspUvdN6D2IBF8w +HwYDVR0jBBgwFoAUQbUgamW3bAvEAKspUvdN6D2IBF8wDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAdu6j6heE2H3CvSIGq/E+NJtaf9pS3RKfMVPR +s/9Ce5/jakFQeaKqnpHIboSWSXzDk7htr+6QgtwSQIUDUzscXfToQB+JJKOR4/Ke +Banz4SlRCi1qPKxVh6+E/9lfLUN9ux7xl+n9NNr8zydpFSUODyJlWXBjPyoELTxX +NsOTSwZSlDQrPSaYHDmpavG6otTDFStUgiPjnCj2iofUP+wgw9pSNQSZ5qbyhyw2 +XvWqVYBbYUR16Z5+56ANYXpTAMg22Senn9/ZLas6zimtNJ/jde4ccRTAKBXoYU8m +8tUUsGGe6bpNhqadBWKDiNtsyAHAPJom4aPEgkpAv9nIlMhC6XPvJ2tnwpqI0pF1 +lLWF5U68WaQ8xWhIg/QEXIrgUxudv7t66GO5dDVsj4jTF9H1Nlr4f4VD0vLXbdyu +DqM+n3zuNWq1ifonqdpcpf+LDt6ierk8YrUWu2PLT72PgeyZ1W+b0PbQaLFSmppE +//ZbiA0y5qLT7mKaTXzbnrBJ3tFXV+J9+cLFDOBUIAPu/97Z3OUbzVcWG0NhdIw1 +RhIKnsYnDb7nJhqaKzjMX234rc23AU65IUrVGidlIAvIBXHIwXeIeg+GjPN35Cdx +RIk45ljPyGJWpLxSxFaErCwVbDokD2hmzdNXy0zCVwrc3EpdB7+LGMb9Lw90AesB +DQgpzeo= +-----END CERTIFICATE----- diff --git a/sample-config/auth/gencert.sh b/sample-config/auth/gencert.sh new file mode 100755 index 00000000..c1d9c8cf --- /dev/null +++ b/sample-config/auth/gencert.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +# Create CA key + cert +openssl req \ + -nodes \ + -x509 \ + -newkey rsa:4096 \ + -keyout ca-key.pem \ + -out ca-cert.pem \ + -sha256 \ + -days 3650 \ + -subj "/C=US/ST=NY/L=New York/O=Plane/OU=Org/CN=plane.test" + +# Create a certificate signing request. +openssl req \ + -nodes \ + -new \ + -newkey rsa:4096 \ + -keyout site-key.pem \ + -out site-cert.csr \ + -subj "/C=US/ST=NY/L=New York/O=Plane/OU=Org/CN=plane.test" + +echo "subjectAltName = DNS:*.plane.test" > extfile + +# Sign the certificate. +openssl x509 \ + -req \ + -in site-cert.csr \ + -days 365 \ + -CA ca-cert.pem \ + -CAkey ca-key.pem \ + -CAcreateserial \ + -out site-cert.pem \ + -extfile extfile + +# Clean up +rm \ + extfile \ + ca-cert.srl \ + ca-key.pem \ + site-cert.csr diff --git a/sample-config/auth/site-cert.pem b/sample-config/auth/site-cert.pem new file mode 100644 index 00000000..b7863bf5 --- /dev/null +++ b/sample-config/auth/site-cert.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIUb8CYy3ceAE4STmZfmwAjMkZ52VcwDQYJKoZIhvcNAQEL +BQAwYDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMREwDwYDVQQHDAhOZXcgWW9y +azEOMAwGA1UECgwFUGxhbmUxDDAKBgNVBAsMA09yZzETMBEGA1UEAwwKcGxhbmUu +dGVzdDAeFw0yNDAxMjExMzQ2NTJaFw0yNTAxMjAxMzQ2NTJaMGAxCzAJBgNVBAYT +AlVTMQswCQYDVQQIDAJOWTERMA8GA1UEBwwITmV3IFlvcmsxDjAMBgNVBAoMBVBs +YW5lMQwwCgYDVQQLDANPcmcxEzARBgNVBAMMCnBsYW5lLnRlc3QwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQDLH12JBRzP2+duSh+4jfbchdL12+bpk1Db +TMo7WeWyEuw3TiyFc16uN8gYun/VbaS6KIKPGl8rEg5Z4b10kb4vVNKdk2xVAVra +gRlGIwpOGliwxivibrjbWsraUJGyY+CqfxCK9p8wW81Ucoyo65teH/dtDb+xySNC +iV9vWGl0pgSPD1n3vQyX+mUzyGcdvEt50eCr9Omqqe3OWp+E++MhBAAc+ZrIc5LA +iLchdGxJifewCHcslCWIBybn/Z03HDNAtlRsovblgKAN+8soqIDBk5uFui4nl0h/ +zb0vleEH5/HjidhnHRX17yMG6q7UN3kuJGQ/UuJywlST8/rORfEPQqiu/9i72xro +ffW3pRXHip/SFBA3b0NqxK1dEkM+QMtpHnZTEr6x34IZ7Vw8LHiwhn45lD1PI2/f +AQAO07efeboCIvHiOaAInnd7aPVRklhLYzmbv1jyWeRy84/kRp7qtFt4ngeKlqqb +uMdKO183Nb06hUeRGErg4x59oJG3dbMFSJAsOD64x644nHOkibAiBiqLtmiCmwOG +VAabg8bL9kKruTfZSqpLAijgWkmAw48uiOUfxqZLH1XEhgCG0scwDJXbbtR2nhm3 +m6H5G9SoGpDq/tLhy15Q365RsNi6PvOu7mwJpi9/erplchvpTlx5PM7lDRaTCrLR +mVObc6FjowIDAQABoxswGTAXBgNVHREEEDAOggwqLnBsYW5lLnRlc3QwDQYJKoZI +hvcNAQELBQADggIBAJgcSyxFI9M8Gc3g9Znh5nruMQDUuGmY+vRiRMIjylrTUCp9 +hPGIy9jQzuCnqebx8oj8zWwSXCygwwTWvIqLr+aoa4RqpgRtX+WqK15LdtB03B8b +nWt6pNeuNDyGqFEN310aaAslwedAErEDCh7AHbkJNt3pD2RqFTR29bl5ydwcHqjr +OL0RcSz3EQy38YsC68SvPixebgK6AiAyWFeNxq8/JMOCY5y8PSZPeYlkPrYker5g +shd35uHODMCHLs33NQ0Y4iJv8YhswKeZuckepj+eUAoEQME0i3e7bEzPHfsFlY1Z +LZsZShReP2wPyYqRr+YaC/W5xuf9fcp/lqMi9CecP5oBcINsbRY24qhyrXw6c0nH +tcnwWrOr17WLwLlDkUmq5IWTntp26VnVwCT4ZzjfkopAx2xGAxrlVXcWMapqw/C9 +fEOT/Y5aahRh2rCAeogntuHcRAul0YEwZPYWILeMZnjnBsjMJoIDUbuMqGQmB51a +QkuFjRCL7JM/0I9aHKnR1/1SSbwEC5Y8VxII40hoT2uiebj8hyzCiTm0S4E4+Ajy +Z/2ZoZqDPshzJF5LlcbMGgA+gT06hHVV8om+mSezgvmk5sr+AsuFhDfZF41ahhHP +BRoaZ4nCROO5Cg0rYGpwy4+AwMy//G7u6lq5rCbvv0nhEWtfSV7GXkinLHzQ +-----END CERTIFICATE----- diff --git a/sample-config/auth/site-key.pem b/sample-config/auth/site-key.pem new file mode 100644 index 00000000..242db105 --- /dev/null +++ b/sample-config/auth/site-key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDLH12JBRzP2+du +Sh+4jfbchdL12+bpk1DbTMo7WeWyEuw3TiyFc16uN8gYun/VbaS6KIKPGl8rEg5Z +4b10kb4vVNKdk2xVAVragRlGIwpOGliwxivibrjbWsraUJGyY+CqfxCK9p8wW81U +coyo65teH/dtDb+xySNCiV9vWGl0pgSPD1n3vQyX+mUzyGcdvEt50eCr9Omqqe3O +Wp+E++MhBAAc+ZrIc5LAiLchdGxJifewCHcslCWIBybn/Z03HDNAtlRsovblgKAN ++8soqIDBk5uFui4nl0h/zb0vleEH5/HjidhnHRX17yMG6q7UN3kuJGQ/UuJywlST +8/rORfEPQqiu/9i72xroffW3pRXHip/SFBA3b0NqxK1dEkM+QMtpHnZTEr6x34IZ +7Vw8LHiwhn45lD1PI2/fAQAO07efeboCIvHiOaAInnd7aPVRklhLYzmbv1jyWeRy +84/kRp7qtFt4ngeKlqqbuMdKO183Nb06hUeRGErg4x59oJG3dbMFSJAsOD64x644 +nHOkibAiBiqLtmiCmwOGVAabg8bL9kKruTfZSqpLAijgWkmAw48uiOUfxqZLH1XE +hgCG0scwDJXbbtR2nhm3m6H5G9SoGpDq/tLhy15Q365RsNi6PvOu7mwJpi9/erpl +chvpTlx5PM7lDRaTCrLRmVObc6FjowIDAQABAoICAAHCq9TcLnu+tCrIia3XzSPs +JP/sf/vWQwUjjT0mck8wystP6iLd9Rwdb6aHN0RqRts2Lqt+1wRQi79LrZCtErDm +U0z6Uh4KfPcX11g8RYKXx90AWrEDtKZyCwAJYrnhE7lmkIYjzQ1mPklVFNMB977B +GCvhyp+H1r5k4mbcpU1VgwSaZXU4Nju699WTDxG66AyOPRxAayYHgwAe2PMWG8nV +dwrJPwYtuj/g73VixO1+PJ9R83Z3cjvTlAq+r/YiUG9nJSQr5WfnzXtRi1k23KbQ +2LA4BN5i+UXiSpl2+xROdlI6h52dKuuOvwb4UbuVH3++4P+LgFruRD1kB/Bxur0b +KEExHOc82Gvp67atU25IGheUTfpH8xPvOyFXSMk3PuF+Xhv0fGoZtUAOudeVsPUp +wEJch0wAv4oUS0GGuq/jOgGbJXCYZvruDVy/mgHebjuRDfCPe9kLI8rsxZi54rE7 +dVgRdigp3wK2vYV8RucX5NaMQUFd4aEdaHAeAPHRKWnyjcbiHEI/Qu5IAz0pdd+3 +x5uQZNucPgnrlVwTolypyILoaq720Ubdar3hwibsBAIeO9e2x4DlRrVsgDdOmXdZ +INXhu8eBlxznN0qJawaH9lUUF1A/ORZjEKokFOy5+yz8QzBodB0N0mqOTOwlqa6z +7s31BGK9kgvvS1fsIavBAoIBAQD1G9qjrDHgUy0W8ak+P3zBXr6Dy9/N8Mypk333 +H6WOIx52p1kXygjvUcIK4qAR4GhIOCn6bomlXe+VP1RXAEYusM66OQrcd7zGOENg +6azs+ymD/UWwjrgOidw83infW5E1CVtlGCJSeDGI8c1MuGnUX3ohXdmujXPqdBt2 +l7AC6t3P4ntgAmHrvpKcZY+Ofvy8UI0Yonl6Iy4pE3zn2/dU06Is34R28A5Bhw19 +643C1U+th/TbbRFUMfNbGL7z1HYXjY0q4UJqgEHlZePo1Y8DzBNlj+46H6vCC6w1 +Nslws1Rgx9NQUrjs01Z60n/6vX1M/mvIyYnqx0/rBCOPrtG/AoIBAQDUJelgo13s +guICOd8PWhMcA+yfymNhnJXanwWuAuuc+uNVDtTBGVp149RKLqchj8jTRDUa3PRY +as0+FKP1aKXzpautqLt9zKdStPFJgfdgz5zI7EjL5tXK+hveHwZa5822DASFdBVS +/d6Ur4bwR4lqaUVOXisusw+opEmge+mnaQGgdvVnC4CKR0qIYkjynMiZer220qdH +y4nqX3EXNmoy7rnOVQE4k56xfEfRiOkDAtMHjPdCHIb2lfr9GIsb5TqXNAT+sb5m +s2DUy4SnebZj5qBsA7NTWPYsfD5i0HMaA4YS+KR34YEsygsJjNC5jpgNyGkfMEzA +BBFNSJwu8p8dAoIBAHSATsWo3PQQ1MepCZZqurN2gvtF9hR+teiMED1br2cFzj77 +f99Myj5mD+L4oLiefzKEb3929rnBElVQCphnqvQaWcgtwLnVt8ZHi88OgoEJSExS +H8Oxsk5GvIW52xEtBrG8xtIgTIACXRkIRVqH9NVapEQjxYmv9j6j/UG7C4/3boKW +1almuIBCzpDDvR4vwZUIfGgPIcop2pDvkFjsNnz9MoI9o0qdA0mmFYWaZoEKvppu +mrWI+8mXDGJqOqjOfWrofwa4lsbkO237pf5PM3DMmROW+9Zoo0zSG4IXjluhASQX +FpuBFiCKuvoZ/3aAhoQIEdcX5BPRtGyyROzQXSUCggEBAKZFtH02bXXiOwstmmMo +4NNTTBENCglGNbksa8IvrcTSanyO7DcQDPNbBlbB6+IFFIxo6ApO5P+rbYNboAnG +5FJrJXwtRA3b6cSL5pULVPy+m45qir+tAyFcF51myY9UIMmu75g9HD14lgwI6oTG +PLB3I4fBjKzEUOCHKuGqXL4GMe03by09OZinOYZdKflk4mBQcAKrZcqCf2x/M17T +GjcjHePWged646UN2Cgk2yOuAgHn/R8GxszbTeUyaE0Lw3kgn02Tt7w8mQcPH8A3 +R7lS16bw24rWrdK96hN+dsgLvPh/gkAnMRwOmsn4YNseJJSg36s/KthQF9NvtmGh +bQ0CggEBANrgntR2tj5Aq0KKeDGgJGTbmLuCbjZOSZeYignYGSwSsv+k9NWnXQjE +V88GQ2lZh2myXj7OmuIDtdLNLjPxuri8y45Q9dORHaffusuXi7sLZZ6O4GHT6wOX +C93A4SRux6mwYaF7UHNfpBBFjC31H2dpTiNrWiyLOkBnU8/nUBzrex4OJnCidfIt +Wdy4D6S1hISDwMUjhMEPOHP6/DniH9A/EVvMo2jAU1lCv2AsuTI5QOhD8eYY5PRi +9fEkauN2QT0M5ol17TrsCPQxu7GtBLwHEfiKmImAwYeuC4d6pQF3VfD/zgPYDiWv +hIsDwbYMspk8BKNPPrIJA3NLI8GcqnU= +-----END PRIVATE KEY----- diff --git a/sample-config/dev-config/drone.toml b/sample-config/dev-config/drone.toml index 1a1a185d..a380e14b 100644 --- a/sample-config/dev-config/drone.toml +++ b/sample-config/dev-config/drone.toml @@ -6,6 +6,9 @@ hosts = ["127.0.0.1"] [agent] ip = "127.0.0.1" +[agent.docker] +allow_volume_mounts = true + [proxy] bind_ip = "127.0.0.1" http_port = "8080"