feat: reset

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
2024-11-23 22:38:25 +01:00
parent 0f5249f620
commit cfe21ad23c
35 changed files with 0 additions and 5254 deletions

View File

@@ -1,39 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## v0.1.0 (2023-08-26)
### New Features
- <csr-id-8f8c5fd41aaa82a495dd0933060f0a3a095bbaf1/> with basic package
- <csr-id-821e14fb1256957a107220c6c775565f5abc58c4/> with publish
- <csr-id-569f5272e667deeef9f269db5eaf3dec57e2df1c/> with monitor
- <csr-id-97978df287ee42f523f509ac686a13fa0400a026/> add initial churn
- <csr-id-f61d0bbf120607e59145a80b65985ab93c938522/> add simple health check
### Commit Statistics
<csr-read-only-do-not-edit/>
- 5 commits contributed to the release over the course of 2 calendar days.
- 5 commits were understood as [conventional](https://www.conventionalcommits.org).
- 0 issues like '(#ID)' were seen in commit messages
### Commit Details
<csr-read-only-do-not-edit/>
<details><summary>view details</summary>
* **Uncategorized**
- With basic package (8f8c5fd)
- With publish (821e14f)
- With monitor (569f527)
- Add initial churn (97978df)
- Add simple health check (f61d0bb)
</details>

View File

@@ -1,24 +0,0 @@
[package]
name = "churn-agent"
description.workspace = true
authors.workspace = true
license-file.workspace = true
version= "0.1.0"
edition.workspace = true
publish.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
churn-domain.workspace = true
anyhow.workspace = true
tokio.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
clap.workspace = true
dotenv.workspace = true
axum.workspace = true
serde.workspace = true
serde_json.workspace = true
reqwest.workspace = true

View File

@@ -1,74 +0,0 @@
use std::sync::Arc;
use axum::async_trait;
use churn_domain::ServerEnrollReq;
use tokio::sync::Mutex;
#[derive(Clone)]
pub struct AgentService(Arc<dyn AgentServiceTrait + Send + Sync + 'static>);
impl std::ops::Deref for AgentService {
type Target = Arc<dyn AgentServiceTrait + Send + Sync + 'static>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Default for AgentService {
fn default() -> Self {
Self(Arc::new(DefaultAgentService::default()))
}
}
#[derive(Default)]
struct DefaultAgentService {
server: Arc<Mutex<String>>,
leases: Arc<Mutex<Vec<String>>>,
}
#[async_trait]
pub trait AgentServiceTrait {
async fn enroll(&self, agent_name: &str, server: &str, lease: &str) -> anyhow::Result<()>;
}
#[async_trait]
impl AgentServiceTrait for DefaultAgentService {
async fn enroll(&self, agent_name: &str, server: &str, lease: &str) -> anyhow::Result<()> {
let mut cur_server = self.server.lock().await;
let mut leases = self.leases.lock().await;
let client = reqwest::Client::new();
let req = client
.post(format!("{server}/agent/enroll"))
.json(&ServerEnrollReq {
lease: lease.into(),
agent_name: agent_name.into(),
})
.build()?;
let resp = client.execute(req).await?;
if !resp.status().is_success() {
if let Ok(text) = resp.text().await {
anyhow::bail!(
"could not enroll agent: {} at server: {}, error: {}",
agent_name,
server,
text
)
}
anyhow::bail!(
"could not enroll agent: {} at server: {}",
agent_name,
server
)
}
*cur_server = server.to_string();
leases.push(lease.to_string());
Ok(())
}
}

View File

@@ -1,130 +0,0 @@
mod agent;
use std::net::SocketAddr;
use agent::AgentService;
use anyhow::Error;
use axum::{
extract::State,
http::StatusCode,
response::{IntoResponse, Response},
routing::{get, post},
Json, Router,
};
use churn_domain::AgentEnrollReq;
use clap::{Parser, Subcommand};
use serde_json::json;
use tokio::net::TcpListener;
#[derive(Parser)]
#[command(author, version, about, long_about = None, subcommand_required = true)]
struct Command {
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand)]
enum Commands {
Daemon {
#[arg(env = "CHURN_ADDR", long)]
host: SocketAddr,
},
Connect {
/// agent name is the hostname which other agents or servers can resolve and connect via. It should be unique
#[arg(env = "CHURN_AGENT_NAME", long)]
agent_name: String,
#[arg(env = "CHURN_ADDR", long)]
host: SocketAddr,
#[arg(env = "CHURN_TOKEN", long)]
token: String,
},
}
#[derive(Clone, Default)]
struct AppState {
agent: AgentService,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
dotenv::dotenv().ok();
tracing_subscriber::fmt::init();
let cli = Command::parse();
handle_command(cli).await?;
Ok(())
}
async fn handle_command(cmd: Command) -> anyhow::Result<()> {
match cmd.command {
Some(Commands::Daemon { host }) => {
tracing::info!("Starting churn server");
let app = Router::new()
.route("/enroll", post(enroll))
.route("/ping", get(ping))
.with_state(AppState::default());
tracing::info!("churn server listening on {}", host);
let listener = TcpListener::bind(&host).await?;
axum::serve(listener, app.into_make_service())
.await
.unwrap();
Ok(())
}
Some(Commands::Connect {
host: _,
token: _,
agent_name: _,
}) => todo!(),
None => todo!(),
}
}
enum AppError {
Internal(Error),
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, error_message) = match self {
AppError::Internal(e) => {
tracing::error!("failed with error: {}", e);
(
StatusCode::INTERNAL_SERVER_ERROR,
"failed with internal error",
)
}
};
let body = Json(json!({
"error": error_message,
}));
(status, body).into_response()
}
}
async fn ping() -> impl IntoResponse {
"pong!"
}
async fn enroll(
State(state): State<AppState>,
Json(req): Json<AgentEnrollReq>,
) -> Result<(), AppError> {
state
.agent
.enroll(&req.agent_name, &req.server, &req.lease)
.await
.map_err(AppError::Internal)?;
Ok(())
}

View File

@@ -1,23 +0,0 @@
[package]
name = "churn-capnp"
repository.workspace = true
description.workspace = true
readme.workspace = true
license-file.workspace = true
authors.workspace = true
version.workspace = true
edition.workspace = true
publish.workspace = true
[dependencies]
churn-domain.workspace = true
uuid.workspace = true
anyhow.workspace = true
chrono.workspace = true
capnp = "0.19.5"
[build-dependencies]
capnpc = "0.19.0"

View File

@@ -1,10 +0,0 @@
extern crate capnpc;
fn main() {
capnpc::CompilerCommand::new()
.output_path("src/")
.src_prefix("schemas/")
.file("schemas/models.capnp")
.run()
.unwrap();
}

View File

@@ -1,17 +0,0 @@
@0xf23adf24ffd8aca4;
struct LogEvent {
id @0 :Text;
author @1 :Text;
content @2 :Text;
datetime @3 :Int64;
}
struct Agent {
name @0 :Text;
}
struct Lease {
id @0 :Text;
lease @1 :Text;
}

View File

