Refactor the project configuration to take in a github configuration
This allows for fetching the Jankyfile directly via REST API so the server doesn't have to do any Git work just yet
This commit is contained in:
parent
909dbe587d
commit
19126ed88b
33
api-description-server.yml
Normal file
33
api-description-server.yml
Normal file
@ -0,0 +1,33 @@
|
||||
---
|
||||
openapi: "3.0.0"
|
||||
info:
|
||||
description: |
|
||||
Janky Server API defintion
|
||||
version: "1.0.0"
|
||||
title: Janky APIs
|
||||
contact:
|
||||
email: "rtyler+janky@brokenco.de"
|
||||
license:
|
||||
name: "AGPL v3.0"
|
||||
url: "https://www.gnu.org/licenses/agpl-3.0.en.html"
|
||||
servers:
|
||||
- url: 'http://localhost:8000/api/v1'
|
||||
description: Local dev server (APIv1)
|
||||
|
||||
paths:
|
||||
'/projects/{name}':
|
||||
post:
|
||||
summary: 'Trigger execution for this project'
|
||||
description:
|
||||
parameters:
|
||||
- in: path
|
||||
name: name
|
||||
required: true
|
||||
example: 'janky'
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
404:
|
||||
summary: 'No project configured by that name'
|
||||
200:
|
||||
summary: 'Execution has been triggered'
|
@ -1 +1 @@
|
||||
{"openapi":"3.0.0","info":{"description":"Janky Agent API defintion\n","version":"1.0.0","title":"Janky APIs","contact":{"email":"rtyler+janky@brokenco.de"},"license":{"name":"AGPL v3.0","url":"https://www.gnu.org/licenses/agpl-3.0.en.html"}},"servers":[{"url":"http://localhost:9000/api/v1","description":"Local dev agent (APIv1)"}],"paths":{"/capabilities":{"get":{"summary":"Retrieve a list of capabilities of this agent","description":null,"responses":{"200":{"description":"Getting capabilities","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CapsResponse"}}}}}}},"/execute":{"put":{"summary":"Execute a series of commands on this agent","description":null,"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommandRequest"},"example":{"commands":[{"script":"echo \"Hi\""}]}}}},"responses":{"200":{"description":"Successfully accepted the commands for execution","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommandResponse"}}}},"409":{"description":"Returned when the agent is busy with another series of commands"}}}}},"components":{"schemas":{"CapsResponse":{"type":"object","properties":{"caps":{"type":"array","items":{"$ref":"#/components/schemas/Capability"}}}},"Capability":{"type":"object","properties":{"name":{"type":"string"},"path":{"type":"string"},"data":{"type":"object"}}},"Command":{"type":"object","properties":{"script":{"type":"string","description":"A script that can be exec()'d on the agent"}}},"CommandRequest":{"type":"object","properties":{"commands":{"type":"array","items":{"$ref":"#/components/schemas/Command"}}}},"CommandResponse":{"type":"object","properties":{"uuid":{"type":"string","format":"uuid"},"stream":{"description":"URL to streaming WebSockets logs","type":"string"},"task":{"description":"URL to the task metadata","type":"string"}}}}}}
|
||||
{"openapi":"3.0.0","info":{"description":"Janky Server API defintion\n","version":"1.0.0","title":"Janky APIs","contact":{"email":"rtyler+janky@brokenco.de"},"license":{"name":"AGPL v3.0","url":"https://www.gnu.org/licenses/agpl-3.0.en.html"}},"servers":[{"url":"http://localhost:8000/api/v1","description":"Local dev server (APIv1)"}],"paths":{"/projects/{name}":{"post":{"summary":"Trigger execution for this project","description":null,"parameters":[{"in":"path","name":"name","required":true,"example":"janky","schema":{"type":"string"}}],"responses":{"404":{"summary":"No project configured by that name"},"200":{"summary":"Execution has been triggered"}}}}}}
|
||||
|
@ -2,10 +2,13 @@
|
||||
agents:
|
||||
- 'http://localhost:9000'
|
||||
projects:
|
||||
- type: 'github'
|
||||
url: 'https://github.com/rtyler/janky'
|
||||
ref: 'main'
|
||||
'janky':
|
||||
filename: 'Jankyfile'
|
||||
scm:
|
||||
github:
|
||||
owner: 'rtyler'
|
||||
repo: 'janky'
|
||||
ref: 'main'
|
||||
# The filetype Git is not yet supported
|
||||
#- type: 'git'
|
||||
# url: 'https://github.com/rtyler/jdp'
|
||||
|
@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
exec ruby -ryaml -rjson -e 'puts JSON.dump(YAML.load(STDIN.read))' < agent-api-description.yml > apidocs/api-description.json
|
||||
exec ruby -ryaml -rjson -e 'puts JSON.dump(YAML.load(STDIN.read))' < api-description-agent.yml > apidocs/api-description.json
|
3
scripts/prepare-server-api
Executable file
3
scripts/prepare-server-api
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
exec ruby -ryaml -rjson -e 'puts JSON.dump(YAML.load(STDIN.read))' < api-description-server.yml > apidocs/api-description.json
|
@ -5,6 +5,7 @@
|
||||
#[macro_use]
|
||||
extern crate serde_json;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use async_std::sync::{Arc, RwLock};
|
||||
@ -19,13 +20,15 @@ use url::Url;
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AppState<'a> {
|
||||
pub db: SqlitePool,
|
||||
pub config: ServerConfig,
|
||||
hb: Arc<RwLock<Handlebars<'a>>>,
|
||||
}
|
||||
|
||||
impl AppState<'_> {
|
||||
fn new(db: SqlitePool) -> Self {
|
||||
fn new(db: SqlitePool, config: ServerConfig) -> Self {
|
||||
Self {
|
||||
db,
|
||||
config,
|
||||
hb: Arc::new(RwLock::new(Handlebars::new())),
|
||||
}
|
||||
}
|
||||
@ -74,52 +77,84 @@ mod routes {
|
||||
"page": "home"
|
||||
});
|
||||
|
||||
let res = octocrab::instance()
|
||||
.repos("rtyler", "janky")
|
||||
.raw_file(
|
||||
octocrab::params::repos::Commitish("main".into()),
|
||||
"Jankyfile",
|
||||
)
|
||||
.await?;
|
||||
|
||||
debug!("jank: {:?}", res);
|
||||
debug!("text: {:?}", res.text().await?);
|
||||
|
||||
let mut body = req.state().render("index", ¶ms).await?;
|
||||
body.set_mime("text/html");
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
pub mod api {}
|
||||
pub mod api {
|
||||
use crate::{AppState, Scm};
|
||||
use log::*;
|
||||
use tide::{Body, StatusCode, Request, Response};
|
||||
|
||||
/**
|
||||
* POST /projects/{name}
|
||||
*/
|
||||
pub async fn execute_project(req: Request<AppState<'_>>) -> Result<Response, tide::Error> {
|
||||
let name: String = req.param("name")?.into();
|
||||
|
||||
if ! req.state().config.has_project(&name) {
|
||||
debug!("Could not find project named: {}", name);
|
||||
return Ok(Response::new(StatusCode::NotFound));
|
||||
}
|
||||
|
||||
if let Some(project) = req.state().config.projects.get(&name) {
|
||||
match &project.scm {
|
||||
Scm::GitHub { owner, repo, scm_ref } => {
|
||||
debug!("Fetching the file {} from {}/{}", &project.filename, owner, repo);
|
||||
let res = octocrab::instance()
|
||||
.repos(owner, repo)
|
||||
.raw_file(
|
||||
octocrab::params::repos::Commitish(scm_ref.into()),
|
||||
&project.filename
|
||||
)
|
||||
.await?;
|
||||
debug!("text: {:?}", res.text().await?);
|
||||
},
|
||||
}
|
||||
return Ok("{}".into());
|
||||
}
|
||||
Ok(Response::new(StatusCode::InternalServerError))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
enum Scm {
|
||||
GitHub,
|
||||
GitHub {
|
||||
owner: String,
|
||||
repo: String,
|
||||
#[serde(rename = "ref")]
|
||||
scm_ref: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
struct Project {
|
||||
#[serde(rename = "type")]
|
||||
scm_type: Scm,
|
||||
url: Url,
|
||||
#[serde(rename = "ref")]
|
||||
scm_ref: String,
|
||||
filename: PathBuf,
|
||||
#[serde(with = "serde_yaml::with::singleton_map")]
|
||||
scm: Scm,
|
||||
filename: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
struct ServerConfig {
|
||||
pub struct ServerConfig {
|
||||
agents: Vec<Url>,
|
||||
projects: Vec<Project>,
|
||||
projects: HashMap<String, Project>,
|
||||
}
|
||||
|
||||
impl ServerConfig {
|
||||
fn has_project(&self, name: &str) -> bool {
|
||||
self.projects.contains_key(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ServerConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
agents: vec![],
|
||||
projects: vec![],
|
||||
projects: HashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -160,7 +195,7 @@ async fn main() -> Result<(), tide::Error> {
|
||||
sqlx::migrate!().run(&pool).await?;
|
||||
}
|
||||
|
||||
let state = AppState::new(pool);
|
||||
let state = AppState::new(pool, config);
|
||||
state.register_templates().await;
|
||||
let mut app = tide::with_state(state);
|
||||
|
||||
@ -193,6 +228,7 @@ async fn main() -> Result<(), tide::Error> {
|
||||
app.at("/static").serve_dir("static/")?;
|
||||
debug!("Configuring routes");
|
||||
app.at("/").get(routes::index);
|
||||
app.at("/api/v1/projects/:name").post(routes::api::execute_project);
|
||||
app.listen(opts.listen).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user