start refactor of ip management

This commit is contained in:
2025-12-14 21:22:07 +00:00
parent 3b5e7e1986
commit 7cabefc76e
13 changed files with 1500 additions and 777 deletions

2
.gitignore vendored
View File

@@ -1 +1,3 @@
/target/ /target/
/mdeploy.db
/mrdeploy_data/

1690
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,14 +4,14 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
bollard = "0.14" bollard = "0.19"
futures = "0.3" futures = "0.3"
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
rusqlite = { version = "0.29.0", features = ["bundled"] } rusqlite = { version = "0.29.0", features = ["bundled"] }
actix-web = "4" actix-web = "4"
env_logger = "*" env_logger = "0.11"
derive_more = "*" derive_more = "*"
log = "*" log = "0.4"
serde = "*" serde = "*"
serde_json = "*" serde_json = "*"
fallible-iterator = "*" fallible-iterator = "*"

View File

@@ -1,9 +1,9 @@
use derive_more::{Display, Error}; use derive_more::{Display, Error};
impl actix_web::error::ResponseError for ReturnedError{} impl actix_web::error::ResponseError for ReturnedError {}
#[derive(Debug, Display, Error)] #[derive(Debug, Display, Error)]
#[display(fmt = "my error: {}", name)] #[display("my error: {}", name)]
pub struct ReturnedError { pub struct ReturnedError {
pub name: String, pub name: String,
} }

View File

@@ -1,17 +1,17 @@
use actix_web::{patch, put, delete, web, App, HttpServer, Responder};
use crate::api::error::ReturnedError; use crate::api::error::ReturnedError;
use crate::controller::Controller; use crate::controller::Controller;
use crate::deploy::starter;
use crate::deploy::container_options::Options; use crate::deploy::container_options::Options;
use crate::deploy::starter;
use actix_web::{delete, get, patch, put, web, App, HttpServer, Responder};
use serde::Deserialize; use serde::Deserialize;
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct ConainerParams { struct ConainerParams {
name: String, name: String,
ip: String, ip: Option<String>,
image: String, image: String,
msg: Option<String>, msg: Option<String>,
memory: Option<String> memory: Option<String>,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@@ -20,30 +20,58 @@ struct ConainerStopParams {
} }
#[put("/container")] #[put("/container")]
async fn create_container(controller: web::Data<Controller>, async fn create_container(
params: web::Query<ConainerParams>) -> impl Responder { controller: web::Data<Controller>,
params: web::Query<ConainerParams>,
) -> impl Responder {
let ops = Options::new(params.memory.clone(), params.msg.clone()); let ops = Options::new(params.memory.clone(), params.msg.clone());
controller.create_container(params.name.clone(), params.ip.clone(), params.image.clone(), ops).await controller
.create_container(
params.name.clone(),
params.ip.clone(),
params.image.clone(),
ops,
)
.await
} }
#[patch("/container/stop")] #[patch("/container/stop")]
async fn stop_container(controller: web::Data<Controller>, async fn stop_container(
params: web::Query<ConainerStopParams>) -> Result<String, ReturnedError> { controller: web::Data<Controller>,
params: web::Query<ConainerStopParams>,
) -> Result<String, ReturnedError> {
match controller.stop_container(params.name.clone()).await { match controller.stop_container(params.name.clone()).await {
Ok(response) => Ok(response), Ok(response) => Ok(response),
Err(e) => Err(ReturnedError { name: e.to_string()}) Err(e) => Err(ReturnedError {
name: e.to_string(),
}),
} }
} }
#[delete("/container")] #[delete("/container")]
async fn delete_container(controller: web::Data<Controller>, async fn delete_container(
params: web::Query<ConainerStopParams>) -> Result<String, ReturnedError> { controller: web::Data<Controller>,
params: web::Query<ConainerStopParams>,
) -> Result<String, ReturnedError> {
match controller.delete_container(params.name.clone()).await { match controller.delete_container(params.name.clone()).await {
Ok(response) => Ok(response), Ok(response) => Ok(response),
Err(e) => Err(ReturnedError { name: e.to_string()}) Err(e) => Err(ReturnedError {
name: e.to_string(),
}),
} }
} }
#[get("/container/{container_name}/ip")]
async fn get_ip(
controller: web::Data<Controller>,
container_name: web::Path<String>,
) -> Result<String, ReturnedError> {
Ok(controller
.get_ip(container_name.into_inner())
.await
.unwrap())
}
pub async fn start() -> std::io::Result<()> { pub async fn start() -> std::io::Result<()> {
let controller = starter::start_docker().await; let controller = starter::start_docker().await;
let data = web::Data::new(controller); let data = web::Data::new(controller);
@@ -54,9 +82,9 @@ pub async fn start() -> std::io::Result<()> {
.service(create_container) .service(create_container)
.service(stop_container) .service(stop_container)
.service(delete_container) .service(delete_container)
.service(get_ip)
}) })
.bind(("127.0.0.1", 8080))? .bind(("127.0.0.1", 8080))?
.run() .run()
.await .await
} }

