Merge pull request #1 from wserr/feature/add-microservices
Feature/add microservices
This commit is contained in:
3
microservices/readme.md
Normal file
3
microservices/readme.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Microservices
|
||||
|
||||
TODO
|
||||
17
microservices/weather/.dockerignore
Normal file
17
microservices/weather/.dockerignore
Normal file
@@ -0,0 +1,17 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
# App settings
|
||||
Settings.toml
|
||||
6
microservices/weather/.env
Normal file
6
microservices/weather/.env
Normal file
@@ -0,0 +1,6 @@
|
||||
weather_weather_api_base_url=https://api.openweathermap.org/data/2.5/weather
|
||||
weather_weather_api_key=4ce897f7ce77f84919fd990cc68d9d20
|
||||
weather_influx_db_base_url=http://jumpbox.io:8092
|
||||
weather_influx_db_token=R1BGVaxWrX_ySaovweRz6ZTBDvHa5omQkricJGmn3PrI44jpfU7411PSmHOCjGcNR7OcBjmtE9gA62boHHXoKg==
|
||||
weather_latitude=50.854019
|
||||
weather_longitude=3.355230
|
||||
21
microservices/weather/.gitignore
vendored
Normal file
21
microservices/weather/.gitignore
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||
Cargo.lock
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
# App settings
|
||||
Settings.toml
|
||||
.env
|
||||
|
||||
# Log files
|
||||
logs.*.txt
|
||||
18
microservices/weather/Cargo.toml
Normal file
18
microservices/weather/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "weather"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.71"
|
||||
chrono = "0.4.24"
|
||||
config = "0.13.3"
|
||||
env_logger = "0.10.0"
|
||||
influxdb = { version = "0.6.0", features = ["derive"] }
|
||||
log = "0.4.17"
|
||||
reqwest = { version = "0.11.17", features = ["json", "serde_json"] }
|
||||
serde = { version = "1.0.162", features = ["serde_derive"] }
|
||||
serde_json = "1.0.96"
|
||||
tokio = { version = "1.28.0", features = ["macros", "rt-multi-thread"] }
|
||||
9
microservices/weather/Dockerfile
Normal file
9
microservices/weather/Dockerfile
Normal file
@@ -0,0 +1,9 @@
|
||||
FROM rust:1.67 as builder
|
||||
WORKDIR /usr/src/weather
|
||||
COPY . .
|
||||
RUN cargo install --path .
|
||||
|
||||
FROM debian:bullseye-slim as runtime-base
|
||||
RUN apt-get update && apt-get install -y libssl1.1 ca-certificates libsasl2-dev && rm -rf /var/lib/apt/lists/*
|
||||
COPY --from=builder /usr/local/cargo/bin/weather /usr/local/bin/weather
|
||||
CMD ["weather"]
|
||||
5
microservices/weather/build-docker.sh
Executable file
5
microservices/weather/build-docker.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
docker build . -t weather-microservice:latest
|
||||
docker tag weather-microservice:latest registry.willemserruys.com/weather-microservice:latest
|
||||
docker push registry.willemserruys.com/weather-microservice:latest
|
||||
18
microservices/weather/readme.md
Normal file
18
microservices/weather/readme.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Weather microservice
|
||||
|
||||
This microservice will control all weather related actions around the house.
|
||||
|
||||
## Test locally
|
||||
|
||||
Add `Settings.toml` with following content:
|
||||
|
||||
```toml
|
||||
weather_api_base_url="xxx"
|
||||
weather_api_key="xxx"
|
||||
influx_db_base_url="xxx"
|
||||
influx_db_token="xxx"
|
||||
latitude="xxx"
|
||||
longitude="xxx"
|
||||
```
|
||||
|
||||
These settings can also be specified in the environment variables, when running in docker.
|
||||
4
microservices/weather/run-docker.sh
Executable file
4
microservices/weather/run-docker.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
file=logs.$(date "+%F").txt
|
||||
|
||||
docker run --env RUST_LOG=info --env-file ./.env registry.willemserruys.com/weather-microservice:latest &> $file
|
||||
1
microservices/weather/rustfmt.toml
Normal file
1
microservices/weather/rustfmt.toml
Normal file
@@ -0,0 +1 @@
|
||||
edition="2021"
|
||||
80
microservices/weather/src/fetch.rs
Normal file
80
microservices/weather/src/fetch.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct WeatherResponse {
|
||||
pub weather: Vec<Weather>,
|
||||
pub wind: Wind,
|
||||
pub main: Main,
|
||||
pub clouds: Clouds,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Weather {
|
||||
pub id: u16,
|
||||
pub main: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Main {
|
||||
pub temp: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Wind {
|
||||
pub speed: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Clouds {
|
||||
pub all: u16,
|
||||
}
|
||||
|
||||
fn construct_weather_data_url(
|
||||
base_url: &str,
|
||||
latitude: &str,
|
||||
longitude: &str,
|
||||
api_key: &str,
|
||||
current_datetime: &DateTime<Utc>,
|
||||
) -> String {
|
||||
format!(
|
||||
"{}?lat={}&lon={}&dt={}&appid={}&exclude=minutely,hourly,daily,alerts",
|
||||
base_url,
|
||||
latitude,
|
||||
longitude,
|
||||
current_datetime.timestamp(),
|
||||
api_key
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn fetch_weather_data(
|
||||
base_url: &str,
|
||||
latitude: &str,
|
||||
longitude: &str,
|
||||
api_key: &str,
|
||||
current_datetime: &DateTime<Utc>,
|
||||
) -> Result<WeatherResponse, reqwest::Error> {
|
||||
let url = construct_weather_data_url(base_url, latitude, longitude, api_key, current_datetime);
|
||||
reqwest::get(url).await?.json().await
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use chrono::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn should_construct_url() {
|
||||
let result = construct_weather_data_url(
|
||||
"http://test",
|
||||
"1.14",
|
||||
"1.12",
|
||||
"abc",
|
||||
&Utc.with_ymd_and_hms(2023, 1, 2, 1, 0, 0).unwrap(),
|
||||
);
|
||||
assert_eq!(
|
||||
"http://test?lat=1.14&lon=1.12&dt=1672621200&appid=abc&exclude=minutely,hourly,daily,alerts",
|
||||
result
|
||||
);
|
||||
}
|
||||
}
|
||||
14
microservices/weather/src/input.rs
Normal file
14
microservices/weather/src/input.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use crate::mode::Mode;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct Input {
|
||||
#[serde(default)]
|
||||
pub program_mode: Mode,
|
||||
pub weather_api_base_url: String,
|
||||
pub weather_api_key: String,
|
||||
pub influx_db_base_url: String,
|
||||
pub influx_db_token: String,
|
||||
pub latitude: String,
|
||||
pub longitude: String,
|
||||
}
|
||||
68
microservices/weather/src/main.rs
Normal file
68
microservices/weather/src/main.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
mod fetch;
|
||||
mod input;
|
||||
mod mode;
|
||||
mod write;
|
||||
|
||||
use anyhow::Result;
|
||||
use chrono::Utc;
|
||||
use config::Config;
|
||||
use log::info;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
let settings: input::Input = fetch_settings()?;
|
||||
|
||||
match settings.program_mode {
|
||||
mode::Mode::ReadWeatherData => read_weather_data(&settings).await?,
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn read_weather_data(settings: &input::Input) -> Result<()> {
|
||||
info!("Start fetching weather data...");
|
||||
let result = fetch::fetch_weather_data(
|
||||
&settings.weather_api_base_url,
|
||||
&settings.latitude,
|
||||
&settings.longitude,
|
||||
&settings.weather_api_key,
|
||||
&Utc::now(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
info!("Start writing weather data...");
|
||||
let mut map = write::WeatherReading {
|
||||
wind_speed: result.wind.speed,
|
||||
time: Utc::now(),
|
||||
temp: result.main.temp,
|
||||
clouds: result.clouds.all,
|
||||
main: None,
|
||||
id: None,
|
||||
};
|
||||
if let Some(weather_element) = result.weather.first() {
|
||||
map.main = Some(weather_element.main.clone());
|
||||
map.id = Some(weather_element.id);
|
||||
}
|
||||
write::write_weather_data(
|
||||
map,
|
||||
&settings.influx_db_base_url,
|
||||
"homeassistant",
|
||||
&settings.influx_db_token,
|
||||
)
|
||||
.await?;
|
||||
println!("");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fetch_settings() -> Result<input::Input, config::ConfigError> {
|
||||
let settings = Config::builder()
|
||||
// Add in `./Settings.toml`
|
||||
.add_source(config::File::with_name("./Settings").required(false))
|
||||
.add_source(config::Environment::with_prefix("weather"))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
settings.try_deserialize::<input::Input>()
|
||||
}
|
||||
7
microservices/weather/src/mode.rs
Normal file
7
microservices/weather/src/mode.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize, Debug, Default)]
|
||||
pub enum Mode {
|
||||
#[default]
|
||||
ReadWeatherData,
|
||||
}
|
||||
25
microservices/weather/src/write.rs
Normal file
25
microservices/weather/src/write.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use anyhow::Result;
|
||||
use chrono::{DateTime, Utc};
|
||||
use influxdb::{Client, InfluxDbWriteable};
|
||||
|
||||
#[derive(InfluxDbWriteable)]
|
||||
pub struct WeatherReading {
|
||||
pub time: DateTime<Utc>,
|
||||
pub wind_speed: f32,
|
||||
pub clouds: u16,
|
||||
pub temp: f32,
|
||||
pub id: Option<u16>,
|
||||
pub main: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn write_weather_data(
|
||||
reading: WeatherReading,
|
||||
base_url: &str,
|
||||
database: &str,
|
||||
token: &str,
|
||||
) -> Result<()> {
|
||||
let client = Client::new(base_url, database).with_token(token);
|
||||
let query = reading.into_query("WeatherReading");
|
||||
client.query(query).await?;
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user