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/
/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"
[dependencies]
bollard = "0.14"
bollard = "0.19"
futures = "0.3"
tokio = { version = "1", features = ["full"] }
rusqlite = { version = "0.29.0", features = ["bundled"] }
actix-web = "4"
env_logger = "*"
env_logger = "0.11"
derive_more = "*"
log = "*"
log = "0.4"
serde = "*"
serde_json = "*"
fallible-iterator = "*"

View File

@@ -3,7 +3,7 @@ use derive_more::{Display, Error};
impl actix_web::error::ResponseError for ReturnedError {}
#[derive(Debug, Display, Error)]
#[display(fmt = "my error: {}", name)]
#[display("my error: {}", name)]
pub struct ReturnedError {
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::controller::Controller;
use crate::deploy::starter;
use crate::deploy::container_options::Options;
use crate::deploy::starter;
use actix_web::{delete, get, patch, put, web, App, HttpServer, Responder};
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct ConainerParams {
name: String,
ip: String,
ip: Option<String>,
image: String,
msg: Option<String>,
memory: Option<String>
memory: Option<String>,
}
#[derive(Debug, Deserialize)]
@@ -20,30 +20,58 @@ struct ConainerStopParams {
}
#[put("/container")]
async fn create_container(controller: web::Data<Controller>,
params: web::Query<ConainerParams>) -> impl Responder {
async fn create_container(
controller: web::Data<Controller>,
params: web::Query<ConainerParams>,
) -> impl Responder {
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")]
async fn stop_container(controller: web::Data<Controller>,
params: web::Query<ConainerStopParams>) -> Result<String, ReturnedError> {
async fn stop_container(
controller: web::Data<Controller>,
params: web::Query<ConainerStopParams>,
) -> Result<String, ReturnedError> {
match controller.stop_container(params.name.clone()).await {
Ok(response) => Ok(response),
Err(e) => Err(ReturnedError { name: e.to_string()})
Err(e) => Err(ReturnedError {
name: e.to_string(),
}),
}
}
#[delete("/container")]
async fn delete_container(controller: web::Data<Controller>,
params: web::Query<ConainerStopParams>) -> Result<String, ReturnedError> {
async fn delete_container(
controller: web::Data<Controller>,
params: web::Query<ConainerStopParams>,
) -> Result<String, ReturnedError> {
match controller.delete_container(params.name.clone()).await {
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<()> {
let controller = starter::start_docker().await;
let data = web::Data::new(controller);
@@ -54,9 +82,9 @@ pub async fn start() -> std::io::Result<()> {
.service(create_container)
.service(stop_container)
.service(delete_container)
.service(get_ip)
})
.bind(("127.0.0.1", 8080))?
.run()
.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::instance::Instance;
use crate::deploy;
use crate::deploy::container::Container;
use crate::deploy::container_options::Options;
use std::sync::Mutex;
use bollard::errors::Error;
use bollard::Docker;
use log::error;
use std::sync::Mutex;
pub struct Controller {
driver: Docker,
@@ -16,7 +16,11 @@ pub struct 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?;
let cont = Self {
driver: driver,
@@ -27,60 +31,86 @@ impl Controller {
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());
match is_stored {
Some(c) => match c.docker_id {
Some(id) => id,
None => "Container without docker_id".to_string()
None => "Container without docker_id".to_string(),
},
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) => {
self.storage.try_lock().unwrap().new_instance(c.clone()).unwrap();
self.storage
.try_lock()
.unwrap()
.new_instance(c.clone())
.unwrap();
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) {
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,
Err(e) => {
error!("image not found: {}", e);
return
},
return;
}
};
match self.load_container(
match self
.load_container(
Some(instance.docker_id.clone()),
instance.domain.clone(),
instance.ip.clone(),
image,
Options::new(None, None)).await {
Options::new(None, None),
)
.await
{
Ok(c) => {
self.storage.lock().unwrap().loaded_instance(instance, c);
},
}
Err(e) => error!("{}", e),
}
}
async fn load_container(&self, docker_id: Option<String>,
async fn load_container(
&self,
docker_id: Option<String>,
domain: String,
ip: String,
ip: Option<String>,
image: String,
ops: Options) -> Result<deploy::container::Container,
bollard::errors::Error>
{
deploy::container::Container::new(self.driver.clone(),
ops: Options,
) -> Result<deploy::container::Container, bollard::errors::Error> {
deploy::container::Container::new(
self.driver.clone(),
docker_id,
domain,
ip,
image,
self.network.clone(),
ops).await
ops,
)
.await
}
pub async fn is_started(&self) -> bool {
@@ -92,8 +122,8 @@ impl Controller {
Ok(d) => d,
Err(e) => {
error!("instances can't be loaded: {}", e);
return false
},
return false;
}
};
println!("instances {}", data.len());
for instance in data {
@@ -118,30 +148,34 @@ impl Controller {
pub async fn stop_container(&self, domain: String) -> Result<String, Error> {
match self.storage.lock().unwrap().stop_instance(domain) {
Some(c) => {
self.stop_given_container(c).await
},
Some(c) => self.stop_given_container(c).await,
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> {
match self.storage.lock().unwrap().remove_instance(domain) {
Some(data) => {
match data.1 {
Some(data) => match data.1 {
Some(c) => self.prune_given_container(c).await,
None => Ok(data.0.docker_id),
}
},
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,7 +1,7 @@
use rusqlite::{Connection, Result};
use fallible_iterator::FallibleIterator;
use crate::database::instance::Instance;
use crate::deploy::container_options::Options;
use fallible_iterator::FallibleIterator;
use rusqlite::{types::Null, Connection, Result};
const PATH: &str = "mdeploy.db";
@@ -19,9 +19,7 @@ pub struct InstanceStorage {
impl InstanceStorage {
pub fn new() -> Result<Self> {
let con = Connection::open(PATH)?;
let ret = Self {
con : con,
};
let ret = Self { con: con };
ret.create_table()?;
Ok(ret)
}
@@ -39,36 +37,42 @@ impl InstanceStorage {
options TEXT,
image INTEGER,
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)")?;
stmt.execute([image])?;
Ok(())
}
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 rows = stmt.query([])?;
rows.map(|r| Ok(Instance{
let mut stmt = self
.con
.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)?,
docker_id: r.get::<_, String>(1)?,
ip: r.get::<_, String>(2)?,
ip: r.get::<_, String>(2).ok(),
domain: r.get::<_, String>(3)?,
ops: Options::new_from_json(r.get::<_, String>(4)?),
image: r.get::<_, i64>(5)?,
})).collect()
})
})
.collect()
}
pub 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([])?;
rows.map(|row| Ok((row.get::<_, String>(1)?, row.get::<_, i64>(0)?))).collect()
let rows = stmt.query([])?;
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 rows = stmt.query([image])?;
Ok(match rows.next()? {
@@ -83,8 +87,10 @@ impl InstanceStorage {
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")?;
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 rows = stmt.query([domain])?;
Ok(match rows.next()? {
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 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(),
domain.clone(),
ops.to_json(),
image.to_string()])?;
Some(domain.clone()),
Some(ops.to_json()),
Some(image.to_string()),
])?;
Ok(Instance {
id: image,
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")?;
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)
}
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 {
return Err(rusqlite::Error::QueryReturnedNoRows);
}
depth += 1;
match self.get_image_id(image.clone()) {
match self.get_image_id(image) {
Ok(Some(id)) => Ok(id),
Ok(None) => {
self.insert_image(image.clone())?;
self.insert_image(image)?;
self.create_or_get_image_id(image, depth)
}
Err(_e) => {
self.insert_image(image.clone())?;
self.insert_image(image)?;
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::instance::Instance;
use crate::deploy::container::Container;
use rusqlite::Error;
use std::collections::hash_map::HashMap;
pub struct MemStorage {
containers: Vec<(Instance, Option<Container>)>,
@@ -13,8 +13,10 @@ pub struct MemStorage {
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))
let images = storage
.load_images()?
.into_iter()
.map(|image| (image.0, image.1))
.collect::<HashMap<_, _>>();
Ok(Self {
containers: Vec::new(),
@@ -23,14 +25,14 @@ impl MemStorage {
})
}
fn image_to_id(&mut self, image: String) -> Result<i64, Error> {
match self.images.get(&mut image.clone()) {
fn image_to_id(&mut self, image: &str) -> Result<i64, Error> {
match self.images.get(image) {
Some(id) => Ok(*id),
None => {
let id = self.storage.image_to_id(image.clone())?;
self.images.insert(image, id.clone());
let id = self.storage.image_to_id(image)?;
self.images.insert(image.to_string(), id.clone());
Ok(id)
},
}
}
}
@@ -39,20 +41,23 @@ impl MemStorage {
}
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(
container.get_id(),
container.ip.clone(),
container.name.clone(),
container.ops.clone(),
image_id)?;
image_id,
)?;
self.containers.push((ins, Some(container)));
Ok(())
}
pub fn search_instance(&self, name: String) -> Option<Container> {
for cont in &self.containers {
if cont.0.domain == name { return cont.1.clone(); }
if cont.0.domain == name {
return cont.1.clone();
}
}
None
}
@@ -71,11 +76,11 @@ impl MemStorage {
match self.containers.get(i) {
Some(c) => {
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));
}
}
None => break
None => break,
}
}
None
@@ -88,5 +93,4 @@ impl MemStorage {
pub fn get_instances_db(&mut self) -> Result<Vec<Instance>, Error> {
self.storage.load_instances()
}
}

View File

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

View File

@@ -1,5 +1,5 @@
use std::fs::{canonicalize, create_dir};
use std::vec;
use std::fs::{create_dir, canonicalize};
const BASE_PATH: &str = "mrdeploy_data/";

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::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)]
pub struct Container {
pub name: String, //Must be the domain name
pub start: bool,
pub ip: String,
pub ip: Option<String>,
pub image: String,
pub net: String,
pub docker_id: Option<String>,
@@ -25,14 +24,15 @@ pub struct Container {
}
impl Container {
pub async fn new(docker: bollard::Docker,
pub async fn new(
docker: bollard::Docker,
docker_id: Option<String>,
name: String,
ip: String,
ip: Option<String>,
image: String,
net: String,
ops: Options) -> Result<Container, Error> {
ops: Options,
) -> Result<Container, Error> {
let mut container = Container {
name: name,
ip: ip,
@@ -44,7 +44,7 @@ impl Container {
ops: ops,
};
match container.start(docker).await {
Ok(c) => {
Ok(_c) => {
container.start = true;
Ok(container)
}
@@ -62,21 +62,30 @@ impl Container {
Some(id) => {
self.docker_id = Some(id);
true
},
}
None => {
log::info!("creating container with name: {}", self.name);
self.docker_id = Some(docker.create_container(Some(
create_options(self.name.clone())),
create_config(self.name.clone(),
self.docker_id = Some(
docker
.create_container(
Some(create_options(self.name.clone())),
create_config(
self.name.clone(),
self.ip.clone(),
self.net.clone(),
self.image.clone(),
self.ops.clone()))
.await?.id);
self.ops.clone(),
),
)
.await?
.id,
);
false
},
}
};
docker.start_container(&self.name, None::<StartContainerOptions<String>>).await?;
docker
.start_container(&self.name, None::<StartContainerOptions>)
.await?;
Ok(ret)
}
@@ -88,9 +97,9 @@ impl Container {
}
async fn exist_container(&self, docker: bollard::Docker) -> Result<Option<String>, Error> {
let list = docker.list_containers(
Some(create_search_container(self.name.clone()))
).await?;
let list = docker
.list_containers(Some(create_search_container(self.name.clone())))
.await?;
let mut comparable_name = self.name.clone();
comparable_name.insert_str(0, "/");
for cs in list {
@@ -101,7 +110,7 @@ impl Container {
return Ok(cs.id.clone());
}
}
},
}
None => (),
}
@@ -110,30 +119,64 @@ impl Container {
}
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> {
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())
}
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();
filters.insert("name".to_string(), vec![name]);
ListContainersOptions {
all: true,
filters,
filters: Some(filters),
..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 {
ipam_config: Some(EndpointIpamConfig {
ipv4_address: Some(ip),
ipv4_address: ip,
..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();
hash.insert(net, endpoint);
let net = NetworkingConfig {
endpoints_config: hash
endpoints_config: Some(hash),
};
let mut env = vec!["EULA=TRUE".to_string()];
env.append(&mut ops.generate_config());
Config {
ContainerCreateBody {
image: Some(image),
env: Some(env),
host_config: Some(create_hostconfig(name)),
@@ -161,16 +204,17 @@ fn create_hostconfig(name: String) -> HostConfig {
}
}
fn create_options(name: String) -> CreateContainerOptions<String> {
fn create_options(name: String) -> CreateContainerOptions {
CreateContainerOptions {
name: name,
platform: None,
name: Some(name),
platform: "".to_string(),
}
}
fn create_stop_options() -> StopContainerOptions {
StopContainerOptions {
t: 30,
signal: None,
t: None,
}
}

View File

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

View File

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