@@ -1,105 +0,0 @@
use capnp::message::{Builder, HeapAllocator};
use capnp::message::{ReaderOptions, TypedReader};
use capnp::serialize::{self, BufferSegments};
use capnp::traits::Owned;
use churn_domain::{Agent, Lease, LogEvent};
mod models_capnp;
pub trait CapnpPackExt {
type Return;
fn serialize_capnp(&self) -> Vec<u8>;
fn deserialize_capnp(content: &[u8]) -> anyhow::Result<Self::Return>;
fn capnp_to_string(builder: &Builder<HeapAllocator>) -> Vec<u8> {
serialize::write_message_to_words(builder)
}
fn string_to_capnp<S>(mut content: &[u8]) -> TypedReader<BufferSegments<&[u8]>, S>
where
S: Owned,
{
let log_event =
serialize::read_message_from_flat_slice(&mut content, ReaderOptions::new()).unwrap();
log_event.into_typed::<S>()
}
}
impl CapnpPackExt for LogEvent {
type Return = Self;
fn serialize_capnp(&self) -> Vec<u8> {
let mut builder = Builder::new_default();
let mut log_event = builder.init_root::<models_capnp::log_event::Builder>();
log_event.set_id(&self.id.to_string());
log_event.set_author(&self.author);
log_event.set_content(&self.content);
log_event.set_datetime(self.timestamp.timestamp());
Self::capnp_to_string(&builder)
}
fn deserialize_capnp(content: &[u8]) -> anyhow::Result<Self> {
let log_event = Self::string_to_capnp::<models_capnp::log_event::Owned>(content);
let log_event = log_event.get()?;
Ok(Self {
id: uuid::Uuid::parse_str(log_event.get_id()?.to_str()?)?,
author: log_event.get_author()?.to_string()?,
content: log_event.get_content()?.to_string()?,
timestamp: chrono::DateTime::<chrono::Utc>::from_utc(
chrono::NaiveDateTime::from_timestamp_opt(log_event.get_datetime(), 0).unwrap(),
chrono::Utc,
),
})
}
}
impl CapnpPackExt for Agent {
type Return = Self;
fn serialize_capnp(&self) -> Vec<u8> {
let mut builder = Builder::new_default();
let mut item = builder.init_root::<models_capnp::agent::Builder>();
item.set_name(&self.name);
Self::capnp_to_string(&builder)
}
fn deserialize_capnp(content: &[u8]) -> anyhow::Result<Self::Return> {
let item = Self::string_to_capnp::<models_capnp::agent::Owned>(content);
let item = item.get()?;
Ok(Self {
name: item.get_name()?.to_string()?,
})
}
}
impl CapnpPackExt for Lease {
type Return = Self;
fn serialize_capnp(&self) -> Vec<u8> {
let mut builder = Builder::new_default();
let mut item = builder.init_root::<models_capnp::lease::Builder>();
item.set_id(&self.id.to_string());
item.set_lease(&self.lease.to_string());
Self::capnp_to_string(&builder)
}
fn deserialize_capnp(content: &[u8]) -> anyhow::Result<Self::Return> {
let item = Self::string_to_capnp::<models_capnp::lease::Owned>(content);
let item = item.get()?;
Ok(Self {
id: uuid::Uuid::parse_str(item.get_id()?.to_str()?)?,
lease: uuid::Uuid::parse_str(item.get_lease()?.to_str()?)?,
})
}
}

View File

