Compare commits
50 Commits
churn-doma
...
v2
Author | SHA1 | Date | |
---|---|---|---|
7487e7336e
|
|||
610a465279
|
|||
cfe21ad23c
|
|||
0f5249f620 | |||
30ca5c540f | |||
e0c622f8fd | |||
8159603490 | |||
736e166b76 | |||
542e7aceaf | |||
3e65cda2c9 | |||
9f23dd935a | |||
085123a1b0 | |||
c3dda47512 | |||
0a61a1d429 | |||
385290973a | |||
d61f17e693 | |||
a5773107be | |||
97445670ab | |||
19a812843f | |||
e962ef58be | |||
fad1329650 | |||
e7b513f8d5 | |||
f61c432762 | |||
dd2bf3fe94 | |||
8946b688df | |||
|
0bee7f11b8 | ||
c322171a39
|
|||
09fc25ebd5
|
|||
27d2c640dd
|
|||
160ecd4b2d
|
|||
ee3241ba39
|
|||
91e7f470ef
|
|||
d5f0785c0c | |||
f5bf3f53a4
|
|||
7e22a7f3ab
|
|||
34fabb72be
|
|||
b161e19021 | |||
541b9b22d2
|
|||
d3beab5006
|
|||
86cfc18076
|
|||
43ed89d0d8
|
|||
75d99c2461
|
|||
757d1081bd
|
|||
9e61ed7ef7
|
|||
e8156ae9d0 | |||
d05038ab5a
|
|||
a651c787e6
|
|||
e4270094c1
|
|||
cbc93bc33f
|
|||
d5212f0e29
|
2
.drone.yml
Normal file
2
.drone.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
kind: template
|
||||
load: cuddle-rust-service-plan.yaml
|
1735
Cargo.lock
generated
1735
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
28
Cargo.toml
28
Cargo.toml
@@ -2,31 +2,13 @@
|
||||
members = ["crates/*"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
repository = "https://git.front.kjuulh.io/kjuulh/cuddle-please"
|
||||
description = "Churn is a distributed configuration manager and engine"
|
||||
readme = "README.md"
|
||||
license-file = "LICENSE-MIT"
|
||||
authors = ["kjuulh <contact@kjuulh.io>"]
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = true
|
||||
|
||||
[workspace.dependencies]
|
||||
churn = { path = "crates/churn" }
|
||||
churn-agent = { path = "crates/churn-agent" }
|
||||
churn-server = { path = "crates/churn-server" }
|
||||
churn-domain = { path = "crates/churn-domain" }
|
||||
|
||||
anyhow = { version = "1.0.71" }
|
||||
anyhow = { version = "1" }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
tracing-subscriber = { version = "0.3.17" }
|
||||
clap = { version = "4.3.4", features = ["derive", "env"] }
|
||||
dotenv = { version = "0.15.0" }
|
||||
axum = { version = "0.6.18", features = ["macros"] }
|
||||
async-trait = "*"
|
||||
serde = {version = "1", features = ["derive"]}
|
||||
serde_json = "1"
|
||||
reqwest = {version = "0.11.20", features = ["json"]}
|
||||
uuid = {version = "1.4.1", features = ["v4", "serde"]}
|
||||
tracing-subscriber = { version = "0.3.18" }
|
||||
clap = { version = "4", features = ["derive", "env"] }
|
||||
dotenv = { version = "0.15" }
|
||||
axum = { version = "0.7" }
|
||||
|
@@ -1,8 +0,0 @@
|
||||
Copyright 2023 Kasper J. Hermansen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
@@ -1,3 +1 @@
|
||||
# Churn - A distributed contriguration manager and engine
|
||||
|
||||
## (Work in progress)
|
||||
# churn
|
||||
|
@@ -1,24 +0,0 @@
|
||||
[package]
|
||||
name = "churn-agent"
|
||||
description.workspace = true
|
||||
authors.workspace = true
|
||||
license-file.workspace = true
|
||||
version.workspace = true
|
||||
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
|
@@ -1,85 +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(())
|
||||
}
|
||||
}
|
@@ -1,132 +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;
|
||||
|
||||
#[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)]
|
||||
#[derive(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);
|
||||
axum::Server::bind(&host)
|
||||
.serve(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(())
|
||||
}
|
@@ -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>
|
||||
|
@@ -1,22 +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
|
@@ -1,25 +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>,
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
[package]
|
||||
name = "churn-server"
|
||||
authors.workspace = true
|
||||
description.workspace = true
|
||||
license-file.workspace = true
|
||||
version.workspace = true
|
||||
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
|
||||
uuid.workspace = true
|
@@ -1,64 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::async_trait;
|
||||
|
||||
use churn_domain::ServerEnrollReq;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::Agent;
|
||||
|
||||
#[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 {
|
||||
agents: Arc<Mutex<HashMap<String, Agent>>>,
|
||||
}
|
||||
|
||||
#[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;
|
||||
|
||||
let mut agents = self.agents.lock().await;
|
||||
|
||||
match agents.insert(
|
||||
agent_name.clone(),
|
||||
Agent {
|
||||
name: agent_name.clone(),
|
||||
},
|
||||
) {
|
||||
Some(_) => {
|
||||
tracing::debug!("agents store already contained agent, replaced existing");
|
||||
|
||||
Ok(agent_name)
|
||||
}
|
||||
None => {
|
||||
tracing::debug!("agents store didn't contain agent, inserted");
|
||||
|
||||
Ok(agent_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,78 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::async_trait;
|
||||
|
||||
use churn_domain::ServerEnrollReq;
|
||||
use serde::{ser::SerializeStruct, Deserialize, Serialize};
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EventService(Arc<dyn EventServiceTrait + Send + Sync + 'static>);
|
||||
|
||||
impl std::ops::Deref for EventService {
|
||||
type Target = Arc<dyn EventServiceTrait + Send + Sync + 'static>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EventService {
|
||||
fn default() -> Self {
|
||||
Self(Arc::new(DefaultEventService::default()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct DefaultEventService {
|
||||
agents: Arc<RwLock<Vec<LogEvent>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct LogEvent {
|
||||
pub id: uuid::Uuid,
|
||||
pub author: String,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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_trait]
|
||||
impl EventServiceTrait for DefaultEventService {
|
||||
async fn append(&self, req: LogEvent) -> anyhow::Result<()> {
|
||||
let mut events = self.agents.write().await;
|
||||
events.push(req);
|
||||
Ok(())
|
||||
}
|
||||
async fn get_from_cursor(&self, cursor: uuid::Uuid) -> anyhow::Result<Vec<LogEvent>> {
|
||||
let events = self.agents.read().await;
|
||||
let items = events
|
||||
.iter()
|
||||
.skip_while(|item| item.id != cursor)
|
||||
.skip(1)
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
Ok(items)
|
||||
}
|
||||
async fn get_from_beginning(&self) -> anyhow::Result<Vec<LogEvent>> {
|
||||
let events = self.agents.read().await;
|
||||
Ok(events.iter().cloned().collect())
|
||||
}
|
||||
}
|
@@ -1,57 +0,0 @@
|
||||
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
use axum::{async_trait};
|
||||
|
||||
|
||||
|
||||
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LeaseService(Arc<dyn LeaseServiceTrait + Send + Sync + 'static>);
|
||||
|
||||
impl std::ops::Deref for LeaseService {
|
||||
type Target = Arc<dyn LeaseServiceTrait + Send + Sync + 'static>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LeaseService {
|
||||
fn default() -> Self {
|
||||
Self(Arc::new(DefaultLeaseService::default()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct DefaultLeaseService {
|
||||
leases: Arc<Mutex<Vec<String>>>,
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[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 mut leases = self.leases.lock().await;
|
||||
|
||||
let lease = uuid::Uuid::new_v4().to_string();
|
||||
|
||||
leases.push(lease.clone());
|
||||
|
||||
Ok(lease)
|
||||
}
|
||||
}
|
@@ -1,210 +0,0 @@
|
||||
mod agent;
|
||||
mod event;
|
||||
mod lease;
|
||||
|
||||
use std::net::SocketAddr;
|
||||
|
||||
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::{LeaseResp, ServerEnrollReq, ServerMonitorResp};
|
||||
use clap::{Parser, Subcommand};
|
||||
use event::{EventService, LogEvent};
|
||||
use lease::LeaseService;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None, subcommand_required = true)]
|
||||
struct Command {
|
||||
#[command(subcommand)]
|
||||
command: Option<Commands>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Commands {
|
||||
Serve {
|
||||
#[arg(env = "SERVICE_HOST", long, default_value = "127.0.0.1:3000")]
|
||||
host: SocketAddr,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
struct Agent {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[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();
|
||||
|
||||
match cli.command {
|
||||
Some(Commands::Serve { host }) => {
|
||||
tracing::info!("Starting churn server");
|
||||
|
||||
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::default(),
|
||||
leases: LeaseService::default(),
|
||||
events: EventService::default(),
|
||||
});
|
||||
|
||||
tracing::info!("churn server listening on {}", host);
|
||||
axum::Server::bind(&host)
|
||||
.serve(app.into_make_service())
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
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::trace!("finding logs from cursor: {}", cursor);
|
||||
}
|
||||
None => {
|
||||
tracing::trace!("finding logs from beginning");
|
||||
}
|
||||
}
|
||||
|
||||
let events = match cursor.cursor {
|
||||
Some(c) => state.events.get_from_cursor(c).await,
|
||||
None => state.events.get_from_beginning().await,
|
||||
}
|
||||
.map_err(AppError::Internal)?;
|
||||
|
||||
if events.is_empty() {
|
||||
return Ok(Json(ServerMonitorResp {
|
||||
cursor: cursor.cursor.clone(),
|
||||
logs: Vec::new(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(Json(ServerMonitorResp {
|
||||
cursor: events.last().map(|e| e.id),
|
||||
logs: events
|
||||
.iter()
|
||||
.map(|e| format!("{}: {}", e.author, e.content))
|
||||
.collect(),
|
||||
}))
|
||||
}
|
@@ -1,49 +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
|
||||
- <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/>
|
||||
|
||||
- 9 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**
|
||||
- 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>
|
||||
|
@@ -1,15 +1,9 @@
|
||||
[package]
|
||||
name = "churn"
|
||||
authors.workspace = true
|
||||
description.workspace = true
|
||||
license-file.workspace = true
|
||||
version= "0.1.0"
|
||||
edition.workspace = true
|
||||
publish.workspace = true
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
churn-domain.workspace = true
|
||||
|
||||
anyhow.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
@@ -17,5 +11,10 @@ tracing-subscriber.workspace = true
|
||||
clap.workspace = true
|
||||
dotenv.workspace = true
|
||||
axum.workspace = true
|
||||
reqwest.workspace = true
|
||||
uuid.workspace = true
|
||||
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
uuid = { version = "1.7.0", features = ["v4"] }
|
||||
tower-http = { version = "0.5.2", features = ["cors", "trace"] }
|
||||
notmad = "0.5.0"
|
||||
tokio-util = "0.7.12"
|
||||
async-trait = "0.1.83"
|
||||
|
65
crates/churn/src/api.rs
Normal file
65
crates/churn/src/api.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use axum::{extract::MatchedPath, http::Request, routing::get, Router};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tower_http::trace::TraceLayer;
|
||||
|
||||
use crate::state::SharedState;
|
||||
|
||||
pub struct Api {
|
||||
state: SharedState,
|
||||
host: SocketAddr,
|
||||
}
|
||||
|
||||
impl Api {
|
||||
pub fn new(state: impl Into<SharedState>, host: impl Into<SocketAddr>) -> Self {
|
||||
Self {
|
||||
state: state.into(),
|
||||
host: host.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn serve(&self) -> anyhow::Result<()> {
|
||||
let app = Router::new()
|
||||
.route("/", get(root))
|
||||
.with_state(self.state.clone())
|
||||
.layer(
|
||||
TraceLayer::new_for_http().make_span_with(|request: &Request<_>| {
|
||||
// Log the matched route's path (with placeholders not filled in).
|
||||
// Use request.uri() or OriginalUri if you want the real path.
|
||||
let matched_path = request
|
||||
.extensions()
|
||||
.get::<MatchedPath>()
|
||||
.map(MatchedPath::as_str);
|
||||
|
||||
tracing::info_span!(
|
||||
"http_request",
|
||||
method = ?request.method(),
|
||||
matched_path,
|
||||
some_other_field = tracing::field::Empty,
|
||||
)
|
||||
}), // ...
|
||||
);
|
||||
|
||||
tracing::info!("listening on {}", self.host);
|
||||
let listener = tokio::net::TcpListener::bind(&self.host).await.unwrap();
|
||||
axum::serve(listener, app.into_make_service())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn root() -> &'static str {
|
||||
"Hello, churn!"
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl notmad::Component for Api {
|
||||
async fn run(&self, _cancellation_token: CancellationToken) -> Result<(), notmad::MadError> {
|
||||
self.serve().await.map_err(notmad::MadError::Inner)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
36
crates/churn/src/cli.rs
Normal file
36
crates/churn/src/cli.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
use crate::{api, state::SharedState};
|
||||
|
||||
pub async fn execute() -> anyhow::Result<()> {
|
||||
let state = SharedState::new().await?;
|
||||
|
||||
let cli = Command::parse();
|
||||
if let Some(Commands::Serve { host }) = cli.command {
|
||||
tracing::info!("Starting service");
|
||||
|
||||
notmad::Mad::builder()
|
||||
.add(api::Api::new(&state, host))
|
||||
.run()
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None, subcommand_required = true)]
|
||||
struct Command {
|
||||
#[command(subcommand)]
|
||||
command: Option<Commands>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Commands {
|
||||
Serve {
|
||||
#[arg(env = "SERVICE_HOST", long, default_value = "127.0.0.1:3000")]
|
||||
host: SocketAddr,
|
||||
},
|
||||
}
|
@@ -1,156 +1,13 @@
|
||||
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,
|
||||
},
|
||||
}
|
||||
mod api;
|
||||
mod cli;
|
||||
mod state;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
dotenv::dotenv().ok();
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let cli = Command::parse();
|
||||
|
||||
handle_command(cli).await?;
|
||||
cli::execute().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;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("no command supplied")
|
||||
}
|
||||
}
|
||||
|
32
crates/churn/src/state.rs
Normal file
32
crates/churn/src/state.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SharedState(Arc<State>);
|
||||
|
||||
impl SharedState {
|
||||
pub async fn new() -> anyhow::Result<Self> {
|
||||
Ok(Self(Arc::new(State::new().await?)))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&SharedState> for SharedState {
|
||||
fn from(value: &SharedState) -> Self {
|
||||
value.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SharedState {
|
||||
type Target = Arc<State>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct State {}
|
||||
|
||||
impl State {
|
||||
pub async fn new() -> anyhow::Result<Self> {
|
||||
Ok(Self {})
|
||||
}
|
||||
}
|
@@ -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.2.2"
|
||||
dagger-rust = "0.2.0"
|
||||
tokio.workspace = true
|
||||
eyre = "*"
|
@@ -1,138 +0,0 @@
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
use dagger_rust::build::{RustVersion, SlimImage};
|
||||
use dagger_sdk::{Config, 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_exec(vec!["churn-server", "serve", "--host", "0.0.0.0:3000"])
|
||||
.with_exposed_port(3000);
|
||||
|
||||
let server_id = server.id().await?;
|
||||
|
||||
let agent = build_container(client.clone(), "churn-agent").await?;
|
||||
let agent = agent
|
||||
.with_service_binding("churn-server", server_id.clone())
|
||||
.with_exec(vec!["churn-agent", "daemon", "--host", "0.0.0.0:3000"])
|
||||
.with_exposed_port(3000);
|
||||
|
||||
let churning = cli
|
||||
.with_service_binding("churn-agent", agent.id().await?)
|
||||
.with_service_binding("churn-server", server_id)
|
||||
.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.exit_code().await?;
|
||||
println!("Finished building churning...");
|
||||
|
||||
repl(churning).await?; //.with_entrypoint(vec!["churn"])).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn repl(container: dagger_sdk::Container) -> eyre::Result<()> {
|
||||
let mut container = container;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
container = container.with_exec(input.split(' ').collect());
|
||||
|
||||
match container.stdout().await {
|
||||
Ok(stdout) => {
|
||||
println!("{stdout}");
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
}
|
||||
}
|
||||
// match container.stderr().await {
|
||||
// Ok(stderr) => {
|
||||
// println!("{stderr}");
|
||||
// }
|
||||
// Err(e) => {
|
||||
// eprintln!("{}", e);
|
||||
// }
|
||||
// }
|
||||
|
||||
match container.exit_code().await {
|
||||
Ok(_) => {}
|
||||
Err(_e) => {
|
||||
//eprintln!("encountred error: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn build_container(
|
||||
client: Arc<Query>,
|
||||
bin_name: &str,
|
||||
) -> eyre::Result<dagger_sdk::Container> {
|
||||
let crates = &["crates/*", "ci"];
|
||||
let debian_deps = &["libssl-dev", "pkg-config", "openssl", "git", "jq"];
|
||||
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())
|
||||
}
|
17
cuddle.yaml
17
cuddle.yaml
@@ -1,7 +1,22 @@
|
||||
# yaml-language-server: $schema=https://git.front.kjuulh.io/kjuulh/cuddle/raw/branch/main/schemas/base.json
|
||||
|
||||
base: "git@git.front.kjuulh.io:kjuulh/cuddle-base.git"
|
||||
base: "git@git.front.kjuulh.io:kjuulh/cuddle-rust-service-plan.git"
|
||||
|
||||
vars:
|
||||
service: "churn"
|
||||
registry: kasperhermansen
|
||||
|
||||
database:
|
||||
crdb: "false"
|
||||
|
||||
ingress:
|
||||
- external: "true"
|
||||
- internal: "true"
|
||||
|
||||
cuddle/clusters:
|
||||
dev:
|
||||
env:
|
||||
service.host: "0.0.0.0:3000"
|
||||
prod:
|
||||
env:
|
||||
service.host: "0.0.0.0:3000"
|
||||
|
3
renovate.json
Normal file
3
renovate.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
|
||||
}
|
Reference in New Issue
Block a user