View File

@@ -1,12 +1,12 @@
use bollard::Docker;
use bollard::errors::Error;
use crate::deploy;
use crate::deploy::container::Container;
use crate::database::exposer::MemStorage; use crate::database::exposer::MemStorage;
use crate::database::instance::Instance; use crate::database::instance::Instance;
use crate::deploy;
use crate::deploy::container::Container;
use crate::deploy::container_options::Options; use crate::deploy::container_options::Options;
use std::sync::Mutex; use bollard::errors::Error;
use bollard::Docker;
use log::error; use log::error;
use std::sync::Mutex;
pub struct Controller { pub struct Controller {
driver: Docker, driver: Docker,
@@ -16,7 +16,11 @@ pub struct Controller {
} }
impl Controller { impl Controller {
pub async fn new(driver: Docker, network: String, range: String) -> Result<Self, bollard::errors::Error>{ pub async fn new(
driver: Docker,
network: String,
range: String,
) -> Result<Self, bollard::errors::Error> {
deploy::network::Network::new(driver.clone(), network.clone(), range).await?; deploy::network::Network::new(driver.clone(), network.clone(), range).await?;
let cont = Self { let cont = Self {
driver: driver, driver: driver,
@@ -27,60 +31,86 @@ impl Controller {
Ok(cont) Ok(cont)
} }
pub async fn create_container(&self, domain: String, ip: String, image: String, ops: Options) -> String { pub async fn create_container(
&self,
domain: String,
ip: Option<String>,
image: String,
ops: Options,
) -> String {
let is_stored = self.storage.lock().unwrap().search_instance(domain.clone()); let is_stored = self.storage.lock().unwrap().search_instance(domain.clone());
match is_stored { match is_stored {
Some(c) => match c.docker_id { Some(c) => match c.docker_id {
Some(id) => id, Some(id) => id,
None => "Container without docker_id".to_string() None => "Container without docker_id".to_string(),
}, },
None => { None => {
match self.load_container(None,domain.clone(),ip.clone(),image.clone(),ops).await { match self
.load_container(None, domain.clone(), ip.clone(), image.clone(), ops)
.await
{
Ok(c) => { Ok(c) => {
self.storage.try_lock().unwrap().new_instance(c.clone()).unwrap(); self.storage
.try_lock()
.unwrap()
.new_instance(c.clone())
.unwrap();
c.get_id() c.get_id()
}, }
Err(_e) => "error creating container".to_string(), Err(e) => format!("error creating container: {}", e.to_string()),
} }
} }
} }
} }
pub async fn start_container_from_instance(&self, instance: Instance) { pub async fn start_container_from_instance(&self, instance: Instance) {
let image = match self.storage.lock().unwrap().id_to_image(instance.image.clone()) { let image = match self
.storage
.lock()
.unwrap()
.id_to_image(instance.image.clone())
{
Ok(i) => i, Ok(i) => i,
Err(e) => { Err(e) => {
error!("image not found: {}", e); error!("image not found: {}", e);
return return;
}, }
}; };
match self.load_container( match self
.load_container(
Some(instance.docker_id.clone()), Some(instance.docker_id.clone()),
instance.domain.clone(), instance.domain.clone(),
instance.ip.clone(), instance.ip.clone(),
image, image,
Options::new(None, None)).await { Options::new(None, None),
)
.await
{
Ok(c) => { Ok(c) => {
self.storage.lock().unwrap().loaded_instance(instance, c); self.storage.lock().unwrap().loaded_instance(instance, c);
}, }
Err(e) => error!("{}",e), Err(e) => error!("{}", e),
} }
} }
async fn load_container(&self, docker_id: Option<String>, async fn load_container(
&self,
docker_id: Option<String>,
domain: String, domain: String,
ip: String, ip: Option<String>,
image: String, image: String,
ops: Options) -> Result<deploy::container::Container, ops: Options,
bollard::errors::Error> ) -> Result<deploy::container::Container, bollard::errors::Error> {
{ deploy::container::Container::new(
deploy::container::Container::new(self.driver.clone(), self.driver.clone(),
docker_id, docker_id,
domain, domain,
ip, ip,
image, image,
self.network.clone(), self.network.clone(),
ops).await ops,
)
.await
} }
pub async fn is_started(&self) -> bool { pub async fn is_started(&self) -> bool {
@@ -91,9 +121,9 @@ impl Controller {
let data = match self.storage.lock().unwrap().get_instances_db() { let data = match self.storage.lock().unwrap().get_instances_db() {
Ok(d) => d, Ok(d) => d,
Err(e) => { Err(e) => {
error!("instances can't be loaded: {}",e); error!("instances can't be loaded: {}", e);
return false return false;
}, }
}; };
println!("instances {}", data.len()); println!("instances {}", data.len());
for instance in data { for instance in data {
@@ -102,14 +132,14 @@ impl Controller {
return true; return true;
} }
async fn stop_given_container(&self, container:Container) -> Result<String, Error> { async fn stop_given_container(&self, container: Container) -> Result<String, Error> {
match container.stop(&self.driver).await { match container.stop(&self.driver).await {
Ok(_i) => Ok(container.get_id()), Ok(_i) => Ok(container.get_id()),
Err(e) => Err(e), Err(e) => Err(e),
} }
} }
async fn prune_given_container(&self, container:Container) -> Result<String, Error> { async fn prune_given_container(&self, container: Container) -> Result<String, Error> {
match container.remove(&self.driver).await { match container.remove(&self.driver).await {
Ok(_i) => Ok(container.get_id()), Ok(_i) => Ok(container.get_id()),
Err(e) => Err(e), Err(e) => Err(e),
@@ -118,30 +148,34 @@ impl Controller {
pub async fn stop_container(&self, domain: String) -> Result<String, Error> { pub async fn stop_container(&self, domain: String) -> Result<String, Error> {
match self.storage.lock().unwrap().stop_instance(domain) { match self.storage.lock().unwrap().stop_instance(domain) {
Some(c) => { Some(c) => self.stop_given_container(c).await,
self.stop_given_container(c).await
},
None => Err(Error::DockerResponseServerError { None => Err(Error::DockerResponseServerError {
status_code: 404, message: "container not found".to_string() status_code: 404,
message: "container not found".to_string(),
}), }),
} }
} }
pub async fn delete_container(&self, domain: String) -> Result<String, Error> { pub async fn delete_container(&self, domain: String) -> Result<String, Error> {
match self.storage.lock().unwrap().remove_instance(domain) { match self.storage.lock().unwrap().remove_instance(domain) {
Some(data) => { Some(data) => match data.1 {
match data.1 {
Some(c) => self.prune_given_container(c).await, Some(c) => self.prune_given_container(c).await,
None => Ok(data.0.docker_id), None => Ok(data.0.docker_id),
}
}, },
None => Err(Error::DockerResponseServerError { None => Err(Error::DockerResponseServerError {
status_code: 404, message: "container not found".to_string() status_code: 404,
message: "container not found".to_string(),
}), }),
} }
} }
pub async fn get_ip(&self, domain: String) -> Result<String, Error> {
let container = self
.storage
.lock()
.unwrap()
.search_instance(domain.clone())
.unwrap();
container.get_ip(&self.driver).await
}
} }

View File

@@ -1,11 +1,11 @@
use rusqlite::{Connection, Result};
use fallible_iterator::FallibleIterator;
use crate::database::instance::Instance; use crate::database::instance::Instance;
use crate::deploy::container_options::Options; use crate::deploy::container_options::Options;
use fallible_iterator::FallibleIterator;
use rusqlite::{types::Null, Connection, Result};
const PATH: &str = "mdeploy.db"; const PATH: &str = "mdeploy.db";
pub struct ConfServer{ pub struct ConfServer {
ip: String, ip: String,
port: String, port: String,
ip_top_limit: u8, ip_top_limit: u8,
@@ -19,9 +19,7 @@ pub struct InstanceStorage {
impl InstanceStorage { impl InstanceStorage {
pub fn new() -> Result<Self> { pub fn new() -> Result<Self> {
let con = Connection::open(PATH)?; let con = Connection::open(PATH)?;
let ret = Self { let ret = Self { con: con };
con : con,
};
ret.create_table()?; ret.create_table()?;
Ok(ret) Ok(ret)
} }
@@ -39,36 +37,42 @@ impl InstanceStorage {
options TEXT, options TEXT,
image INTEGER, image INTEGER,
FOREIGN KEY(image) REFERENCES images(id)); FOREIGN KEY(image) REFERENCES images(id));
COMMIT;" COMMIT;",
) )
} }
fn insert_image(&self, image: String) -> Result<()>{ fn insert_image(&self, image: &str) -> Result<()> {
let mut stmt = self.con.prepare("INSERT INTO images(name) values(?1)")?; let mut stmt = self.con.prepare("INSERT INTO images(name) values(?1)")?;
stmt.execute([image])?; stmt.execute([image])?;
Ok(()) Ok(())
} }
pub fn load_instances(&mut self) -> Result<Vec<Instance>> { pub fn load_instances(&mut self) -> Result<Vec<Instance>> {
let mut stmt = self.con.prepare("select id, docker_id, ip, domain, options, image from l_instances")?; let mut stmt = self
let mut rows = stmt.query([])?; .con
rows.map(|r| Ok(Instance{ .prepare("select id, docker_id, ip, domain, options, image from l_instances")?;
let rows = stmt.query([])?;
rows.map(|r| {
Ok(Instance {
id: r.get::<_, i64>(0)?, id: r.get::<_, i64>(0)?,
docker_id: r.get::<_, String>(1)?, docker_id: r.get::<_, String>(1)?,
ip: r.get::<_, String>(2)?, ip: r.get::<_, String>(2).ok(),
domain: r.get::<_, String>(3)?, domain: r.get::<_, String>(3)?,
ops: Options::new_from_json(r.get::<_, String>(4)?), ops: Options::new_from_json(r.get::<_, String>(4)?),
image: r.get::<_, i64>(5)?, image: r.get::<_, i64>(5)?,
})).collect() })
})
.collect()
} }
pub fn load_images(&mut self) -> Result<Vec<(String, i64)>> { pub fn load_images(&mut self) -> Result<Vec<(String, i64)>> {
let mut stmt = self.con.prepare("select id, name from images")?; let mut stmt = self.con.prepare("select id, name from images")?;
let mut rows = stmt.query([])?; let rows = stmt.query([])?;
rows.map(|row| Ok((row.get::<_, String>(1)?, row.get::<_, i64>(0)?))).collect() rows.map(|row| Ok((row.get::<_, String>(1)?, row.get::<_, i64>(0)?)))
.collect()
} }
fn get_image_id(&self, image: String) -> Result<Option<i64>> { fn get_image_id(&self, image: &str) -> Result<Option<i64>> {
let mut stmt = self.con.prepare("select id from images where name = ?1")?; let mut stmt = self.con.prepare("select id from images where name = ?1")?;
let mut rows = stmt.query([image])?; let mut rows = stmt.query([image])?;
Ok(match rows.next()? { Ok(match rows.next()? {
@@ -83,8 +87,10 @@ impl InstanceStorage {
Ok(rows.next()?.unwrap().get::<_, String>(0)?) Ok(rows.next()?.unwrap().get::<_, String>(0)?)
} }
fn get_instance_id(&self, domain: String) -> Result<Option<i64>> { fn get_instance_id(&self, domain: &str) -> Result<Option<i64>> {
let mut stmt = self.con.prepare("select id from l_instances where domain = ?1")?; let mut stmt = self
.con
.prepare("select id from l_instances where domain = ?1")?;
let mut rows = stmt.query([domain])?; let mut rows = stmt.query([domain])?;
Ok(match rows.next()? { Ok(match rows.next()? {
Some(i) => Some(i.get::<_, i64>(0)?), Some(i) => Some(i.get::<_, i64>(0)?),
@@ -92,14 +98,23 @@ impl InstanceStorage {
}) })
} }
pub fn new_instance(&mut self, docker_id: String, ip: String, domain: String, ops: Options, image: i64) -> Result<Instance>{ pub fn new_instance(
&mut self,
docker_id: String,
ip: Option<String>,
domain: String,
ops: Options,
image: i64,
) -> Result<Instance> {
//let image_id = self.create_or_get_image_id(image.clone(), 0)?; //let image_id = self.create_or_get_image_id(image.clone(), 0)?;
let mut stmt = self.con.prepare("INSERT INTO l_instances(docker_id, ip, domain, options, image) values(?1, ?2, ?3, ?4, ?5)")?; let mut stmt = self.con.prepare("INSERT INTO l_instances(docker_id, ip, domain, options, image) values(?1, ?2, ?3, ?4, ?5)")?;
stmt.execute([docker_id.clone(), stmt.execute([
Some(docker_id.clone()),
ip.clone(), ip.clone(),
domain.clone(), Some(domain.clone()),
ops.to_json(), Some(ops.to_json()),
image.to_string()])?; Some(image.to_string()),
])?;
Ok(Instance { Ok(Instance {
id: image, id: image,
docker_id: docker_id, docker_id: docker_id,
@@ -110,31 +125,30 @@ impl InstanceStorage {
}) })
} }
pub fn remove_instance(&mut self, id: String) -> Result<usize> { pub fn remove_instance(&mut self, id: &str) -> Result<usize> {
let mut stmt = self.con.prepare("DELETE from l_instances where id=?1")?; let mut stmt = self.con.prepare("DELETE from l_instances where id=?1")?;
stmt.execute([id]) stmt.execute([id])
} }
pub fn image_to_id(&self, image: String) -> Result<i64> { pub fn image_to_id(&self, image: &str) -> Result<i64> {
self.create_or_get_image_id(image, 0) self.create_or_get_image_id(image, 0)
} }
fn create_or_get_image_id(&self, image: String, mut depth: i64) -> Result<i64> { fn create_or_get_image_id(&self, image: &str, mut depth: i64) -> Result<i64> {
if depth > 1 { if depth > 1 {
return Err(rusqlite::Error::QueryReturnedNoRows); return Err(rusqlite::Error::QueryReturnedNoRows);
} }
depth+=1; depth += 1;
match self.get_image_id(image.clone()) { match self.get_image_id(image) {
Ok(Some(id)) => Ok(id), Ok(Some(id)) => Ok(id),
Ok(None) => { Ok(None) => {
self.insert_image(image.clone())?; self.insert_image(image)?;
self.create_or_get_image_id(image, depth) self.create_or_get_image_id(image, depth)
} }
Err(_e) => { Err(_e) => {
self.insert_image(image.clone())?; self.insert_image(image)?;
self.create_or_get_image_id(image, depth) self.create_or_get_image_id(image, depth)
} }
} }
} }
} }

View File

@@ -1,8 +1,8 @@
use std::collections::hash_map::HashMap;
use rusqlite::Error;
use crate::deploy::container::Container;
use crate::database::direct_access::InstanceStorage; use crate::database::direct_access::InstanceStorage;
use crate::database::instance::Instance; use crate::database::instance::Instance;
use crate::deploy::container::Container;
use rusqlite::Error;
use std::collections::hash_map::HashMap;
pub struct MemStorage { pub struct MemStorage {
containers: Vec<(Instance, Option<Container>)>, containers: Vec<(Instance, Option<Container>)>,
@@ -13,46 +13,51 @@ pub struct MemStorage {
impl MemStorage { impl MemStorage {
pub fn new() -> Result<Self, Error> { pub fn new() -> Result<Self, Error> {
let mut storage = InstanceStorage::new()?; let mut storage = InstanceStorage::new()?;
let images = storage.load_images()? let images = storage
.into_iter().map(|image|(image.0, image.1)) .load_images()?
.into_iter()
.map(|image| (image.0, image.1))
.collect::<HashMap<_, _>>(); .collect::<HashMap<_, _>>();
Ok(Self{ Ok(Self {
containers: Vec::new(), containers: Vec::new(),
images: images, images: images,
storage: storage, storage: storage,
}) })
} }
fn image_to_id(&mut self, image: String) -> Result<i64, Error> { fn image_to_id(&mut self, image: &str) -> Result<i64, Error> {
match self.images.get(&mut image.clone()) { match self.images.get(image) {
Some(id) => Ok(*id), Some(id) => Ok(*id),
None => { None => {
let id = self.storage.image_to_id(image.clone())?; let id = self.storage.image_to_id(image)?;
self.images.insert(image, id.clone()); self.images.insert(image.to_string(), id.clone());
Ok(id) Ok(id)
}, }
} }
} }
pub fn id_to_image(&mut self, image: i64) -> Result<String, Error>{ pub fn id_to_image(&mut self, image: i64) -> Result<String, Error> {
self.storage.get_image_name(image) self.storage.get_image_name(image)
} }
pub fn new_instance(&mut self, container: Container) -> Result<(), Error> { pub fn new_instance(&mut self, container: Container) -> Result<(), Error> {
let image_id = self.image_to_id(container.image.clone())?; let image_id = self.image_to_id(&container.image)?;
let ins = self.storage.new_instance( let ins = self.storage.new_instance(
container.get_id(), container.get_id(),
container.ip.clone(), container.ip.clone(),
container.name.clone(), container.name.clone(),
container.ops.clone(), container.ops.clone(),
image_id)?; image_id,
)?;
self.containers.push((ins, Some(container))); self.containers.push((ins, Some(container)));
Ok(()) Ok(())
} }
pub fn search_instance(&self, name: String) -> Option<Container> { pub fn search_instance(&self, name: String) -> Option<Container> {
for cont in &self.containers { for cont in &self.containers {
if cont.0.domain == name { return cont.1.clone(); } if cont.0.domain == name {
return cont.1.clone();
}
} }
None None
} }
@@ -71,11 +76,11 @@ impl MemStorage {
match self.containers.get(i) { match self.containers.get(i) {
Some(c) => { Some(c) => {
if c.0.domain == name { if c.0.domain == name {
self.storage.remove_instance(c.0.id.to_string().clone()); _ = self.storage.remove_instance(&c.0.id.to_string());
return Some(self.containers.remove(i)); return Some(self.containers.remove(i));
} }
} }
None => break None => break,
} }
} }
None None
@@ -88,5 +93,4 @@ impl MemStorage {
pub fn get_instances_db(&mut self) -> Result<Vec<Instance>, Error> { pub fn get_instances_db(&mut self) -> Result<Vec<Instance>, Error> {
self.storage.load_instances() self.storage.load_instances()
} }
} }

View File

@@ -4,9 +4,8 @@ use crate::deploy::container_options::Options;
pub struct Instance { pub struct Instance {
pub id: i64, pub id: i64,
pub docker_id: String, pub docker_id: String,
pub ip: String, pub ip: Option<String>,
pub domain: String, pub domain: String,
pub image: i64, pub image: i64,
pub ops: Options, pub ops: Options,
} }

View File

@@ -1,10 +1,10 @@
use std::fs::{canonicalize, create_dir};
use std::vec; use std::vec;
use std::fs::{create_dir, canonicalize};
const BASE_PATH : &str = "mrdeploy_data/"; const BASE_PATH: &str = "mrdeploy_data/";
pub fn generate_bindpaths(mut name: String) -> Vec<String>{ pub fn generate_bindpaths(mut name: String) -> Vec<String> {
name.insert_str(0,BASE_PATH); name.insert_str(0, BASE_PATH);
let path = match canonicalize(name.clone()) { let path = match canonicalize(name.clone()) {
Ok(p) => String::from(p.to_str().unwrap()), Ok(p) => String::from(p.to_str().unwrap()),
Err(_e) => name, Err(_e) => name,
@@ -13,6 +13,6 @@ pub fn generate_bindpaths(mut name: String) -> Vec<String>{
} }
pub fn check_fs(mut name: String) -> Result<(), std::io::Error> { pub fn check_fs(mut name: String) -> Result<(), std::io::Error> {
name.insert_str(0,BASE_PATH); name.insert_str(0, BASE_PATH);
create_dir(name) create_dir(name)
} }

View File

@@ -1,22 +1,21 @@
use bollard::container::{StartContainerOptions,
StopContainerOptions,
CreateContainerOptions,
RemoveContainerOptions,
Config,
NetworkingConfig,
ListContainersOptions};
use bollard::models::{EndpointSettings, EndpointIpamConfig, HostConfig};
use bollard::errors::{Error, Error::IOError};
use std::io::ErrorKind::AlreadyExists;
use std::collections::hash_map::HashMap;
use crate::deploy::bind; use crate::deploy::bind;
use crate::deploy::container_options::Options; use crate::deploy::container_options::Options;
//use bollard::container::InspectContainerOptions;
use bollard::errors::{Error, Error::IOError};
use bollard::models::{EndpointIpamConfig, EndpointSettings, HostConfig, NetworkingConfig};
use bollard::query_parameters::{
CreateContainerOptions, InspectContainerOptions, InspectContainerOptionsBuilder,
ListContainersOptions, RemoveContainerOptions, StartContainerOptions, StopContainerOptions,
};
use bollard::secret::ContainerCreateBody;
use std::collections::hash_map::HashMap;
use std::io::ErrorKind::AlreadyExists;
#[derive(Clone)] #[derive(Clone)]
pub struct Container { pub struct Container {
pub name: String, //Must be the domain name pub name: String, //Must be the domain name
pub start: bool, pub start: bool,
pub ip: String, pub ip: Option<String>,
pub image: String, pub image: String,
pub net: String, pub net: String,
pub docker_id: Option<String>, pub docker_id: Option<String>,
@@ -25,14 +24,15 @@ pub struct Container {
} }
impl Container { impl Container {
pub async fn new(docker: bollard::Docker, pub async fn new(
docker: bollard::Docker,
docker_id: Option<String>, docker_id: Option<String>,
name: String, name: String,
ip: String, ip: Option<String>,
image: String, image: String,
net: String, net: String,
ops: Options) -> Result<Container, Error> { ops: Options,
) -> Result<Container, Error> {
let mut container = Container { let mut container = Container {
name: name, name: name,
ip: ip, ip: ip,
@@ -44,15 +44,15 @@ impl Container {
ops: ops, ops: ops,
}; };
match container.start(docker).await { match container.start(docker).await {
Ok(c) => { Ok(_c) => {
container.start=true; container.start = true;
Ok(container) Ok(container)
} }
Err(e) => Err(e), Err(e) => Err(e),
} }
} }
async fn start(&mut self, docker: bollard::Docker) -> Result<bool, Error>{ async fn start(&mut self, docker: bollard::Docker) -> Result<bool, Error> {
match bind::check_fs(self.name.clone()) { match bind::check_fs(self.name.clone()) {
Ok(_r) => (), Ok(_r) => (),
Err(ref e) if e.kind() == AlreadyExists => (), Err(ref e) if e.kind() == AlreadyExists => (),
@@ -60,23 +60,32 @@ impl Container {
} }
let ret = match self.exist_container(docker.clone()).await? { let ret = match self.exist_container(docker.clone()).await? {
Some(id) => { Some(id) => {
self.docker_id=Some(id); self.docker_id = Some(id);
true true
}, }
None => { None => {
log::info!("creating container with name: {}", self.name); log::info!("creating container with name: {}", self.name);
self.docker_id = Some(docker.create_container(Some( self.docker_id = Some(
create_options(self.name.clone())), docker
create_config(self.name.clone(), .create_container(
Some(create_options(self.name.clone())),
create_config(
self.name.clone(),
self.ip.clone(), self.ip.clone(),
self.net.clone(), self.net.clone(),
self.image.clone(), self.image.clone(),
self.ops.clone())) self.ops.clone(),
.await?.id); ),
)
.await?
.id,
);
false false
}, }
}; };
docker.start_container(&self.name, None::<StartContainerOptions<String>>).await?; docker
.start_container(&self.name, None::<StartContainerOptions>)
.await?;
Ok(ret) Ok(ret)
} }
@@ -88,9 +97,9 @@ impl Container {
} }
async fn exist_container(&self, docker: bollard::Docker) -> Result<Option<String>, Error> { async fn exist_container(&self, docker: bollard::Docker) -> Result<Option<String>, Error> {
let list = docker.list_containers( let list = docker
Some(create_search_container(self.name.clone())) .list_containers(Some(create_search_container(self.name.clone())))
).await?; .await?;
let mut comparable_name = self.name.clone(); let mut comparable_name = self.name.clone();
comparable_name.insert_str(0, "/"); comparable_name.insert_str(0, "/");
for cs in list { for cs in list {
@@ -101,7 +110,7 @@ impl Container {
return Ok(cs.id.clone()); return Ok(cs.id.clone());
} }
} }
}, }
None => (), None => (),
} }
@@ -109,31 +118,65 @@ impl Container {
Ok(None) Ok(None)
} }
pub async fn stop(&self, docker: &bollard::Docker) -> Result<(), Error>{ pub async fn stop(&self, docker: &bollard::Docker) -> Result<(), Error> {
docker.stop_container(self.name.as_str(), Some(create_stop_options())).await docker
.stop_container(self.name.as_str(), Some(create_stop_options()))
.await
} }
pub async fn remove(&self, docker: &bollard::Docker) -> Result<String, Error> { pub async fn remove(&self, docker: &bollard::Docker) -> Result<String, Error> {
docker.remove_container(self.get_id().as_str(), Some(create_remove_option())).await?; docker
.remove_container(self.get_id().as_str(), Some(create_remove_option()))
.await?;
Ok(self.docker_id.clone().unwrap()) Ok(self.docker_id.clone().unwrap())
} }
pub async fn get_ip(&self, docker: &bollard::Docker) -> Result<String, Error> {
match &self.ip {
Some(ip) => Ok(ip.clone()),
None => {
let response = docker
.inspect_container(
&self.name,
Some(InspectContainerOptionsBuilder::new().size(true).build()),
)
.await?;
let ns = response.network_settings.unwrap().networks.unwrap();
let ip = ns.into_values().next().unwrap().ip_address.unwrap();
//let ip =
//let b_ns = ns.is_some();
//let ip = ns.unwrap().ip_address.unwrap();
//log::debug!("network settings: {}, ip address: {}", b_ns, ip);
Ok(ip)
}
}
}
} }
fn create_search_container(name: String) -> ListContainersOptions<String> { fn create_search_container(name: String) -> ListContainersOptions {
let mut filters = HashMap::new(); let mut filters = HashMap::new();
filters.insert("name".to_string(), vec![name]); filters.insert("name".to_string(), vec![name]);
ListContainersOptions{ ListContainersOptions {
all: true, all: true,
filters, filters: Some(filters),
..Default::default() ..Default::default()
} }
} }
fn create_config(name: String, ip: String, net: String, image: String, ops: Options) -> Config<String> { fn create_config(
name: String,
ip: Option<String>,
net: String,
image: String,
ops: Options,
) -> ContainerCreateBody {
let endpoint = EndpointSettings { let endpoint = EndpointSettings {
ipam_config: Some(EndpointIpamConfig{ ipam_config: Some(EndpointIpamConfig {
ipv4_address: Some(ip), ipv4_address: ip,
..Default::default() ..Default::default()
}), }),
..Default::default() ..Default::default()
@@ -141,11 +184,11 @@ fn create_config(name: String, ip: String, net: String, image: String, ops: Opti
let mut hash = HashMap::new(); let mut hash = HashMap::new();
hash.insert(net, endpoint); hash.insert(net, endpoint);
let net = NetworkingConfig { let net = NetworkingConfig {
endpoints_config: hash endpoints_config: Some(hash),
}; };
let mut env = vec!["EULA=TRUE".to_string()]; let mut env = vec!["EULA=TRUE".to_string()];
env.append(&mut ops.generate_config()); env.append(&mut ops.generate_config());
Config { ContainerCreateBody {
image: Some(image), image: Some(image),
env: Some(env), env: Some(env),
host_config: Some(create_hostconfig(name)), host_config: Some(create_hostconfig(name)),
@@ -161,21 +204,22 @@ fn create_hostconfig(name: String) -> HostConfig {
} }
} }
fn create_options(name: String) -> CreateContainerOptions<String> { fn create_options(name: String) -> CreateContainerOptions {
CreateContainerOptions{ CreateContainerOptions {
name: name, name: Some(name),
platform: None, platform: "".to_string(),
} }
} }
fn create_stop_options() -> StopContainerOptions { fn create_stop_options() -> StopContainerOptions {
StopContainerOptions { StopContainerOptions {
t: 30, signal: None,
t: None,
} }
} }
fn create_remove_option() -> RemoveContainerOptions { fn create_remove_option() -> RemoveContainerOptions {
RemoveContainerOptions{ RemoveContainerOptions {
force: true, force: true,
..Default::default() ..Default::default()
} }

View File

@@ -1,13 +1,11 @@
use bollard::errors::Error; use bollard::errors::Error;
use bollard::errors::Error::DockerResponseServerError; use bollard::models::{Ipam, IpamConfig, NetworkCreateRequest};
use bollard::models::{IpamConfig,Ipam}; use bollard::query_parameters::ListNetworksOptions;
use bollard::network::{CreateNetworkOptions, ListNetworksOptions};
use std::collections::hash_map::HashMap; use std::collections::hash_map::HashMap;
pub struct Network { pub struct Network {
name: String, name: String,
range: String, range: String,
} }
impl Network { impl Network {
@@ -24,37 +22,43 @@ impl Network {
async fn check_network(docker: bollard::Docker, name: String) -> bool { async fn check_network(docker: bollard::Docker, name: String) -> bool {
let mut list_networks_filters = HashMap::new(); let mut list_networks_filters = HashMap::new();
list_networks_filters.insert("name", vec![name.as_str()]); list_networks_filters.insert("name".to_string(), vec![name]);
match docker.list_networks(Some(ListNetworksOptions { match docker
filters: list_networks_filters, .list_networks(Some(ListNetworksOptions {
})).await { filters: Some(list_networks_filters),
Ok(l)=> l.len()>0, }))
Err(e)=> false .await
{
Ok(l) => l.len() > 0,
Err(_e) => false,
} }
} }
async fn create_network(docker: bollard::Docker, name: String, range: String) -> Result<Self, Error>{ async fn create_network(
docker: bollard::Docker,
name: String,
range: String,
) -> Result<Self, Error> {
let ipam_config = IpamConfig { let ipam_config = IpamConfig {
subnet: Some(range.clone()), subnet: Some(range.clone()),
..Default::default() ..Default::default()
}; };
let create_network_options = CreateNetworkOptions { let create_network_options = NetworkCreateRequest {
name: name.as_str(), name: name.clone(),
driver: "bridge", driver: Some("bridge".to_string()),
ipam: Ipam { ipam: Some(Ipam {
config: Some(vec![ipam_config]), config: Some(vec![ipam_config]),
..Default::default() ..Default::default()
}, }),
..Default::default() ..Default::default()
}; };
match docker.create_network(create_network_options).await { match docker.create_network(create_network_options).await {
Ok(n)=> Ok(Network { Ok(_n) => Ok(Network {
name: name, name: name,
range: range, range: range,
}), }),
Err(e)=> Err(e), Err(e) => Err(e),
} }
} }
} }

View File

@@ -1,9 +1,9 @@
mod deploy;
mod api; mod api;
mod database;
mod controller; mod controller;
mod database;
mod deploy;
#[actix_web::main] #[actix_web::main]
async fn main() { async fn main() {
api::routes::start().await; _ = api::routes::start().await;
} }