diff --git a/Cargo.lock b/Cargo.lock index 1ff80b1..dab7a14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "async-trait" +version = "0.1.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atty" version = "0.2.14" @@ -595,6 +606,17 @@ version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +[[package]] +name = "serde_derive" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "serde_json" version = "1.0.68" @@ -647,10 +669,13 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" name = "subbeat" version = "0.0.2" dependencies = [ + "async-trait", "bytes", "clap", "hyper", "hyper-tls", + "serde", + "serde_derive", "serde_json", "tokio", ] diff --git a/Cargo.toml b/Cargo.toml index 5a62780..03d5666 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,9 @@ tokio = { version = "1", features = ["full"] } clap = "2.33.3" hyper = { version = "0.14.13", features = ["full"] } hyper-tls = "0.5.0" -serde_json = "1.0.59" +serde = { version = "1.0" } +serde_json = "1.0" +serde_derive = "1.0" +async-trait = "0.1.51" + + diff --git a/src/grafana_service.rs b/src/grafana_service.rs index 3dc5076..d404152 100644 --- a/src/grafana_service.rs +++ b/src/grafana_service.rs @@ -1,11 +1,16 @@ use crate::types; use hyper::{Body, Client, Method, Request, StatusCode}; -use serde_json::json; use tokio::io::{stdout, AsyncWriteExt as _}; use bytes::Buf as _; +mod prometheus; + +use serde_json; + +use crate::metric::Metric; + pub struct GrafanaService { url: String, api_key: String, @@ -29,34 +34,11 @@ impl GrafanaService { Ok(()) } - pub async fn extract_metrics(&self) -> types::Result<()> { - let (s, p) = self - .post( - "/api/datasources/proxy/1/api/v1/query_range", - // serde_json::json!({ - // "from": "1634237655", - // "to": "1634238555", - // "queries": [ - // { - // "datasourceId": 1, - // "refId": "A", - // "expr": "rate(go_memstats_alloc_bytes_total[5m])", - // "format": "time_series", - // "step": "15", - // "start": "1634329050", - // "end": "1634329950" - // } - // ] - // "query": "rate(go_memstats_alloc_bytes_total[5m])", - // "start": 1634672070, - // "end": 1634672970, - // "step": "15" - // }), - serde_json::json!({}) - - ) - .await?; - println!("{}", p.to_string()); + pub async fn extract_metrics(&self, panel_url: &str) -> types::Result<()> { + let pm = prometheus::Prometheus::new(self, "rate(go_memstats_alloc_bytes_total[5m])", 15); + let r = pm.query(1634672070, 1634672970).await; + + // println!("{}", p.to_string()); Ok(()) } @@ -80,20 +62,42 @@ impl GrafanaService { Ok((status, result)) } - async fn post( + async fn post_form( &self, suburl: &str, - value: serde_json::Value, + value: &str, ) -> types::Result<(StatusCode, serde_json::Value)> { let req = Request::builder() .method(Method::POST) .uri(self.url.to_owned() + suburl) .header("Accept", "application/json") - // .header("Content-Type", "application/json") .header("Content-Type", "application/x-www-form-urlencoded") .header("Authorization", format!("Bearer {}", self.api_key)) - // .body(Body::from(value.to_string())) - .body(Body::from("query=rate%28go_memstats_alloc_bytes_total%5B5m%5D%29&start=1634672070&end=1634672970&step=15")) + .body(Body::from(value.to_string())) + .unwrap(); + + let client = Client::new(); + let res = client.request(req).await?; + let status = res.status(); + + let body = hyper::body::aggregate(res).await?; + let reader = body.reader(); + let result: serde_json::Value = serde_json::from_reader(reader)?; + Ok((status, result)) + } + + async fn post_json( + &self, + suburl: &str, + value: serde_json::Value, + ) -> types::Result<(StatusCode, serde_json::Value)> { + let req = Request::builder() + .method(Method::POST) + .uri(self.url.to_owned() + suburl) + .header("Accept", "application/json") + .header("Content-Type", "application/json") + .header("Authorization", format!("Bearer {}", self.api_key)) + .body(Body::from(value.to_string())) .unwrap(); let client = Client::new(); diff --git a/src/grafana_service/prometheus.rs b/src/grafana_service/prometheus.rs new file mode 100644 index 0000000..6029e04 --- /dev/null +++ b/src/grafana_service/prometheus.rs @@ -0,0 +1,58 @@ +use async_trait::async_trait; + +use crate::{ + metric::{Metric, MetricResult}, + types, +}; + +use serde_derive::{Deserialize, Serialize}; + +use super::GrafanaService; + +pub struct Prometheus<'a> { + query: String, + step: u32, + grafana_service: &'a GrafanaService, +} + +#[derive(Deserialize, Serialize)] +struct Query { + query: String, + step: u32, + start: u64, + end: u64, +} + +impl<'a> Prometheus<'a> { + pub fn new(grafana_service: &'a GrafanaService, query: &str, step: u32) -> Prometheus<'a> { + Prometheus { + grafana_service, + query: query.to_string(), + step, + } + } +} + +#[async_trait] +impl Metric for Prometheus<'_> { + async fn query(&self, from: u64, to: u64) -> types::Result { + let q = Query { + query: self.query.to_owned(), + step: self.step, + start: from, + end: to, + }; + let url = "/api/datasources/proxy/1/api/v1/query_range"; + // TODO: use serialisatoin from serde + let rq = "query=rate%28go_memstats_alloc_bytes_total%5B5m%5D%29&start=1634672070&end=1634672970&step=15"; + let (status_code, value) = self.grafana_service.post_form(&url, &rq).await?; + // TODO: return error + // if status_code != StatusCode::OK { + // return std::error::("Bad status code"); + // } + + println!("{:?}", value); + + return Ok(Vec::new()); + } +} diff --git a/src/main.rs b/src/main.rs index 0693f27..bb8d51f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,8 @@ async fn main() -> types::Result<()> { // gs.test_connection().await?; // gs.get_datasources().await?; - gs.extract_metrics().await?; + gs.extract_metrics("http://localhost:3000/d/YeBxHjzWz/starter-app-stats?editPanel=2&orgId=1") + .await?; Ok(()) } diff --git a/src/metric.rs b/src/metric.rs index 43e0205..eecbdb9 100644 --- a/src/metric.rs +++ b/src/metric.rs @@ -1,5 +1,8 @@ +use async_trait::async_trait; use std::collections::HashMap; +use crate::types; + pub type MetricId = String; struct DatasourceParams { @@ -23,6 +26,9 @@ struct MetricQuery { headers: Option>, } -trait Metric { - fn query(); +pub type MetricResult = Vec<(u64, f64)>; + +#[async_trait] +pub trait Metric { + async fn query(&self, from: u64, to: u64) -> types::Result; }