fix some database managements

This commit is contained in:
2023-09-06 21:38:17 +02:00
parent f078e9a032
commit d0f75d89a8
7 changed files with 235 additions and 75 deletions

2
Cargo.lock generated
View File

@@ -1005,8 +1005,10 @@ dependencies = [
"actix-web", "actix-web",
"bollard", "bollard",
"env_logger", "env_logger",
"fallible-iterator",
"futures", "futures",
"rusqlite", "rusqlite",
"serde",
"tokio", "tokio",
] ]

View File

@@ -10,6 +10,8 @@ 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 = "*"
serde = "*"
fallible-iterator = "*"
[target.x86_64-unknown-linux-gnu] [target.x86_64-unknown-linux-gnu]
rustflags = [ rustflags = [

View File

@@ -1,6 +1,7 @@
use std::net::{Ipv4Addr, SocketAddrV4};
use rusqlite::{Connection, Result}; use rusqlite::{Connection, Result};
use std::collections::hash_map::HashMap; use fallible_iterator::FallibleIterator;
pub mod storage;
const PATH: &str = "mdeploy.db"; const PATH: &str = "mdeploy.db";
@@ -11,16 +12,15 @@ pub struct ConfServer{
ip_base_limit: u8, ip_base_limit: u8,
} }
pub struct MInstance { pub struct Instance {
id: i64, id: i64,
ip: String, pub ip: String,
image: i64, pub domain: String,
pub image: i64,
} }
pub struct InstanceStorage { pub struct InstanceStorage {
con: Connection, con: Connection,
instances: HashMap<String, MInstance>,
images: HashMap<String, i64>,
} }
impl InstanceStorage { impl InstanceStorage {
@@ -28,8 +28,6 @@ impl InstanceStorage {
let con = Connection::open(PATH)?; let con = Connection::open(PATH)?;
let ret = Self { let ret = Self {
con : con, con : con,
instances: HashMap::new(),
images: HashMap::new(),
}; };
ret.create_table()?; ret.create_table()?;
Ok(ret) Ok(ret)
@@ -45,75 +43,87 @@ impl InstanceStorage {
ip TEXT, ip TEXT,
domain TEXT, domain TEXT,
image INTEGER, image INTEGER,
FOREIGN KEY(INTEGER) REFERENCES images(id)); FOREIGN KEY(image) REFERENCES images(id));
COMMIT;" COMMIT;"
) )
} }
fn insert_image(&self, image: String) -> Result<()>{ fn insert_image(&self, image: String) -> Result<()>{
let mut stmt = self.con.prepare("INSERT INTO images values(?1)")?; let mut stmt = self.con.prepare("INSERT INTO images(name) values(?1)")?;
stmt.execute([image])?; stmt.execute([image])?;
Ok(()) Ok(())
} }
fn load_instances(&mut self) -> Result<()> { fn load_instances(&mut self) -> Result<Vec<Instance>> {
let mut stmt = self.con.prepare("select id, ip, domain, image from l_instances")?; let mut stmt = self.con.prepare("select id, ip, domain, image from l_instances")?;
let mut rows = stmt.query([])?; let mut rows = stmt.query([])?;
for row in rows.next()? { rows.map(|r| Ok(Instance{
let mut domain = row.get::<_, String>(2)?; id: r.get::<_, i64>(0)?,
self.instances.insert(domain, MInstance { ip: r.get::<_, String>(1)?,
id: row.get::<_, i64>(0)?, domain: r.get::<_, String>(2)?,
ip: row.get::<_, String>(1)?, image: r.get::<_, i64>(3)?,
image: row.get::<_, i64>(3)? })).collect()
,
});
}
Ok(())
} }
fn load_images(&mut self) -> Result<()> { 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 mut rows = stmt.query([])?;
for row in rows.next()? { rows.map(|row| Ok((row.get::<_, String>(1)?, row.get::<_, i64>(0)?))).collect()
self.images.insert(row.get::<_, String>(1)?, row.get::<_, i64>(0)?);
}
Ok(())
} }
fn get_image_id(&self, image: String) -> Result<i64> { fn get_image_id(&self, image: String) -> 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])?;
let id = rows.next()?.unwrap().get::<_, i64>(0)?; Ok(match rows.next()? {
Ok(id) Some(i) => Some(i.get::<_, i64>(0)?),
None => None,
})
} }
fn get_instance_id(&self, domain: String) -> Result<i64> { pub fn get_image_name(&self, id: i64) -> Result<String> {
let mut stmt = self.con.prepare("select name from images where id = ?1")?;
let mut rows = stmt.query([id])?;
Ok(rows.next()?.unwrap().get::<_, String>(0)?)
}
fn get_instance_id(&self, domain: String) -> 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])?;
let id = rows.next()?.unwrap().get::<_, i64>(0)?; Ok(match rows.next()? {
Ok(id) Some(i) => Some(i.get::<_, i64>(0)?),
None => None,
})
} }
fn new_instance(&mut self, domain: String, image: String, ip: String) -> Result<i64>{ pub fn new_instance(&mut self, ip: String, domain: String, 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 values(?1, ?2, ?3)")?; let mut stmt = self.con.prepare("INSERT INTO l_instances(ip, domain, image) values(?1, ?2, ?3)")?;
stmt.execute([ip.clone(), image, image_id.to_string()])?; stmt.execute([ip.clone(),
let instance_id = self.get_instance_id(domain.clone())?; domain.clone(),
self.instances.insert(domain.clone(), MInstance{ image.to_string()])?;
id: instance_id, Ok(Instance {
id: image,
ip: ip, ip: ip,
image: image_id domain: domain,
}); image: image,
Ok(instance_id) })
}
pub fn image_to_id(&self, image: String) -> Result<i64> {
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: String, mut depth: i64) -> Result<i64> {
if depth > 0 { 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.clone()) {
Ok(id) => Ok(id), Ok(Some(id)) => Ok(id),
Ok(None) => {
self.insert_image(image.clone())?;
self.create_or_get_image_id(image, depth)
}
Err(_e) => { Err(_e) => {
self.insert_image(image.clone())?; self.insert_image(image.clone())?;
self.create_or_get_image_id(image, depth) self.create_or_get_image_id(image, depth)

58
src/conf/storage.rs Normal file
View File

@@ -0,0 +1,58 @@
use std::collections::hash_map::HashMap;
use rusqlite::Error;
use crate::deploy::container::Container;
use crate::conf::InstanceStorage;
use crate::conf::Instance;
pub struct MemStorage {
containers: Vec<(Instance, Option<Container>)>,
images: HashMap<String, i64>,
storage: InstanceStorage,
}
impl MemStorage {
pub fn new() -> Result<Self, Error> {
let mut storage = InstanceStorage::new()?;
let images = storage.load_images()?
.into_iter().map(|image|(image.0, image.1))
.collect::<HashMap<_, _>>();
Ok(Self{
containers: Vec::new(),
images: images,
storage: storage,
})
}
fn image_to_id(&mut self, image: String) -> Result<i64, Error> {
match self.images.get(&mut image.clone()) {
Some(id) => Ok(*id),
None => {
let id = self.storage.image_to_id(image.clone())?;
self.images.insert(image, id.clone());
Ok(id)
},
}
}
pub fn id_to_image(&mut self, image: i64) -> Result<String, Error>{
self.storage.get_image_name(image)
}
pub fn new_instance(&mut self, container: Container) -> Result<(), Error> {
let image_id = self.image_to_id(container.image.clone())?;
let ins = self.storage.new_instance(
container.ip.clone(),
container.name.clone(),
image_id)?;
self.containers.push((ins, Some(container)));
Ok(())
}
pub fn loaded_instance(&mut self, ins: Instance, container: Container) {
self.containers.push((ins, Some(container)));
}
pub fn get_instances_db(&mut self) -> Result<Vec<Instance>, Error> {
self.storage.load_instances()
}
}

View File

@@ -1,27 +1,82 @@
use bollard::Docker; use bollard::Docker;
use crate::deploy; use crate::deploy;
use crate::conf::storage::MemStorage;
use crate::conf::Instance;
use std::sync::Mutex;
pub struct Controller { pub struct Controller {
driver: Docker, driver: Docker,
network: String, network: String,
storage: Mutex<MemStorage>,
started: bool,
} }
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?;
Ok(Controller { let cont = Self {
driver: driver, driver: driver,
network: network, network: network,
}) storage: Mutex::new(MemStorage::new().unwrap()),
started: false,
};
Ok(cont)
} }
pub async fn create_container(&self, domain: String, ip: String) -> String { pub async fn create_container(&self, domain: String, ip: String, image: String) -> String {
match deploy::container::Container::new(self.driver.clone(), match self.load_container(domain.clone(),ip.clone(),image.clone()).await {
domain, Ok(c) => {
ip, let ret = c.get_id();
self.network.clone()).await { self.storage.lock().unwrap().new_instance(c).unwrap();
Ok(c) => c.get_id(), ret
Err(e) => "error creating container".to_string(), },
Err(_e) => "error creating container".to_string(),
} }
} }
pub async fn start_container_from_instance(&self, instance: Instance) {
let image = match self.storage.lock().unwrap().id_to_image(instance.image.clone()) {
Ok(i) => i,
Err(_e) => return, //meter logs aquí
};
match self.load_container(
instance.domain.clone(),
instance.ip.clone(),
image).await {
Ok(c) => {
self.storage.lock().unwrap().loaded_instance(instance, c);
},
Err(_e) => (),
}
}
async fn load_container(&self, domain: String,
ip: String,
image: String) -> Result<deploy::container::Container,
bollard::errors::Error>
{
deploy::container::Container::new(self.driver.clone(),
domain.clone(),
ip.clone(),
image.clone(),
self.network.clone()).await
}
pub async fn is_started(&self) -> bool {
self.started
}
pub async fn load_all_instances(&self) -> bool {
let data = match self.storage.lock().unwrap().get_instances_db() {
Ok(d) => d,
Err(_e) => return false,
};
println!("instances {}", data.len());
for instance in data {
self.start_container_from_instance(instance).await;
}
return true;
}
//pub async fn stop_container(&self, )
} }

View File

@@ -1,46 +1,58 @@
use bollard::container::{StartContainerOptions, CreateContainerOptions, Config, NetworkingConfig, ListContainersOptions}; use bollard::container::{StartContainerOptions,
StopContainerOptions,
CreateContainerOptions,
Config,
NetworkingConfig,
ListContainersOptions};
use bollard::models::{EndpointSettings, EndpointIpamConfig}; use bollard::models::{EndpointSettings, EndpointIpamConfig};
use bollard::errors::Error; use bollard::errors::Error;
use std::collections::hash_map::HashMap; use std::collections::hash_map::HashMap;
pub struct Container { pub struct Container {
name: String, pub name: String, //Must be the domain name
start: bool, pub start: bool,
ip: String, pub ip: String,
net: String, pub image: String,
id: Option<String>, pub net: String,
pub docker_id: Option<String>,
pub database_id: Option<i64>
} }
impl Container { impl Container {
pub async fn new(docker: bollard::Docker, pub async fn new(docker: bollard::Docker,
name: String, name: String,
ip: String, ip: String,
image: String,
net: String) -> Result<Container, Error> { net: String) -> Result<Container, Error> {
let mut container = Container { let mut container = Container {
name: name, name: name,
ip: ip, ip: ip,
image: image,
net: net, net: net,
start: false, start: false,
id: None, docker_id: None,
database_id: None,
}; };
match container.create(docker).await { match container.start(docker).await {
Ok(c) => { Ok(c) => {
container.start=true; container.start=true;
container.id = Some(c); container.docker_id = Some(c);
Ok(container) Ok(container)
} }
Err(e) => Err(e), Err(e) => Err(e),
} }
} }
async fn create(&self, docker: bollard::Docker) -> Result<String, Error>{ async fn start(&self, docker: bollard::Docker) -> Result<String, Error>{
let id_container = match self.exist_container(docker.clone()).await? { let id_container = match self.exist_container(docker.clone()).await? {
Some(id_ret) => id_ret, Some(id_ret) => id_ret,
None => docker.create_container(Some( None => docker.create_container(Some(
create_options(self.name.clone())), create_options(self.name.clone())),
create_config(self.ip.clone(), self.net.clone())) create_config(self.ip.clone(),
self.net.clone(),
self.image.clone()))
.await?.id, .await?.id,
}; };
docker.start_container(&self.name, None::<StartContainerOptions<String>>).await?; docker.start_container(&self.name, None::<StartContainerOptions<String>>).await?;
@@ -48,7 +60,7 @@ impl Container {
} }
pub fn get_id(&self) -> String { pub fn get_id(&self) -> String {
match &self.id { match &self.docker_id {
Some(id) => id.clone(), Some(id) => id.clone(),
None => String::from(""), None => String::from(""),
} }
@@ -58,17 +70,21 @@ impl Container {
let list = docker.list_containers( let list = docker.list_containers(
Some(create_search_container(self.name.clone())) Some(create_search_container(self.name.clone()))
).await?; ).await?;
println!("{}", list.len());
Ok (if list.len() > 0 { Ok (if list.len() > 0 {
list[0].id.clone() list[0].id.clone()
} else { } else {
None None
}) })
} }
}
async fn stop(&self, docker: bollard::Docker) -> Result<(), Error>{
docker.stop_container(self.name.as_str(), Some(stop_options())).await
}
}
fn create_search_container(name: String) -> ListContainersOptions<String> { fn create_search_container(name: String) -> ListContainersOptions<String> {
let mut filters = HashMap::new(); let mut filters = HashMap::new();
filters.insert("name".to_string(), vec![name]); filters.insert("before".to_string(), vec![name]);
ListContainersOptions{ ListContainersOptions{
all: true, all: true,
@@ -77,7 +93,7 @@ fn create_search_container(name: String) -> ListContainersOptions<String> {
} }
} }
fn create_config(ip: String, net: String) -> Config<String> { fn create_config(ip: String, net: String, image: String) -> Config<String> {
let endpoint = EndpointSettings { let endpoint = EndpointSettings {
ipam_config: Some(EndpointIpamConfig{ ipam_config: Some(EndpointIpamConfig{
ipv4_address: Some(ip), ipv4_address: Some(ip),
@@ -92,7 +108,7 @@ fn create_config(ip: String, net: String) -> Config<String> {
}; };
let env = vec!["EULA=TRUE".to_string()]; let env = vec!["EULA=TRUE".to_string()];
Config { Config {
image: Some("itzg/minecraft-server:latest".to_string()), image: Some(image),
env: Some(env), env: Some(env),
networking_config: Some(net), networking_config: Some(net),
..Default::default() ..Default::default()
@@ -105,3 +121,9 @@ fn create_options(name: String) -> CreateContainerOptions<String> {
platform: None, platform: None,
} }
} }
fn stop_options() -> StopContainerOptions{
StopContainerOptions {
t: 30,
}
}

View File

@@ -1,17 +1,26 @@
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder}; use actix_web::{get, post, put, web, App, HttpResponse, HttpServer, Responder};
use bollard::Docker; use bollard::Docker;
use serde::Deserialize;
use env_logger; use env_logger;
use crate::controller::Controller; use crate::controller::Controller;
#[derive(Debug, Deserialize)]
struct ConainerParams {
name: String,
ip: String,
image: String,
}
#[get("/")] #[get("/")]
async fn hello() -> impl Responder { async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello world!") HttpResponse::Ok().body("Hello world!")
} }
#[get("/create")] #[put("/container")]
async fn create(controller: web::Data<Controller>) -> impl Responder { async fn create_container(controller: web::Data<Controller>,
controller.create_container("container1".to_string(), "172.20.0.5".to_string()).await params: web::Query<ConainerParams>) -> impl Responder {
controller.create_container(params.name.clone(), params.ip.clone(), params.image.clone()).await
} }
#[post("/echo")] #[post("/echo")]
@@ -34,13 +43,15 @@ pub async fn start() -> std::io::Result<()> {
Ok(c) => c, Ok(c) => c,
Err(e) => panic!("error: {}",e), Err(e) => panic!("error: {}",e),
}; };
controller.load_all_instances().await;
let data = web::Data::new(controller); let data = web::Data::new(controller);
env_logger::init_from_env(env_logger::Env::new().default_filter_or("debug")); env_logger::init_from_env(env_logger::Env::new().default_filter_or("debug"));
HttpServer::new(move || { HttpServer::new(move || {
App::new() App::new()
.wrap(actix_web::middleware::Logger::default())
.app_data(data.clone()) .app_data(data.clone())
.service(hello) .service(hello)
.service(create) .service(create_container)
.service(echo) .service(echo)
.route("/hey", web::get().to(manual_hello)) .route("/hey", web::get().to(manual_hello))
}) })