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",
"bollard",
"env_logger",
"fallible-iterator",
"futures",
"rusqlite",
"serde",
"tokio",
]

View File

@@ -10,6 +10,8 @@ tokio = { version = "1", features = ["full"] }
rusqlite = { version = "0.29.0", features = ["bundled"] }
actix-web = "4"
env_logger = "*"
serde = "*"
fallible-iterator = "*"
[target.x86_64-unknown-linux-gnu]
rustflags = [

View File

@@ -1,6 +1,7 @@
use std::net::{Ipv4Addr, SocketAddrV4};
use rusqlite::{Connection, Result};
use std::collections::hash_map::HashMap;
use fallible_iterator::FallibleIterator;
pub mod storage;
const PATH: &str = "mdeploy.db";
@@ -11,16 +12,15 @@ pub struct ConfServer{
ip_base_limit: u8,
}
pub struct MInstance {
pub struct Instance {
id: i64,
ip: String,
image: i64,
pub ip: String,
pub domain: String,
pub image: i64,
}
pub struct InstanceStorage {
con: Connection,
instances: HashMap<String, MInstance>,
images: HashMap<String, i64>,
}
impl InstanceStorage {
@@ -28,8 +28,6 @@ impl InstanceStorage {
let con = Connection::open(PATH)?;
let ret = Self {
con : con,
instances: HashMap::new(),
images: HashMap::new(),
};
ret.create_table()?;
Ok(ret)
@@ -45,75 +43,87 @@ impl InstanceStorage {
ip TEXT,
domain TEXT,
image INTEGER,
FOREIGN KEY(INTEGER) REFERENCES images(id));
FOREIGN KEY(image) REFERENCES images(id));
COMMIT;"
)
}
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])?;
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 rows = stmt.query([])?;
for row in rows.next()? {
let mut domain = row.get::<_, String>(2)?;
self.instances.insert(domain, MInstance {
id: row.get::<_, i64>(0)?,
ip: row.get::<_, String>(1)?,
image: row.get::<_, i64>(3)?
,
});
}
Ok(())
rows.map(|r| Ok(Instance{
id: r.get::<_, i64>(0)?,
ip: r.get::<_, String>(1)?,
domain: r.get::<_, String>(2)?,
image: r.get::<_, i64>(3)?,
})).collect()
}
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 rows = stmt.query([])?;
for row in rows.next()? {
self.images.insert(row.get::<_, String>(1)?, row.get::<_, i64>(0)?);
}
Ok(())
rows.map(|row| Ok((row.get::<_, String>(1)?, row.get::<_, i64>(0)?))).collect()
}
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 rows = stmt.query([image])?;
let id = rows.next()?.unwrap().get::<_, i64>(0)?;
Ok(id)
Ok(match rows.next()? {
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 rows = stmt.query([domain])?;
let id = rows.next()?.unwrap().get::<_, i64>(0)?;
Ok(id)
Ok(match rows.next()? {
Some(i) => Some(i.get::<_, i64>(0)?),
None => None,
})
}
fn new_instance(&mut self, domain: String, image: String, ip: String) -> Result<i64>{
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)")?;
stmt.execute([ip.clone(), image, image_id.to_string()])?;
let instance_id = self.get_instance_id(domain.clone())?;
self.instances.insert(domain.clone(), MInstance{
id: instance_id,
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 mut stmt = self.con.prepare("INSERT INTO l_instances(ip, domain, image) values(?1, ?2, ?3)")?;
stmt.execute([ip.clone(),
domain.clone(),
image.to_string()])?;
Ok(Instance {
id: image,
ip: ip,
image: image_id
});
Ok(instance_id)
domain: domain,
image: image,
})
}
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> {
if depth > 0 {
if depth > 1 {
return Err(rusqlite::Error::QueryReturnedNoRows);
}
depth+=1;
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) => {
self.insert_image(image.clone())?;
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 crate::deploy;
use crate::conf::storage::MemStorage;
use crate::conf::Instance;
use std::sync::Mutex;
pub struct Controller {
driver: Docker,
network: String,
storage: Mutex<MemStorage>,
started: bool,
}
impl Controller {
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?;
Ok(Controller {
let cont = Self {
driver: driver,
network: network,
})
storage: Mutex::new(MemStorage::new().unwrap()),
started: false,
};
Ok(cont)
}
pub async fn create_container(&self, domain: String, ip: String) -> String {
match deploy::container::Container::new(self.driver.clone(),
domain,
ip,
self.network.clone()).await {
Ok(c) => c.get_id(),
Err(e) => "error creating container".to_string(),
pub async fn create_container(&self, domain: String, ip: String, image: String) -> String {
match self.load_container(domain.clone(),ip.clone(),image.clone()).await {
Ok(c) => {
let ret = c.get_id();
self.storage.lock().unwrap().new_instance(c).unwrap();
ret
},
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::errors::Error;
use std::collections::hash_map::HashMap;
pub struct Container {
name: String,
start: bool,
ip: String,
net: String,
id: Option<String>,
pub name: String, //Must be the domain name
pub start: bool,
pub ip: String,
pub image: String,
pub net: String,
pub docker_id: Option<String>,
pub database_id: Option<i64>
}
impl Container {
pub async fn new(docker: bollard::Docker,
name: String,
ip: String,
image: String,
net: String) -> Result<Container, Error> {
let mut container = Container {
name: name,
ip: ip,
image: image,
net: net,
start: false,
id: None,
docker_id: None,
database_id: None,
};
match container.create(docker).await {
match container.start(docker).await {
Ok(c) => {
container.start=true;
container.id = Some(c);
container.docker_id = Some(c);
Ok(container)
}
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? {
Some(id_ret) => id_ret,
None => docker.create_container(Some(
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,
};
docker.start_container(&self.name, None::<StartContainerOptions<String>>).await?;
@@ -48,7 +60,7 @@ impl Container {
}
pub fn get_id(&self) -> String {
match &self.id {
match &self.docker_id {
Some(id) => id.clone(),
None => String::from(""),
}
@@ -58,17 +70,21 @@ impl Container {
let list = docker.list_containers(
Some(create_search_container(self.name.clone()))
).await?;
println!("{}", list.len());
Ok (if list.len() > 0 {
list[0].id.clone()
} else {
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> {
let mut filters = HashMap::new();
filters.insert("name".to_string(), vec![name]);
filters.insert("before".to_string(), vec![name]);
ListContainersOptions{
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 {
ipam_config: Some(EndpointIpamConfig{
ipv4_address: Some(ip),
@@ -92,7 +108,7 @@ fn create_config(ip: String, net: String) -> Config<String> {
};
let env = vec!["EULA=TRUE".to_string()];
Config {
image: Some("itzg/minecraft-server:latest".to_string()),
image: Some(image),
env: Some(env),
networking_config: Some(net),
..Default::default()
@@ -105,3 +121,9 @@ fn create_options(name: String) -> CreateContainerOptions<String> {
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 serde::Deserialize;
use env_logger;
use crate::controller::Controller;
#[derive(Debug, Deserialize)]
struct ConainerParams {
name: String,
ip: String,
image: String,
}
#[get("/")]
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello world!")
}
#[get("/create")]
async fn create(controller: web::Data<Controller>) -> impl Responder {
controller.create_container("container1".to_string(), "172.20.0.5".to_string()).await
#[put("/container")]
async fn create_container(controller: web::Data<Controller>,
params: web::Query<ConainerParams>) -> impl Responder {
controller.create_container(params.name.clone(), params.ip.clone(), params.image.clone()).await
}
#[post("/echo")]
@@ -34,13 +43,15 @@ pub async fn start() -> std::io::Result<()> {
Ok(c) => c,
Err(e) => panic!("error: {}",e),
};
controller.load_all_instances().await;
let data = web::Data::new(controller);
env_logger::init_from_env(env_logger::Env::new().default_filter_or("debug"));
HttpServer::new(move || {
App::new()
.wrap(actix_web::middleware::Logger::default())
.app_data(data.clone())
.service(hello)
.service(create)
.service(create_container)
.service(echo)
.route("/hey", web::get().to(manual_hello))
})