nzrd: don't store ci-metadata
This will be handled entirely in omyacid.
This commit is contained in:
parent
3d0ea1f2ef
commit
da51722c54
|
@ -1,3 +1,5 @@
|
|||
use std::net::Ipv4Addr;
|
||||
|
||||
use model::{CreateStatus, Instance, Subnet};
|
||||
|
||||
pub mod args;
|
||||
|
@ -6,6 +8,15 @@ pub mod model;
|
|||
pub mod net;
|
||||
|
||||
pub use hickory_proto;
|
||||
use net::mac::MacAddr;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum InstanceQuery {
|
||||
Name(String),
|
||||
MacAddr(MacAddr),
|
||||
Ipv4Addr(Ipv4Addr),
|
||||
}
|
||||
|
||||
#[tarpc::service]
|
||||
pub trait Nazrin {
|
||||
|
@ -18,6 +29,8 @@ pub trait Nazrin {
|
|||
/// This should involve deleting all related disks and clearing
|
||||
/// the lease information from the subnet data, if any.
|
||||
async fn delete_instance(name: String) -> Result<(), String>;
|
||||
/// Gets a single instance by the given InstanceQuery.
|
||||
async fn find_instance(query: InstanceQuery) -> Result<Option<Instance>, String>;
|
||||
/// Gets a list of existing instances.
|
||||
async fn get_instances(with_status: bool) -> Result<Vec<Instance>, String>;
|
||||
/// Cleans up unusable entries in the database.
|
||||
|
@ -34,4 +47,20 @@ pub trait Nazrin {
|
|||
async fn get_subnets() -> Result<Vec<Subnet>, String>;
|
||||
/// Deletes an existing subnet.
|
||||
async fn delete_subnet(interface: String) -> Result<(), String>;
|
||||
// Gets the cloud-init user-data for the given instance.
|
||||
async fn get_instance_userdata(id: i32) -> Result<Vec<u8>, String>;
|
||||
}
|
||||
|
||||
/// Create a new NazrinClient.
|
||||
pub fn new_client(sock: tokio::net::UnixStream) -> NazrinClient {
|
||||
use tarpc::tokio_serde::formats::Bincode;
|
||||
use tarpc::tokio_util::codec::LengthDelimitedCodec;
|
||||
|
||||
let framed_io = LengthDelimitedCodec::builder()
|
||||
.length_field_type::<u32>()
|
||||
.new_framed(sock);
|
||||
let transport = tarpc::serde_transport::new(framed_io, Bincode::default());
|
||||
NazrinClient::new(Default::default(), transport).spawn()
|
||||
}
|
||||
|
||||
pub use tarpc::context::current as default_ctx;
|
||||
|
|
1
nzrd/migrations/2024081101_no_cimeta/down.sql
Normal file
1
nzrd/migrations/2024081101_no_cimeta/down.sql
Normal file
|
@ -0,0 +1 @@
|
|||
ALTER TABLE instances ADD COLUMN ci_metadata TEXT NOT NULL;
|
1
nzrd/migrations/2024081101_no_cimeta/up.sql
Normal file
1
nzrd/migrations/2024081101_no_cimeta/up.sql
Normal file
|
@ -0,0 +1 @@
|
|||
ALTER TABLE instances DROP COLUMN ci_metadata;
|
|
@ -1,149 +0,0 @@
|
|||
use std::net::Ipv4Addr;
|
||||
|
||||
use hickory_server::proto::rr::Name;
|
||||
use serde::Serialize;
|
||||
use serde_with::skip_serializing_none;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use nzr_api::net::{cidr::CidrV4, mac::MacAddr};
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct Metadata<'a> {
|
||||
instance_id: &'a str,
|
||||
local_hostname: &'a str,
|
||||
public_keys: Option<Vec<&'a String>>,
|
||||
}
|
||||
|
||||
impl<'a> Metadata<'a> {
|
||||
pub fn new(instance_id: &'a str) -> Self {
|
||||
Self {
|
||||
instance_id,
|
||||
local_hostname: instance_id,
|
||||
public_keys: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ssh_pubkeys(mut self, pubkeys: &'a [String]) -> Self {
|
||||
self.public_keys = Some(pubkeys.iter().filter(|i| !i.is_empty()).collect());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct NetworkMeta<'a> {
|
||||
version: u32,
|
||||
ethernets: HashMap<String, EtherNic<'a>>,
|
||||
#[serde(skip)]
|
||||
ethnum: u8,
|
||||
}
|
||||
|
||||
impl<'a> NetworkMeta<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
version: 2,
|
||||
ethernets: HashMap::new(),
|
||||
ethnum: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Define a NIC with a static address.
|
||||
pub fn static_nic(
|
||||
mut self,
|
||||
match_data: EtherMatch<'a>,
|
||||
cidr: &'a CidrV4,
|
||||
gateway: &'a Ipv4Addr,
|
||||
dns: DNSMeta<'a>,
|
||||
) -> Self {
|
||||
self.ethernets.insert(
|
||||
format!("eth{}", self.ethnum),
|
||||
EtherNic {
|
||||
r#match: match_data,
|
||||
addresses: Some(vec![cidr]),
|
||||
gateway4: Some(gateway),
|
||||
dhcp4: false,
|
||||
nameservers: Some(dns),
|
||||
},
|
||||
);
|
||||
self.ethnum += 1;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn dhcp_nic(mut self, match_data: EtherMatch<'a>) -> Self {
|
||||
self.ethernets.insert(
|
||||
format!("eth{}", self.ethnum),
|
||||
EtherNic {
|
||||
r#match: match_data,
|
||||
addresses: None,
|
||||
gateway4: None,
|
||||
dhcp4: true,
|
||||
nameservers: None,
|
||||
},
|
||||
);
|
||||
self.ethnum += 1;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Ethernets<'a> {
|
||||
nics: Vec<EtherNic<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct EtherNic<'a> {
|
||||
r#match: EtherMatch<'a>,
|
||||
addresses: Option<Vec<&'a CidrV4>>,
|
||||
gateway4: Option<&'a Ipv4Addr>,
|
||||
dhcp4: bool,
|
||||
nameservers: Option<DNSMeta<'a>>,
|
||||
}
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Default, Debug, Serialize)]
|
||||
pub struct EtherMatch<'a> {
|
||||
name: Option<&'a str>,
|
||||
macaddress: Option<&'a MacAddr>,
|
||||
driver: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> EtherMatch<'a> {
|
||||
#[allow(dead_code)]
|
||||
pub fn name(name: &'a str) -> Self {
|
||||
Self {
|
||||
name: Some(name),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mac_addr(addr: &'a MacAddr) -> Self {
|
||||
Self {
|
||||
macaddress: Some(addr),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn driver(driver: &'a str) -> Self {
|
||||
Self {
|
||||
driver: Some(driver),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct DNSMeta<'a> {
|
||||
search: Vec<Name>,
|
||||
addresses: &'a Vec<Ipv4Addr>,
|
||||
}
|
||||
|
||||
impl<'a> DNSMeta<'a> {
|
||||
pub fn with_addrs(search: Option<Vec<Name>>, addrs: &'a Vec<Ipv4Addr>) -> Self {
|
||||
Self {
|
||||
addresses: addrs,
|
||||
search: search.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ use nzr_virt::{datasize, dom, vol};
|
|||
use tokio::sync::RwLock;
|
||||
|
||||
use super::*;
|
||||
use crate::cloud::Metadata;
|
||||
use crate::ctrl::vm::Progress;
|
||||
use crate::ctx::Context;
|
||||
use crate::model::{Instance, Subnet};
|
||||
|
@ -86,14 +85,7 @@ pub async fn new_instance(
|
|||
};
|
||||
|
||||
// generate cloud-init data
|
||||
let ci_meta = {
|
||||
let m = Metadata::new(&args.name).ssh_pubkeys(&args.ssh_keys);
|
||||
serde_yaml::to_string(&m)
|
||||
.map_err(|err| cmd_error!("Couldn't generate cloud-init metadata: {err}"))
|
||||
}?;
|
||||
|
||||
let db_inst =
|
||||
Instance::insert(&ctx, &args.name, &subnet, lease.clone(), ci_meta, None).await?;
|
||||
let db_inst = Instance::insert(&ctx, &args.name, &subnet, lease.clone(), None).await?;
|
||||
|
||||
progress!(prog_task, 30.0, "Creating instance images...");
|
||||
// create primary volume from base image
|
||||
|
|
|
@ -12,6 +12,10 @@ use crate::dns::ZoneData;
|
|||
use nzr_api::config::Config;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
|
||||
|
||||
#[cfg(not(test))]
|
||||
const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
|
||||
|
||||
pub struct PoolRefs {
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
mod cloud;
|
||||
mod cmd;
|
||||
mod ctrl;
|
||||
mod ctx;
|
||||
mod dns;
|
||||
mod model;
|
||||
mod rpc;
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
use hickory_server::ServerFuture;
|
||||
use log::LevelFilter;
|
||||
use log::*;
|
||||
use model::{Instance, Subnet};
|
||||
use nzr_api::config;
|
||||
use std::str::FromStr;
|
||||
use std::{net::IpAddr, str::FromStr};
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
#[tokio::main(flavor = "multi_thread")]
|
||||
|
@ -62,7 +59,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
|
||||
// DNS init
|
||||
let mut dns_listener = ServerFuture::new(ctx.zones.catalog());
|
||||
let dns_socket = UdpSocket::bind(ctx.config.dns.listen_addr.as_str()).await?;
|
||||
let dns_socket = {
|
||||
let dns_ip: IpAddr = ctx.config.dns.listen_addr.parse()?;
|
||||
UdpSocket::bind((dns_ip, ctx.config.dns.port)).await?
|
||||
};
|
||||
dns_listener.register_socket(dns_socket);
|
||||
|
||||
tokio::select! {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use std::{net::Ipv4Addr, str::FromStr};
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
pub mod tx;
|
||||
|
||||
use diesel::{associations::HasTable, prelude::*};
|
||||
|
@ -33,7 +35,6 @@ diesel::table! {
|
|||
mac_addr -> Text,
|
||||
subnet_id -> Integer,
|
||||
host_num -> Integer,
|
||||
ci_metadata -> Text,
|
||||
ci_userdata -> Nullable<Binary>,
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +72,6 @@ pub struct Instance {
|
|||
pub mac_addr: MacAddr,
|
||||
pub subnet_id: i32,
|
||||
pub host_num: i32,
|
||||
pub ci_metadata: String,
|
||||
pub ci_userdata: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
|
@ -91,6 +91,17 @@ impl Instance {
|
|||
Ok(res)
|
||||
}
|
||||
|
||||
pub async fn get(ctx: &Context, id: i32) -> Result<Option<Self>, ModelError> {
|
||||
ctx.spawn_db(move |mut db| {
|
||||
self::instances::table
|
||||
.find(id)
|
||||
.load::<Instance>(&mut db)
|
||||
.map(|m| m.into_iter().next())
|
||||
})
|
||||
.await?
|
||||
.map_err(ModelError::Db)
|
||||
}
|
||||
|
||||
pub async fn all_in_subnet(ctx: &Context, net: &Subnet) -> Result<Vec<Self>, ModelError> {
|
||||
let subnet = net.clone();
|
||||
|
||||
|
@ -122,6 +133,19 @@ impl Instance {
|
|||
Ok(res.into_iter().next())
|
||||
}
|
||||
|
||||
/// Gets an Instance model with the given MAC address.
|
||||
pub async fn get_by_mac(ctx: &Context, addr: MacAddr) -> Result<Option<Self>, ModelError> {
|
||||
ctx.spawn_db(move |mut db| {
|
||||
use self::instances::dsl::{instances, mac_addr};
|
||||
instances
|
||||
.filter(mac_addr.eq(addr))
|
||||
.select(Instance::as_select())
|
||||
.load::<Instance>(&mut db)
|
||||
})
|
||||
.await?
|
||||
.map_or_else(|e| Err(ModelError::Db(e)), |m| Ok(m.into_iter().next()))
|
||||
}
|
||||
|
||||
/// Gets an Instance model by the IPv4 address that has been assigned to it.
|
||||
pub async fn get_by_ip4(ctx: &Context, ip_addr: Ipv4Addr) -> Result<Option<Self>, ModelError> {
|
||||
use self::instances::dsl::host_num;
|
||||
|
@ -157,7 +181,6 @@ impl Instance {
|
|||
name: impl AsRef<str>,
|
||||
subnet: &Subnet,
|
||||
lease: nzr_api::model::Lease,
|
||||
ci_meta: impl Into<String>,
|
||||
ci_user: Option<Vec<u8>>,
|
||||
) -> Result<Self, ModelError> {
|
||||
// Get highest host addr + 1 for our addr
|
||||
|
@ -169,7 +192,6 @@ impl Instance {
|
|||
|
||||
let wanted_name = name.as_ref().to_owned();
|
||||
let netid = subnet.id;
|
||||
let ci_meta = ci_meta.into();
|
||||
|
||||
if addr_num > subnet.end_host {
|
||||
Err(cidr::Error::HostBitsTooLarge)?;
|
||||
|
@ -184,7 +206,6 @@ impl Instance {
|
|||
mac_addr.eq(lease.mac_addr),
|
||||
subnet_id.eq(netid),
|
||||
host_num.eq(addr_num),
|
||||
ci_metadata.eq(ci_meta),
|
||||
ci_userdata.eq(ci_user),
|
||||
);
|
||||
|
||||
|
|
14
nzrd/src/model/test.rs
Normal file
14
nzrd/src/model/test.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
use diesel::Connection;
|
||||
use diesel_migrations::MigrationHarness;
|
||||
|
||||
#[test]
|
||||
fn migrations() {
|
||||
let mut sql = diesel::SqliteConnection::establish(":memory:").unwrap();
|
||||
let pending = sql.pending_migrations(crate::ctx::MIGRATIONS).unwrap();
|
||||
assert!(!pending.is_empty(), "No migrations found");
|
||||
for migration in pending {
|
||||
sql.run_migration(&migration).unwrap();
|
||||
}
|
||||
|
||||
sql.revert_all_migrations(crate::ctx::MIGRATIONS).unwrap();
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use futures::{future, StreamExt};
|
||||
use nzr_api::{args, model, Nazrin};
|
||||
use nzr_api::{args, model, InstanceQuery, Nazrin};
|
||||
use std::sync::Arc;
|
||||
use tarpc::server::{BaseChannel, Channel};
|
||||
use tarpc::tokio_serde::formats::Bincode;
|
||||
|
@ -114,6 +114,27 @@ impl Nazrin for NzrServer {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn find_instance(
|
||||
self,
|
||||
_: tarpc::context::Context,
|
||||
query: nzr_api::InstanceQuery,
|
||||
) -> Result<Option<model::Instance>, String> {
|
||||
let res = match query {
|
||||
InstanceQuery::Name(name) => Instance::get_by_name(&self.ctx, name).await,
|
||||
InstanceQuery::MacAddr(addr) => Instance::get_by_mac(&self.ctx, addr).await,
|
||||
InstanceQuery::Ipv4Addr(addr) => Instance::get_by_ip4(&self.ctx, addr).await,
|
||||
}
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
if let Some(inst) = res {
|
||||
inst.api_model(&self.ctx)
|
||||
.await
|
||||
.map_or_else(|e| Err(e.to_string()), |m| Ok(Some(m)))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_instances(
|
||||
self,
|
||||
_: tarpc::context::Context,
|
||||
|
@ -216,6 +237,21 @@ impl Nazrin for NzrServer {
|
|||
.map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_instance_userdata(
|
||||
self,
|
||||
_: tarpc::context::Context,
|
||||
id: i32,
|
||||
) -> Result<Vec<u8>, String> {
|
||||
let Some(db_model) = Instance::get(&self.ctx, id)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?
|
||||
else {
|
||||
return Err("Instance doesn't exist".to_owned());
|
||||
};
|
||||
|
||||
Ok(db_model.ci_userdata.unwrap_or_default())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
use std::{net::Ipv4Addr, str::FromStr};
|
||||
|
||||
use crate::cloud::*;
|
||||
use nzr_api::net::{cidr::CidrV4, mac::MacAddr};
|
||||
|
||||
#[test]
|
||||
fn cloud_metadata() {
|
||||
let expected = r#"
|
||||
instance-id: my-instance
|
||||
local-hostname: my-instance
|
||||
public-keys:
|
||||
- ssh-key 123456 admin@laptop
|
||||
"#
|
||||
.trim_start();
|
||||
let pubkeys = vec!["ssh-key 123456 admin@laptop".to_owned(), "".to_owned()];
|
||||
let meta = Metadata::new("my-instance").ssh_pubkeys(&pubkeys);
|
||||
|
||||
let meta_xml = serde_yaml::to_string(&meta).unwrap();
|
||||
assert_eq!(meta_xml, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cloud_netdata() {
|
||||
let expected = r#"
|
||||
version: 2
|
||||
ethernets:
|
||||
eth0:
|
||||
match:
|
||||
macaddress: 02:15:42:0b:ee:01
|
||||
addresses:
|
||||
- 192.0.2.69/24
|
||||
gateway4: 192.0.2.1
|
||||
dhcp4: false
|
||||
nameservers:
|
||||
search: []
|
||||
addresses:
|
||||
- 192.0.2.1
|
||||
"#
|
||||
.trim_start();
|
||||
let mac_addr = MacAddr::new(0x02, 0x15, 0x42, 0x0b, 0xee, 0x01);
|
||||
let cidr = CidrV4::from_str("192.0.2.69/24").unwrap();
|
||||
let gateway = Ipv4Addr::from_str("192.0.2.1").unwrap();
|
||||
|
||||
let dns = vec![gateway];
|
||||
let netconfig = NetworkMeta::new().static_nic(
|
||||
EtherMatch::mac_addr(&mac_addr),
|
||||
&cidr,
|
||||
&gateway,
|
||||
DNSMeta::with_addrs(None, &dns),
|
||||
);
|
||||
|
||||
let net_xml = serde_yaml::to_string(&netconfig).unwrap();
|
||||
assert_eq!(net_xml, expected);
|
||||
}
|
Loading…
Reference in a new issue