@@ -1,771 +0,0 @@
// @generated by the capnpc-rust plugin to the Cap'n Proto schema compiler.
// DO NOT EDIT.
// source: models.capnp
pub mod log_event {
#[derive(Copy, Clone)]
pub struct Owned(());
impl ::capnp::introspect::Introspect for Owned { fn introspect() -> ::capnp::introspect::Type { ::capnp::introspect::TypeVariant::Struct(::capnp::introspect::RawBrandedStructSchema { generic: &_private::RAW_SCHEMA, field_types: _private::get_field_types, annotation_types: _private::get_annotation_types }).into() } }
impl ::capnp::traits::Owned for Owned { type Reader<'a> = Reader<'a>; type Builder<'a> = Builder<'a>; }
impl ::capnp::traits::OwnedStruct for Owned { type Reader<'a> = Reader<'a>; type Builder<'a> = Builder<'a>; }
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
impl <'a,> ::core::marker::Copy for Reader<'a,> {}
impl <'a,> ::core::clone::Clone for Reader<'a,> {
fn clone(&self) -> Self { *self }
}
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
const TYPE_ID: u64 = _private::TYPE_ID;
}
impl <'a,> ::core::convert::From<::capnp::private::layout::StructReader<'a>> for Reader<'a,> {
fn from(reader: ::capnp::private::layout::StructReader<'a>) -> Self {
Self { reader, }
}
}
impl <'a,> ::core::convert::From<Reader<'a,>> for ::capnp::dynamic_value::Reader<'a> {
fn from(reader: Reader<'a,>) -> Self {
Self::Struct(::capnp::dynamic_struct::Reader::new(reader.reader, ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema { generic: &_private::RAW_SCHEMA, field_types: _private::get_field_types::<>, annotation_types: _private::get_annotation_types::<>})))
}
}
impl <'a,> ::core::fmt::Debug for Reader<'a,> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::result::Result<(), ::core::fmt::Error> {
core::fmt::Debug::fmt(&::core::convert::Into::<::capnp::dynamic_value::Reader<'_>>::into(*self), f)
}
}
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [::capnp::Word]>) -> ::capnp::Result<Self> {
::core::result::Result::Ok(reader.get_struct(default)?.into())
}
}
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
self.reader
}
}
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
}
}
impl <'a,> Reader<'a,> {
pub fn reborrow(&self) -> Reader<'_,> {
Self { .. *self }
}
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
self.reader.total_size()
}
#[inline]
pub fn get_id(self) -> ::capnp::Result<::capnp::text::Reader<'a>> {
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(0), ::core::option::Option::None)
}
#[inline]
pub fn has_id(&self) -> bool {
!self.reader.get_pointer_field(0).is_null()
}
#[inline]
pub fn get_author(self) -> ::capnp::Result<::capnp::text::Reader<'a>> {
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(1), ::core::option::Option::None)
}
#[inline]
pub fn has_author(&self) -> bool {
!self.reader.get_pointer_field(1).is_null()
}
#[inline]
pub fn get_content(self) -> ::capnp::Result<::capnp::text::Reader<'a>> {
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(2), ::core::option::Option::None)
}
#[inline]
pub fn has_content(&self) -> bool {
!self.reader.get_pointer_field(2).is_null()
}
#[inline]
pub fn get_datetime(self) -> i64 {
self.reader.get_data_field::<i64>(0)
}
}
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
const STRUCT_SIZE: ::capnp::private::layout::StructSize = ::capnp::private::layout::StructSize { data: 1, pointers: 3 };
}
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
const TYPE_ID: u64 = _private::TYPE_ID;
}
impl <'a,> ::core::convert::From<::capnp::private::layout::StructBuilder<'a>> for Builder<'a,> {
fn from(builder: ::capnp::private::layout::StructBuilder<'a>) -> Self {
Self { builder, }
}
}
impl <'a,> ::core::convert::From<Builder<'a,>> for ::capnp::dynamic_value::Builder<'a> {
fn from(builder: Builder<'a,>) -> Self {
Self::Struct(::capnp::dynamic_struct::Builder::new(builder.builder, ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema { generic: &_private::RAW_SCHEMA, field_types: _private::get_field_types::<>, annotation_types: _private::get_annotation_types::<>})))
}
}
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
}
}
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Self {
builder.init_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE).into()
}
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [::capnp::Word]>) -> ::capnp::Result<Self> {
::core::result::Result::Ok(builder.get_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE, default)?.into())
}
}
impl <'a,> ::capnp::traits::SetterInput<Owned<>> for Reader<'a,> {
fn set_pointer_builder(mut pointer: ::capnp::private::layout::PointerBuilder<'_>, value: Self, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
}
impl <'a,> Builder<'a,> {
pub fn into_reader(self) -> Reader<'a,> {
self.builder.into_reader().into()
}
pub fn reborrow(&mut self) -> Builder<'_,> {
Builder { builder: self.builder.reborrow() }
}
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
self.builder.as_reader().into()
}
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
self.builder.as_reader().total_size()
}
#[inline]
pub fn get_id(self) -> ::capnp::Result<::capnp::text::Builder<'a>> {
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(0), ::core::option::Option::None)
}
#[inline]
pub fn set_id(&mut self, value: impl ::capnp::traits::SetterInput<::capnp::text::Owned>) {
::capnp::traits::SetterInput::set_pointer_builder(self.builder.reborrow().get_pointer_field(0), value, false).unwrap()
}
#[inline]
pub fn init_id(self, size: u32) -> ::capnp::text::Builder<'a> {
self.builder.get_pointer_field(0).init_text(size)
}
#[inline]
pub fn has_id(&self) -> bool {
!self.builder.is_pointer_field_null(0)
}
#[inline]
pub fn get_author(self) -> ::capnp::Result<::capnp::text::Builder<'a>> {
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(1), ::core::option::Option::None)
}
#[inline]
pub fn set_author(&mut self, value: impl ::capnp::traits::SetterInput<::capnp::text::Owned>) {
::capnp::traits::SetterInput::set_pointer_builder(self.builder.reborrow().get_pointer_field(1), value, false).unwrap()
}
#[inline]
pub fn init_author(self, size: u32) -> ::capnp::text::Builder<'a> {
self.builder.get_pointer_field(1).init_text(size)
}
#[inline]
pub fn has_author(&self) -> bool {
!self.builder.is_pointer_field_null(1)
}
#[inline]
pub fn get_content(self) -> ::capnp::Result<::capnp::text::Builder<'a>> {
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(2), ::core::option::Option::None)
}
#[inline]
pub fn set_content(&mut self, value: impl ::capnp::traits::SetterInput<::capnp::text::Owned>) {
::capnp::traits::SetterInput::set_pointer_builder(self.builder.reborrow().get_pointer_field(2), value, false).unwrap()
}
#[inline]
pub fn init_content(self, size: u32) -> ::capnp::text::Builder<'a> {
self.builder.get_pointer_field(2).init_text(size)
}
#[inline]
pub fn has_content(&self) -> bool {
!self.builder.is_pointer_field_null(2)
}
#[inline]
pub fn get_datetime(self) -> i64 {
self.builder.get_data_field::<i64>(0)
}
#[inline]
pub fn set_datetime(&mut self, value: i64) {
self.builder.set_data_field::<i64>(0, value);
}
}
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Self {
Self { _typeless: typeless, }
}
}
impl Pipeline {
}
mod _private {
pub static ENCODED_NODE: [::capnp::Word; 78] = [
::capnp::word(0, 0, 0, 0, 5, 0, 6, 0),
::capnp::word(50, 25, 14, 89, 91, 12, 143, 231),
::capnp::word(13, 0, 0, 0, 1, 0, 1, 0),
::capnp::word(164, 172, 216, 255, 36, 223, 58, 242),
::capnp::word(3, 0, 7, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(21, 0, 0, 0, 178, 0, 0, 0),
::capnp::word(29, 0, 0, 0, 7, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(25, 0, 0, 0, 231, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(109, 111, 100, 101, 108, 115, 46, 99),
::capnp::word(97, 112, 110, 112, 58, 76, 111, 103),
::capnp::word(69, 118, 101, 110, 116, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 1, 0, 1, 0),
::capnp::word(16, 0, 0, 0, 3, 0, 4, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 1, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(97, 0, 0, 0, 26, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(92, 0, 0, 0, 3, 0, 1, 0),
::capnp::word(104, 0, 0, 0, 2, 0, 1, 0),
::capnp::word(1, 0, 0, 0, 1, 0, 0, 0),
::capnp::word(0, 0, 1, 0, 1, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(101, 0, 0, 0, 58, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(96, 0, 0, 0, 3, 0, 1, 0),
::capnp::word(108, 0, 0, 0, 2, 0, 1, 0),
::capnp::word(2, 0, 0, 0, 2, 0, 0, 0),
::capnp::word(0, 0, 1, 0, 2, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(105, 0, 0, 0, 66, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(100, 0, 0, 0, 3, 0, 1, 0),
::capnp::word(112, 0, 0, 0, 2, 0, 1, 0),
::capnp::word(3, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 1, 0, 3, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(109, 0, 0, 0, 74, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(108, 0, 0, 0, 3, 0, 1, 0),
::capnp::word(120, 0, 0, 0, 2, 0, 1, 0),
::capnp::word(105, 100, 0, 0, 0, 0, 0, 0),
::capnp::word(12, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(12, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(97, 117, 116, 104, 111, 114, 0, 0),
::capnp::word(12, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(12, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(99, 111, 110, 116, 101, 110, 116, 0),
::capnp::word(12, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(12, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(100, 97, 116, 101, 116, 105, 109, 101),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(5, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(5, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
];
pub fn get_field_types(index: u16) -> ::capnp::introspect::Type {
match index {
0 => <::capnp::text::Owned as ::capnp::introspect::Introspect>::introspect(),
1 => <::capnp::text::Owned as ::capnp::introspect::Introspect>::introspect(),
2 => <::capnp::text::Owned as ::capnp::introspect::Introspect>::introspect(),
3 => <i64 as ::capnp::introspect::Introspect>::introspect(),
_ => panic!("invalid field index {}", index),
}
}
pub fn get_annotation_types(child_index: Option<u16>, index: u32) -> ::capnp::introspect::Type {
panic!("invalid annotation indices ({:?}, {}) ", child_index, index)
}
pub static RAW_SCHEMA: ::capnp::introspect::RawStructSchema = ::capnp::introspect::RawStructSchema {
encoded_node: &ENCODED_NODE,
nonunion_members: NONUNION_MEMBERS,
members_by_discriminant: MEMBERS_BY_DISCRIMINANT,
members_by_name: MEMBERS_BY_NAME,
};
pub static NONUNION_MEMBERS : &[u16] = &[0,1,2,3];
pub static MEMBERS_BY_DISCRIMINANT : &[u16] = &[];
pub static MEMBERS_BY_NAME : &[u16] = &[1,2,3,0];
pub const TYPE_ID: u64 = 0xe78f_0c5b_590e_1932;
}
}
pub mod agent {
#[derive(Copy, Clone)]
pub struct Owned(());
impl ::capnp::introspect::Introspect for Owned { fn introspect() -> ::capnp::introspect::Type { ::capnp::introspect::TypeVariant::Struct(::capnp::introspect::RawBrandedStructSchema { generic: &_private::RAW_SCHEMA, field_types: _private::get_field_types, annotation_types: _private::get_annotation_types }).into() } }
impl ::capnp::traits::Owned for Owned { type Reader<'a> = Reader<'a>; type Builder<'a> = Builder<'a>; }
impl ::capnp::traits::OwnedStruct for Owned { type Reader<'a> = Reader<'a>; type Builder<'a> = Builder<'a>; }
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
impl <'a,> ::core::marker::Copy for Reader<'a,> {}
impl <'a,> ::core::clone::Clone for Reader<'a,> {
fn clone(&self) -> Self { *self }
}
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
const TYPE_ID: u64 = _private::TYPE_ID;
}
impl <'a,> ::core::convert::From<::capnp::private::layout::StructReader<'a>> for Reader<'a,> {
fn from(reader: ::capnp::private::layout::StructReader<'a>) -> Self {
Self { reader, }
}
}
impl <'a,> ::core::convert::From<Reader<'a,>> for ::capnp::dynamic_value::Reader<'a> {
fn from(reader: Reader<'a,>) -> Self {
Self::Struct(::capnp::dynamic_struct::Reader::new(reader.reader, ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema { generic: &_private::RAW_SCHEMA, field_types: _private::get_field_types::<>, annotation_types: _private::get_annotation_types::<>})))
}
}
impl <'a,> ::core::fmt::Debug for Reader<'a,> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::result::Result<(), ::core::fmt::Error> {
core::fmt::Debug::fmt(&::core::convert::Into::<::capnp::dynamic_value::Reader<'_>>::into(*self), f)
}
}
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [::capnp::Word]>) -> ::capnp::Result<Self> {
::core::result::Result::Ok(reader.get_struct(default)?.into())
}
}
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
self.reader
}
}
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
}
}
impl <'a,> Reader<'a,> {
pub fn reborrow(&self) -> Reader<'_,> {
Self { .. *self }
}
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
self.reader.total_size()
}
#[inline]
pub fn get_name(self) -> ::capnp::Result<::capnp::text::Reader<'a>> {
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(0), ::core::option::Option::None)
}
#[inline]
pub fn has_name(&self) -> bool {
!self.reader.get_pointer_field(0).is_null()
}
}
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
const STRUCT_SIZE: ::capnp::private::layout::StructSize = ::capnp::private::layout::StructSize { data: 0, pointers: 1 };
}
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
const TYPE_ID: u64 = _private::TYPE_ID;
}
impl <'a,> ::core::convert::From<::capnp::private::layout::StructBuilder<'a>> for Builder<'a,> {
fn from(builder: ::capnp::private::layout::StructBuilder<'a>) -> Self {
Self { builder, }
}
}
impl <'a,> ::core::convert::From<Builder<'a,>> for ::capnp::dynamic_value::Builder<'a> {
fn from(builder: Builder<'a,>) -> Self {
Self::Struct(::capnp::dynamic_struct::Builder::new(builder.builder, ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema { generic: &_private::RAW_SCHEMA, field_types: _private::get_field_types::<>, annotation_types: _private::get_annotation_types::<>})))
}
}
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
}
}
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Self {
builder.init_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE).into()
}
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [::capnp::Word]>) -> ::capnp::Result<Self> {
::core::result::Result::Ok(builder.get_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE, default)?.into())
}
}
impl <'a,> ::capnp::traits::SetterInput<Owned<>> for Reader<'a,> {
fn set_pointer_builder(mut pointer: ::capnp::private::layout::PointerBuilder<'_>, value: Self, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
}
impl <'a,> Builder<'a,> {
pub fn into_reader(self) -> Reader<'a,> {
self.builder.into_reader().into()
}
pub fn reborrow(&mut self) -> Builder<'_,> {
Builder { builder: self.builder.reborrow() }
}
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
self.builder.as_reader().into()
}
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
self.builder.as_reader().total_size()
}
#[inline]
pub fn get_name(self) -> ::capnp::Result<::capnp::text::Builder<'a>> {
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(0), ::core::option::Option::None)
}
#[inline]
pub fn set_name(&mut self, value: impl ::capnp::traits::SetterInput<::capnp::text::Owned>) {
::capnp::traits::SetterInput::set_pointer_builder(self.builder.reborrow().get_pointer_field(0), value, false).unwrap()
}
#[inline]
pub fn init_name(self, size: u32) -> ::capnp::text::Builder<'a> {
self.builder.get_pointer_field(0).init_text(size)
}
#[inline]
pub fn has_name(&self) -> bool {
!self.builder.is_pointer_field_null(0)
}
}
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Self {
Self { _typeless: typeless, }
}
}
impl Pipeline {
}
mod _private {
pub static ENCODED_NODE: [::capnp::Word; 32] = [
::capnp::word(0, 0, 0, 0, 5, 0, 6, 0),
::capnp::word(160, 129, 44, 52, 151, 203, 164, 244),
::capnp::word(13, 0, 0, 0, 1, 0, 0, 0),
::capnp::word(164, 172, 216, 255, 36, 223, 58, 242),
::capnp::word(1, 0, 7, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(21, 0, 0, 0, 154, 0, 0, 0),
::capnp::word(29, 0, 0, 0, 7, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(25, 0, 0, 0, 63, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(109, 111, 100, 101, 108, 115, 46, 99),
::capnp::word(97, 112, 110, 112, 58, 65, 103, 101),
::capnp::word(110, 116, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 1, 0, 1, 0),
::capnp::word(4, 0, 0, 0, 3, 0, 4, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 1, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(13, 0, 0, 0, 42, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(8, 0, 0, 0, 3, 0, 1, 0),
::capnp::word(20, 0, 0, 0, 2, 0, 1, 0),
::capnp::word(110, 97, 109, 101, 0, 0, 0, 0),
::capnp::word(12, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(12, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
];
pub fn get_field_types(index: u16) -> ::capnp::introspect::Type {
match index {
0 => <::capnp::text::Owned as ::capnp::introspect::Introspect>::introspect(),
_ => panic!("invalid field index {}", index),
}
}
pub fn get_annotation_types(child_index: Option<u16>, index: u32) -> ::capnp::introspect::Type {
panic!("invalid annotation indices ({:?}, {}) ", child_index, index)
}
pub static RAW_SCHEMA: ::capnp::introspect::RawStructSchema = ::capnp::introspect::RawStructSchema {
encoded_node: &ENCODED_NODE,
nonunion_members: NONUNION_MEMBERS,
members_by_discriminant: MEMBERS_BY_DISCRIMINANT,
members_by_name: MEMBERS_BY_NAME,
};
pub static NONUNION_MEMBERS : &[u16] = &[0];
pub static MEMBERS_BY_DISCRIMINANT : &[u16] = &[];
pub static MEMBERS_BY_NAME : &[u16] = &[0];
pub const TYPE_ID: u64 = 0xf4a4_cb97_342c_81a0;
}
}
pub mod lease {
#[derive(Copy, Clone)]
pub struct Owned(());
impl ::capnp::introspect::Introspect for Owned { fn introspect() -> ::capnp::introspect::Type { ::capnp::introspect::TypeVariant::Struct(::capnp::introspect::RawBrandedStructSchema { generic: &_private::RAW_SCHEMA, field_types: _private::get_field_types, annotation_types: _private::get_annotation_types }).into() } }
impl ::capnp::traits::Owned for Owned { type Reader<'a> = Reader<'a>; type Builder<'a> = Builder<'a>; }
impl ::capnp::traits::OwnedStruct for Owned { type Reader<'a> = Reader<'a>; type Builder<'a> = Builder<'a>; }
impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }
pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }
impl <'a,> ::core::marker::Copy for Reader<'a,> {}
impl <'a,> ::core::clone::Clone for Reader<'a,> {
fn clone(&self) -> Self { *self }
}
impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,> {
const TYPE_ID: u64 = _private::TYPE_ID;
}
impl <'a,> ::core::convert::From<::capnp::private::layout::StructReader<'a>> for Reader<'a,> {
fn from(reader: ::capnp::private::layout::StructReader<'a>) -> Self {
Self { reader, }
}
}
impl <'a,> ::core::convert::From<Reader<'a,>> for ::capnp::dynamic_value::Reader<'a> {
fn from(reader: Reader<'a,>) -> Self {
Self::Struct(::capnp::dynamic_struct::Reader::new(reader.reader, ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema { generic: &_private::RAW_SCHEMA, field_types: _private::get_field_types::<>, annotation_types: _private::get_annotation_types::<>})))
}
}
impl <'a,> ::core::fmt::Debug for Reader<'a,> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::result::Result<(), ::core::fmt::Error> {
core::fmt::Debug::fmt(&::core::convert::Into::<::capnp::dynamic_value::Reader<'_>>::into(*self), f)
}
}
impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,> {
fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::core::option::Option<&'a [::capnp::Word]>) -> ::capnp::Result<Self> {
::core::result::Result::Ok(reader.get_struct(default)?.into())
}
}
impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,> {
fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
self.reader
}
}
impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,> {
fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
}
}
impl <'a,> Reader<'a,> {
pub fn reborrow(&self) -> Reader<'_,> {
Self { .. *self }
}
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
self.reader.total_size()
}
#[inline]
pub fn get_id(self) -> ::capnp::Result<::capnp::text::Reader<'a>> {
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(0), ::core::option::Option::None)
}
#[inline]
pub fn has_id(&self) -> bool {
!self.reader.get_pointer_field(0).is_null()
}
#[inline]
pub fn get_lease(self) -> ::capnp::Result<::capnp::text::Reader<'a>> {
::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(1), ::core::option::Option::None)
}
#[inline]
pub fn has_lease(&self) -> bool {
!self.reader.get_pointer_field(1).is_null()
}
}
pub struct Builder<'a> { builder: ::capnp::private::layout::StructBuilder<'a> }
impl <'a,> ::capnp::traits::HasStructSize for Builder<'a,> {
const STRUCT_SIZE: ::capnp::private::layout::StructSize = ::capnp::private::layout::StructSize { data: 0, pointers: 2 };
}
impl <'a,> ::capnp::traits::HasTypeId for Builder<'a,> {
const TYPE_ID: u64 = _private::TYPE_ID;
}
impl <'a,> ::core::convert::From<::capnp::private::layout::StructBuilder<'a>> for Builder<'a,> {
fn from(builder: ::capnp::private::layout::StructBuilder<'a>) -> Self {
Self { builder, }
}
}
impl <'a,> ::core::convert::From<Builder<'a,>> for ::capnp::dynamic_value::Builder<'a> {
fn from(builder: Builder<'a,>) -> Self {
Self::Struct(::capnp::dynamic_struct::Builder::new(builder.builder, ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema { generic: &_private::RAW_SCHEMA, field_types: _private::get_field_types::<>, annotation_types: _private::get_annotation_types::<>})))
}
}
impl <'a,> ::capnp::traits::ImbueMut<'a> for Builder<'a,> {
fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
self.builder.imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
}
}
impl <'a,> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a,> {
fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Self {
builder.init_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE).into()
}
fn get_from_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, default: ::core::option::Option<&'a [::capnp::Word]>) -> ::capnp::Result<Self> {
::core::result::Result::Ok(builder.get_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE, default)?.into())
}
}
impl <'a,> ::capnp::traits::SetterInput<Owned<>> for Reader<'a,> {
fn set_pointer_builder(mut pointer: ::capnp::private::layout::PointerBuilder<'_>, value: Self, canonicalize: bool) -> ::capnp::Result<()> { pointer.set_struct(&value.reader, canonicalize) }
}
impl <'a,> Builder<'a,> {
pub fn into_reader(self) -> Reader<'a,> {
self.builder.into_reader().into()
}
pub fn reborrow(&mut self) -> Builder<'_,> {
Builder { builder: self.builder.reborrow() }
}
pub fn reborrow_as_reader(&self) -> Reader<'_,> {
self.builder.as_reader().into()
}
pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
self.builder.as_reader().total_size()
}
#[inline]
pub fn get_id(self) -> ::capnp::Result<::capnp::text::Builder<'a>> {
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(0), ::core::option::Option::None)
}
#[inline]
pub fn set_id(&mut self, value: impl ::capnp::traits::SetterInput<::capnp::text::Owned>) {
::capnp::traits::SetterInput::set_pointer_builder(self.builder.reborrow().get_pointer_field(0), value, false).unwrap()
}
#[inline]
pub fn init_id(self, size: u32) -> ::capnp::text::Builder<'a> {
self.builder.get_pointer_field(0).init_text(size)
}
#[inline]
pub fn has_id(&self) -> bool {
!self.builder.is_pointer_field_null(0)
}
#[inline]
pub fn get_lease(self) -> ::capnp::Result<::capnp::text::Builder<'a>> {
::capnp::traits::FromPointerBuilder::get_from_pointer(self.builder.get_pointer_field(1), ::core::option::Option::None)
}
#[inline]
pub fn set_lease(&mut self, value: impl ::capnp::traits::SetterInput<::capnp::text::Owned>) {
::capnp::traits::SetterInput::set_pointer_builder(self.builder.reborrow().get_pointer_field(1), value, false).unwrap()
}
#[inline]
pub fn init_lease(self, size: u32) -> ::capnp::text::Builder<'a> {
self.builder.get_pointer_field(1).init_text(size)
}
#[inline]
pub fn has_lease(&self) -> bool {
!self.builder.is_pointer_field_null(1)
}
}
pub struct Pipeline { _typeless: ::capnp::any_pointer::Pipeline }
impl ::capnp::capability::FromTypelessPipeline for Pipeline {
fn new(typeless: ::capnp::any_pointer::Pipeline) -> Self {
Self { _typeless: typeless, }
}
}
impl Pipeline {
}
mod _private {
pub static ENCODED_NODE: [::capnp::Word; 47] = [
::capnp::word(0, 0, 0, 0, 5, 0, 6, 0),
::capnp::word(98, 86, 14, 197, 84, 8, 214, 176),
::capnp::word(13, 0, 0, 0, 1, 0, 0, 0),
::capnp::word(164, 172, 216, 255, 36, 223, 58, 242),
::capnp::word(2, 0, 7, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(21, 0, 0, 0, 154, 0, 0, 0),
::capnp::word(29, 0, 0, 0, 7, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(25, 0, 0, 0, 119, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(109, 111, 100, 101, 108, 115, 46, 99),
::capnp::word(97, 112, 110, 112, 58, 76, 101, 97),
::capnp::word(115, 101, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 1, 0, 1, 0),
::capnp::word(8, 0, 0, 0, 3, 0, 4, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 1, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(41, 0, 0, 0, 26, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(36, 0, 0, 0, 3, 0, 1, 0),
::capnp::word(48, 0, 0, 0, 2, 0, 1, 0),
::capnp::word(1, 0, 0, 0, 1, 0, 0, 0),
::capnp::word(0, 0, 1, 0, 1, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(45, 0, 0, 0, 50, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(40, 0, 0, 0, 3, 0, 1, 0),
::capnp::word(52, 0, 0, 0, 2, 0, 1, 0),
::capnp::word(105, 100, 0, 0, 0, 0, 0, 0),
::capnp::word(12, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(12, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(108, 101, 97, 115, 101, 0, 0, 0),
::capnp::word(12, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(12, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
];
pub fn get_field_types(index: u16) -> ::capnp::introspect::Type {
match index {
0 => <::capnp::text::Owned as ::capnp::introspect::Introspect>::introspect(),
1 => <::capnp::text::Owned as ::capnp::introspect::Introspect>::introspect(),
_ => panic!("invalid field index {}", index),
}
}
pub fn get_annotation_types(child_index: Option<u16>, index: u32) -> ::capnp::introspect::Type {
panic!("invalid annotation indices ({:?}, {}) ", child_index, index)
}
pub static RAW_SCHEMA: ::capnp::introspect::RawStructSchema = ::capnp::introspect::RawStructSchema {
encoded_node: &ENCODED_NODE,
nonunion_members: NONUNION_MEMBERS,
members_by_discriminant: MEMBERS_BY_DISCRIMINANT,
members_by_name: MEMBERS_BY_NAME,
};
pub static NONUNION_MEMBERS : &[u16] = &[0,1];
pub static MEMBERS_BY_DISCRIMINANT : &[u16] = &[];
pub static MEMBERS_BY_NAME : &[u16] = &[0,1];
pub const TYPE_ID: u64 = 0xb0d6_0854_c50e_5662;
}
}

View File

@@ -1,43 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## v0.1.0 (2023-08-26)
### Chore
- <csr-id-1ae70ac5258ae9f8f5471923fefd3e8ab02f46c1/> with changelog
### New Features
- <csr-id-8f8c5fd41aaa82a495dd0933060f0a3a095bbaf1/> with basic package
- <csr-id-821e14fb1256957a107220c6c775565f5abc58c4/> with publish
- <csr-id-e0545c726c44dccfb8ea179266c1da93389c07e4/> with monitoring
- <csr-id-569f5272e667deeef9f269db5eaf3dec57e2df1c/> with monitor
### Commit Statistics
<csr-read-only-do-not-edit/>
- 6 commits contributed to the release.
- 5 commits were understood as [conventional](https://www.conventionalcommits.org).
- 0 issues like '(#ID)' were seen in commit messages
### Commit Details
<csr-read-only-do-not-edit/>
<details><summary>view details</summary>
* **Uncategorized**
- With changelog (1ae70ac)
- Release churn-domain v0.1.0, churn v0.1.0 (34bc81e)
- With basic package (8f8c5fd)
- With publish (821e14f)
- With monitoring (e0545c7)
- With monitor (569f527)
</details>

View File

@@ -1,23 +0,0 @@
[package]
name = "churn-domain"
description.workspace = true
authors.workspace = true
license-file.workspace = true
version= "0.1.0"
edition.workspace = true
publish.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow.workspace = true
tokio.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
clap.workspace = true
dotenv.workspace = true
axum.workspace = true
reqwest.workspace = true
serde.workspace = true
uuid.workspace = true
chrono.workspace = true

View File

@@ -1,55 +0,0 @@
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct LeaseResp {
pub token: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct AgentEnrollReq {
pub lease: String,
pub server: String,
pub agent_name: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ServerEnrollReq {
pub lease: String,
pub agent_name: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ServerMonitorResp {
pub cursor: Option<uuid::Uuid>,
pub logs: Vec<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct LogEvent {
pub id: uuid::Uuid,
pub author: String,
pub content: String,
pub timestamp: chrono::DateTime<chrono::Utc>,
}
impl LogEvent {
pub fn new(author: impl Into<String>, content: impl Into<String>) -> Self {
Self {
id: uuid::Uuid::new_v4(),
author: author.into(),
content: content.into(),
timestamp: chrono::Utc::now(),
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Agent {
pub name: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Lease {
pub id: uuid::Uuid,
pub lease: uuid::Uuid,
}

View File

@@ -1,44 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## v0.1.0 (2023-08-26)
### New Features
- <csr-id-8f8c5fd41aaa82a495dd0933060f0a3a095bbaf1/> with basic package
- <csr-id-821e14fb1256957a107220c6c775565f5abc58c4/> with publish
- <csr-id-e0545c726c44dccfb8ea179266c1da93389c07e4/> with monitoring
- <csr-id-569f5272e667deeef9f269db5eaf3dec57e2df1c/> with monitor
- <csr-id-8c41e1004c11bc3018d36a72be6e38b2e410c362/> with enroll
- <csr-id-97978df287ee42f523f509ac686a13fa0400a026/> add initial churn
- <csr-id-f61d0bbf120607e59145a80b65985ab93c938522/> add simple health check
### Commit Statistics
<csr-read-only-do-not-edit/>
- 8 commits contributed to the release over the course of 2 calendar days.
- 7 commits were understood as [conventional](https://www.conventionalcommits.org).
- 0 issues like '(#ID)' were seen in commit messages
### Commit Details
<csr-read-only-do-not-edit/>
<details><summary>view details</summary>
* **Uncategorized**
- Release churn-domain v0.1.0, churn v0.1.0 (34bc81e)
- With basic package (8f8c5fd)
- With publish (821e14f)
- With monitoring (e0545c7)
- With monitor (569f527)
- With enroll (8c41e10)
- Add initial churn (97978df)
- Add simple health check (f61d0bb)
</details>

View File

@@ -1,29 +0,0 @@
[package]
name = "churn-server"
authors.workspace = true
description.workspace = true
license-file.workspace = true
version= "0.1.0"
edition.workspace = true
publish.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
churn-domain.workspace = true
churn-capnp.workspace = true
anyhow.workspace = true
tokio.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
clap.workspace = true
dotenv.workspace = true
axum.workspace = true
serde.workspace = true
serde_json.workspace = true
uuid.workspace = true
async-trait.workspace = true
itertools.workspace = true
sled.workspace = true

View File

@@ -1,60 +0,0 @@
use std::sync::Arc;
use axum::async_trait;
use churn_capnp::CapnpPackExt;
use churn_domain::{Agent, ServerEnrollReq};
use crate::db::Db;
#[derive(Clone)]
pub struct AgentService(Arc<dyn AgentServiceTrait + Send + Sync + 'static>);
impl AgentService {
pub fn new(db: Db) -> Self {
Self(Arc::new(DefaultAgentService::new(db)))
}
}
impl std::ops::Deref for AgentService {
type Target = Arc<dyn AgentServiceTrait + Send + Sync + 'static>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
struct DefaultAgentService {
agents: Db,
}
impl DefaultAgentService {
pub fn new(db: Db) -> Self {
Self { agents: db }
}
}
#[async_trait]
pub trait AgentServiceTrait {
async fn enroll(&self, req: ServerEnrollReq) -> anyhow::Result<String>;
}
#[async_trait]
impl AgentServiceTrait for DefaultAgentService {
async fn enroll(&self, req: ServerEnrollReq) -> anyhow::Result<String> {
let agent_name = req.agent_name;
self.agents
.insert(
"agents",
&agent_name,
&Agent {
name: agent_name.clone(),
}
.serialize_capnp(),
)
.await?;
Ok(agent_name)
}
}

View File

@@ -1,64 +0,0 @@
use core::slice::SlicePattern;
use std::path::{Path};
use std::sync::Arc;
use async_trait::async_trait;
#[derive(Clone)]
pub struct Db(Arc<dyn DbTrait + Send + Sync + 'static>);
impl Db {
pub fn new_sled(path: &Path) -> Self {
Self(Arc::new(DefaultDb::new(path)))
}
}
impl std::ops::Deref for Db {
type Target = Arc<dyn DbTrait + Send + Sync + 'static>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
struct DefaultDb {
db: sled::Db,
}
impl DefaultDb {
pub fn new(path: &Path) -> Self {
Self {
db: sled::open(path).expect("to be able open a sled path"),
}
}
}
#[async_trait]
pub trait DbTrait {
async fn insert(&self, namespace: &str, key: &str, value: &[u8]) -> anyhow::Result<()>;
async fn get_all(&self, namespace: &str) -> anyhow::Result<Vec<Vec<u8>>>;
}
#[async_trait]
impl DbTrait for DefaultDb {
async fn insert(&self, namespace: &str, key: &str, value: &[u8]) -> anyhow::Result<()> {
let tree = self.db.open_tree(namespace)?;
tree.insert(key, value)?;
//tree.flush_async().await?;
Ok(())
}
async fn get_all(&self, namespace: &str) -> anyhow::Result<Vec<Vec<u8>>> {
let tree = self.db.open_tree(namespace)?;
Ok(tree
.iter()
.flatten()
.map(|(_, val)| val.as_slice().to_vec())
.collect::<Vec<_>>())
}
}

View File

@@ -1,108 +0,0 @@
use std::sync::Arc;
use axum::async_trait;
use churn_domain::LogEvent;
use itertools::Itertools;
use churn_capnp::CapnpPackExt;
use crate::db::Db;
#[derive(Clone)]
pub struct EventService(Arc<dyn EventServiceTrait + Send + Sync + 'static>);
impl EventService {
pub fn new(db: Db) -> Self {
Self(Arc::new(DefaultEventService::new(db)))
}
}
impl std::ops::Deref for EventService {
type Target = Arc<dyn EventServiceTrait + Send + Sync + 'static>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
struct DefaultEventService {
db: Db,
}
impl DefaultEventService {
pub fn new(db: Db) -> Self {
Self { db }
}
}
#[async_trait]
pub trait EventServiceTrait {
async fn append(&self, req: LogEvent) -> anyhow::Result<()>;
async fn get_from_cursor(&self, cursor: uuid::Uuid) -> anyhow::Result<Vec<LogEvent>>;
async fn get_from_beginning(&self) -> anyhow::Result<Vec<LogEvent>>;
async fn get_latest_cursor(&self) -> anyhow::Result<uuid::Uuid>;
}
#[async_trait]
impl EventServiceTrait for DefaultEventService {
async fn append(&self, req: LogEvent) -> anyhow::Result<()> {
self.db
.insert("events_log", &req.id.to_string(), &req.serialize_capnp())
.await?;
Ok(())
}
async fn get_from_cursor(&self, cursor: uuid::Uuid) -> anyhow::Result<Vec<LogEvent>> {
let events = self.db.get_all("events_log").await?;
let events = events
.iter()
.flat_map(|e| match LogEvent::deserialize_capnp(e) {
Ok(o) => Ok(o),
Err(e) => {
tracing::error!("failed to deserialize capnp: {e}");
Err(e)
}
})
.sorted_by_key(|i| i.timestamp)
.skip_while(|item| item.id != cursor)
.skip(1)
.collect();
Ok(events)
}
async fn get_from_beginning(&self) -> anyhow::Result<Vec<LogEvent>> {
let events = self.db.get_all("events_log").await?;
let events = events
.iter()
.map(|x| x.as_slice())
.flat_map(LogEvent::deserialize_capnp)
.sorted_by_key(|i| i.timestamp)
.collect();
Ok(events)
}
async fn get_latest_cursor(&self) -> anyhow::Result<uuid::Uuid> {
let events = self.db.get_all("events_log").await?;
let event = events
.iter()
.flat_map(|e| match LogEvent::deserialize_capnp(e) {
Ok(o) => Ok(o),
Err(e) => {
tracing::error!("failed to deserialize capnp: {e}");
Err(e)
}
})
.sorted_by_key(|i| i.timestamp)
.last();
match event {
Some(x) => Ok(x.id),
None => anyhow::bail!("no events found"),
}
}
}

View File

@@ -1,59 +0,0 @@
use std::sync::Arc;
use axum::async_trait;
use churn_capnp::CapnpPackExt;
use churn_domain::Lease;
use crate::db::Db;
#[derive(Clone)]
pub struct LeaseService(Arc<dyn LeaseServiceTrait + Send + Sync + 'static>);
impl LeaseService {
pub fn new(db: Db) -> Self {
Self(Arc::new(DefaultLeaseService::new(db)))
}
}
impl std::ops::Deref for LeaseService {
type Target = Arc<dyn LeaseServiceTrait + Send + Sync + 'static>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
struct DefaultLeaseService {
db: Db,
}
impl DefaultLeaseService {
pub fn new(db: Db) -> Self {
Self { db }
}
}
#[async_trait]
pub trait LeaseServiceTrait {
async fn create_lease(&self) -> anyhow::Result<String>;
}
#[async_trait]
impl LeaseServiceTrait for DefaultLeaseService {
async fn create_lease(&self) -> anyhow::Result<String> {
let lease = uuid::Uuid::new_v4();
let id = uuid::Uuid::new_v4();
self.db
.insert(
"lease",
&lease.to_string(),
&Lease { id, lease }.serialize_capnp(),
)
.await?;
Ok(lease.to_string())
}
}

View File

@@ -1,238 +0,0 @@
#![feature(slice_pattern)]
mod agent;
mod db;
mod event;
mod lease;
use std::net::SocketAddr;
use std::path::PathBuf;
use agent::AgentService;
use anyhow::Error;
use axum::extract::{Query, State};
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use axum::routing::{get, post};
use axum::{Json, Router};
use churn_domain::{Agent, LeaseResp, LogEvent, ServerEnrollReq, ServerMonitorResp};
use clap::{Args, Parser, Subcommand, ValueEnum};
use event::EventService;
use lease::LeaseService;
use serde::Deserialize;
use serde_json::json;
use tokio::net::TcpListener;
use crate::db::Db;
#[derive(Parser)]
#[command(author, version, about, long_about = None, subcommand_required = true)]
struct Command {
#[command(subcommand)]
command: Option<Commands>,
#[clap(flatten)]
global: GlobalArgs,
}
#[derive(Args)]
struct GlobalArgs {
#[arg(env = "CHURN_DATABASE", long, default_value = "sled")]
database: DatabaseType,
#[arg(env = "CHURN_SLED_PATH", long, default_value = "churn-server.sled")]
sled_path: PathBuf,
}
#[derive(ValueEnum, Clone)]
enum DatabaseType {
Sled,
}
#[derive(Subcommand)]
enum Commands {
Serve {
#[arg(env = "SERVICE_HOST", long, default_value = "127.0.0.1:3000")]
host: SocketAddr,
},
}
#[derive(Clone)]
struct AppState {
agent: AgentService,
leases: LeaseService,
events: EventService,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
dotenv::dotenv().ok();
tracing_subscriber::fmt::init();
let cli = Command::parse();
if let Some(Commands::Serve { host }) = cli.command {
tracing::info!("Starting churn server");
let db = match cli.global.database {
DatabaseType::Sled => Db::new_sled(&cli.global.sled_path),
};
let app = Router::new()
.route("/ping", get(ping))
.route("/logs", get(logs))
.nest(
"/agent",
Router::new()
.route("/enroll", post(enroll))
.route("/ping", post(agent_ping))
.route("/events", post(get_tasks))
.route("/lease", post(agent_lease)),
)
.with_state(AppState {
agent: AgentService::new(db.clone()),
leases: LeaseService::new(db.clone()),
events: EventService::new(db.clone()),
});
tracing::info!("churn server listening on {}", host);
let listener = TcpListener::bind(&host).await?;
axum::serve(listener, app.into_make_service())
.await
.unwrap();
}
Ok(())
}
enum AppError {
Internal(Error),
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, error_message) = match self {
AppError::Internal(e) => {
tracing::error!("failed with error: {}", e);
(
StatusCode::INTERNAL_SERVER_ERROR,
"failed with internal error",
)
}
};
let body = Json(json!({
"error": error_message,
}));
(status, body).into_response()
}
}
async fn enroll(
State(state): State<AppState>,
Json(req): Json<ServerEnrollReq>,
) -> Result<Json<Agent>, AppError> {
state
.events
.append(LogEvent::new(&req.agent_name, "attempting to enroll agent"))
.await
.map_err(AppError::Internal)?;
let name = state.agent.enroll(req).await.map_err(AppError::Internal)?;
state
.events
.append(LogEvent::new(&name, "enrolled agent"))
.await
.map_err(AppError::Internal)?;
Ok(Json(Agent { name }))
}
async fn agent_lease(State(state): State<AppState>) -> Result<Json<LeaseResp>, AppError> {
let lease = state
.leases
.create_lease()
.await
.map_err(AppError::Internal)?;
Ok(Json(LeaseResp { token: lease }))
}
async fn agent_ping() -> impl IntoResponse {
todo!()
}
async fn get_tasks() -> impl IntoResponse {
todo!()
}
async fn ping() -> impl IntoResponse {
"pong!"
}
#[derive(Clone, Deserialize)]
struct LogsQuery {
cursor: Option<uuid::Uuid>,
}
async fn logs(
State(state): State<AppState>,
Query(cursor): Query<LogsQuery>,
) -> Result<Json<ServerMonitorResp>, AppError> {
state
.events
.append(LogEvent::new(
"author",
format!(
"logs called: {}",
cursor
.cursor
.as_ref()
.map(|c| format!("(cursor={c})"))
.unwrap_or("".to_string())
),
))
.await
.map_err(AppError::Internal)?;
match cursor.cursor {
Some(cursor) => {
tracing::debug!("finding logs from cursor: {}", cursor);
}
None => {
tracing::debug!("finding logs from beginning");
}
}
match cursor.cursor {
Some(c) => {
let events = state
.events
.get_from_cursor(c)
.await
.map_err(AppError::Internal)?;
Ok(Json(ServerMonitorResp {
cursor: events.last().map(|e| e.id),
logs: events
.iter()
.map(|e| format!("{}: {}", e.author, e.content))
.collect(),
}))
}
None => {
let cursor = state
.events
.get_latest_cursor()
.await
.map_err(AppError::Internal)?;
Ok(Json(ServerMonitorResp {
cursor: Some(cursor),
logs: Vec::new(),
}))
}
}
}

View File

@@ -1 +0,0 @@
/target

View File

@@ -1,53 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## v0.1.0 (2023-08-26)
<csr-id-1ae70ac5258ae9f8f5471923fefd3e8ab02f46c1/>
### Chore
- <csr-id-1ae70ac5258ae9f8f5471923fefd3e8ab02f46c1/> with changelog
### New Features
- <csr-id-8f8c5fd41aaa82a495dd0933060f0a3a095bbaf1/> with basic package
- <csr-id-821e14fb1256957a107220c6c775565f5abc58c4/> with publish
- <csr-id-e0545c726c44dccfb8ea179266c1da93389c07e4/> with monitoring
- <csr-id-569f5272e667deeef9f269db5eaf3dec57e2df1c/> with monitor
- <csr-id-10eae9b36cfe82b86fe0bf4d7c02f99d727b839d/> with extra churning repl thingy
- <csr-id-97978df287ee42f523f509ac686a13fa0400a026/> add initial churn
- <csr-id-f61d0bbf120607e59145a80b65985ab93c938522/> add simple health check
### Commit Statistics
<csr-read-only-do-not-edit/>
- 11 commits contributed to the release over the course of 2 calendar days.
- 8 commits were understood as [conventional](https://www.conventionalcommits.org).
- 0 issues like '(#ID)' were seen in commit messages
### Commit Details
<csr-read-only-do-not-edit/>
<details><summary>view details</summary>
* **Uncategorized**
- Release churn v0.1.0 (d5212f0)
- Release churn-domain v0.1.0, churn v0.1.0 (e4e05bc)
- With changelog (1ae70ac)
- Release churn-domain v0.1.0, churn v0.1.0 (34bc81e)
- With basic package (8f8c5fd)
- With publish (821e14f)
- With monitoring (e0545c7)
- With monitor (569f527)
- With extra churning repl thingy (10eae9b)
- Add initial churn (97978df)
- Add simple health check (f61d0bb)
</details>

View File

@@ -1,21 +0,0 @@
[package]
name = "churn"
authors.workspace = true
description.workspace = true
license-file.workspace = true
version= "0.1.0"
edition.workspace = true
publish.workspace = true
[dependencies]
churn-domain.workspace = true
anyhow.workspace = true
tokio.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
clap.workspace = true
dotenv.workspace = true
axum.workspace = true
reqwest.workspace = true
uuid.workspace = true

View File

@@ -1,154 +0,0 @@
use churn_domain::{AgentEnrollReq, LeaseResp, ServerMonitorResp};
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(author, version, about, long_about = None, subcommand_required = true)]
struct Command {
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand)]
enum Commands {
Auth {
#[arg(env = "CHURN_SERVER", long)]
server: String,
#[arg(env = "CHURN_SERVER_TOKEN", long)]
server_token: String,
},
Bootstrap {
#[arg(env = "CHURN_AGENT", long)]
agent: String,
#[arg(env = "CHURN_AGENT_NAME", long)]
agent_name: String,
#[arg(env = "CHURN_SERVER", long)]
server: String,
#[arg(env = "CHURN_SERVER_TOKEN", long)]
server_token: String,
},
Health {
#[arg(env = "CHURN_SERVER", long)]
server: String,
#[arg(env = "CHURN_AGENT", long)]
agent: String,
},
Monitor {
#[arg(env = "CHURN_SERVER", long)]
server: String,
#[arg(env = "CHURN_SERVER_TOKEN", long)]
server_token: String,
},
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
dotenv::dotenv().ok();
tracing_subscriber::fmt::init();
let cli = Command::parse();
handle_command(cli).await?;
Ok(())
}
async fn handle_command(cmd: Command) -> anyhow::Result<()> {
if let Some(cmd) = cmd.command {
match cmd {
Commands::Bootstrap {
agent,
agent_name,
server,
server_token: _,
} => {
tracing::info!("enrolling agent: {} for server: {}", agent, server);
let client = reqwest::Client::new();
let req = client.post(format!("{server}/agent/lease")).build()?;
let lease_resp = client.execute(req).await?;
let lease = lease_resp.json::<LeaseResp>().await?;
let req = client
.post(format!("{agent}/enroll"))
.json(&AgentEnrollReq {
lease: lease.token,
server,
agent_name,
})
.build()?;
let lease_resp = client.execute(req).await?;
if !lease_resp.status().is_success() {
if let Ok(text) = lease_resp.text().await {
tracing::warn!(
"could not enroll because agent server encoutered error: {}",
text
);
anyhow::bail!("encountered error: {}", text);
}
anyhow::bail!("encountered error");
}
Ok(())
}
Commands::Health { server, agent } => {
tracing::info!("connecting to server: {}", server);
reqwest::get(format!("{server}/ping")).await?;
tracing::info!("connected to server successfully");
tracing::info!("connecting to agent: {}", agent);
reqwest::get(format!("{agent}/ping")).await?;
tracing::info!("connected to agent successfully");
Ok(())
}
Commands::Auth {
server: _,
server_token: _,
} => todo!(),
Commands::Monitor {
server,
server_token: _,
} => {
tracing::info!("monitoring server: {}", server);
let mut cursor: Option<uuid::Uuid> = None;
loop {
tracing::debug!("reading logs from server: {}", server);
let resp = reqwest::get(format!(
"{server}/logs{}",
match &cursor {
None => "".to_string(),
Some(cursor) => format!("?cursor={}", cursor),
}
))
.await?;
if !resp.status().is_success() {
if let Ok(text) = resp.text().await {
anyhow::bail!("encountered error: {}", text);
}
anyhow::bail!("encountered error");
}
match resp.json::<ServerMonitorResp>().await {
Ok(resp) => {
for line in resp.logs {
tracing::info!("event: {}", line);
}
cursor = resp.cursor;
}
Err(e) => {
tracing::warn!("failed to call server (error={})", e);
}
}
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}
}
}
} else {
panic!("no command supplied")
}
}

View File

@@ -1,12 +0,0 @@
[package]
name = "churning"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
dagger-sdk = "0.11.0"
dagger-rust = {git = "https://git.front.kjuulh.io/kjuulh/dagger-components.git", ref = "main"}
tokio.workspace = true
eyre = "*"

View File

@@ -1,136 +0,0 @@
use std::{path::PathBuf, sync::Arc};
use dagger_rust::build::{RustVersion, SlimImage};
use dagger_sdk::Query;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt};
#[tokio::main]
async fn main() -> eyre::Result<()> {
// let mut config = Config::default();
// config.logger = None;
println!("Building churning...");
//let client = dagger_sdk::connect_opts(config).await?;
let client = dagger_sdk::connect().await?;
let cli = build_container(client.clone(), "churn").await?;
let server = build_container(client.clone(), "churn-server").await?;
let server = server
.with_env_variable("CHURN_DATABASE", "sled")
.with_env_variable("CHURN_SLED_PATH", "/mnt/sled")
.with_mounted_cache("/mnt/sled", client.cache_volume("sled"))
.with_exec(vec!["churn-server", "serve", "--host", "0.0.0.0:3000"])
.with_exposed_port(3000);
let server_service = server.as_service();
let agent = build_container(client.clone(), "churn-agent").await?;
let agent = agent
.with_service_binding("churn-server", server_service.clone())
.with_exec(vec!["churn-agent", "daemon", "--host", "0.0.0.0:3000"])
.with_exposed_port(3000);
let agent_service = agent.as_service();
let churning = cli
.with_service_binding("churn-agent", agent_service)
.with_service_binding("churn-server", server_service)
.with_env_variable("CHURN_SERVER", "http://churn-server:3000")
.with_env_variable("CHURN_SERVER_TOKEN", "something")
.with_env_variable("CHURN_AGENT", "http://churn-agent:3000")
.with_env_variable("CHURN_AGENT_NAME", "churn-agent")
.with_exec(vec![
"churn",
"health",
"--server",
"http://churn-server:3000",
"--agent",
"http://churn-agent:3000",
]);
let stdout = churning.stdout().await?;
println!("{stdout}");
let stderr = churning.stderr().await?;
println!("{stderr}");
churning.sync().await?;
println!("Finished building churning...");
repl(churning).await?; //.with_entrypoint(vec!["churn"])).await?;
Ok(())
}
async fn repl(container: dagger_sdk::Container) -> eyre::Result<()> {
loop {
let stdin = tokio::io::stdin();
let mut stdout = tokio::io::stdout();
stdout.write_all(b"> ").await?;
stdout.flush().await?;
let mut input = String::new();
let mut stdin = tokio::io::BufReader::new(stdin);
stdin.read_line(&mut input).await?;
let input = input.trim();
if input == "q" {
break;
}
let container = container.with_exec(input.split(' ').collect());
match container.stdout().await {
Ok(stdout) => {
println!("{stdout}");
}
Err(e) => {
eprintln!("{}", e);
}
}
match container.sync().await {
Ok(_) => {}
Err(_e) => {
//eprintln!("encountred error: {}", e);
}
}
}
Ok(())
}
async fn build_container(client: Query, bin_name: &str) -> eyre::Result<dagger_sdk::Container> {
let crates = &["crates/*", "ci"];
let debian_deps = &[
"libssl-dev",
"pkg-config",
"openssl",
"git",
"jq",
"capnproto",
];
let debian_image = "debian:bullseye".to_string();
let images = dagger_rust::build::RustBuild::new(client.clone())
.build_release(
None::<PathBuf>,
RustVersion::Nightly,
crates,
debian_deps,
vec![SlimImage::Debian {
image: debian_image,
deps: debian_deps
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>(),
architecture: dagger_rust::build::BuildArchitecture::Amd64,
}],
bin_name,
)
.await?;
Ok(images.first().take().unwrap().clone())
}