ci: add docker support

This commit is contained in:
Matthieu Bessat 2024-08-02 16:39:21 +02:00
parent 3384228e61
commit 84a456003c
10 changed files with 189 additions and 112 deletions

3
.dockerignore Normal file
View file

@ -0,0 +1,3 @@
target
.env
tmp

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
/target
autotasker.db
.env
tmp/

159
Cargo.lock generated
View file

@ -27,7 +27,7 @@ dependencies = [
"getrandom",
"once_cell",
"version_check",
"zerocopy",
"zerocopy 0.7.35",
]
[[package]]
@ -62,9 +62,9 @@ dependencies = [
[[package]]
name = "anstream"
version = "0.6.14"
version = "0.6.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
dependencies = [
"anstyle",
"anstyle-parse",
@ -77,33 +77,33 @@ dependencies = [
[[package]]
name = "anstyle"
version = "1.0.7"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
[[package]]
name = "anstyle-parse"
version = "0.2.4"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.0"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.3"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
dependencies = [
"anstyle",
"windows-sys 0.52.0",
@ -180,7 +180,6 @@ dependencies = [
"argh",
"axum",
"chrono",
"clap",
"env_logger",
"fully_pub",
"log",
@ -317,15 +316,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.6.1"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
[[package]]
name = "cc"
version = "1.1.6"
version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f"
checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc"
[[package]]
name = "cfg-if"
@ -348,38 +347,11 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "clap"
version = "4.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_lex"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70"
[[package]]
name = "colorchoice"
version = "1.0.1"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]]
name = "const-oid"
@ -493,9 +465,9 @@ dependencies = [
[[package]]
name = "env_filter"
version = "0.1.0"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
dependencies = [
"log",
"regex",
@ -503,9 +475,9 @@ dependencies = [
[[package]]
name = "env_logger"
version = "0.11.3"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9"
checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d"
dependencies = [
"anstream",
"anstyle",
@ -888,9 +860,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "2.2.6"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
dependencies = [
"equivalent",
"hashbrown",
@ -898,9 +870,9 @@ dependencies = [
[[package]]
name = "is_terminal_polyfill"
version = "1.70.0"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itoa"
@ -1011,9 +983,9 @@ dependencies = [
[[package]]
name = "minijinja"
version = "2.1.0"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45f7e8e35b6c7b169bf40b0176d2c79291ab8ee53290b84e0668ab21d841aa9d"
checksum = "f4bf71af278c578cbcc91d0b1ff092910208bc86f7b3750364642bd424e3dcd3"
dependencies = [
"serde",
]
@ -1035,13 +1007,14 @@ dependencies = [
[[package]]
name = "mio"
version = "0.8.11"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4"
dependencies = [
"hermit-abi",
"libc",
"wasi",
"windows-sys 0.48.0",
"windows-sys 0.52.0",
]
[[package]]
@ -1112,21 +1085,11 @@ dependencies = [
"libm",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "object"
version = "0.36.1"
version = "0.36.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce"
checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e"
dependencies = [
"memchr",
]
@ -1242,9 +1205,12 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f"
dependencies = [
"zerocopy 0.6.6",
]
[[package]]
name = "proc-macro2"
@ -1420,11 +1386,12 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.120"
version = "1.0.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
@ -1772,12 +1739,6 @@ dependencies = [
"unicode-properties",
]
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "strum_macros"
version = "0.26.4"
@ -1880,21 +1841,20 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.38.1"
version = "1.39.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df"
checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
dependencies = [
"backtrace",
"bytes",
"libc",
"mio",
"num_cpus",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.48.0",
"windows-sys 0.52.0",
]
[[package]]
@ -1914,9 +1874,9 @@ dependencies = [
[[package]]
name = "tokio-macros"
version = "2.3.0"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote",
@ -2133,9 +2093,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.4"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasi"
@ -2361,13 +2321,34 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "zerocopy"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6"
dependencies = [
"byteorder",
"zerocopy-derive 0.6.6",
]
[[package]]
name = "zerocopy"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"zerocopy-derive",
"zerocopy-derive 0.7.35",
]
[[package]]
name = "zerocopy-derive"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
]
[[package]]

View file

@ -10,14 +10,13 @@ serde_yaml = "0.9"
chrono = { version = "0.4.26", features = ["serde"] }
sqlx = { version = "0.7.4", features = ["sqlite", "runtime-tokio", "chrono", "uuid"] }
anyhow = "1.0.75"
clap = "4.5.4"
tokio = { version = "1.37.0", features = ["full"] }
axum = { version = "0.7.5", features = ["json"] }
minijinja = { version = "2.1", features = ["builtins"] }
uuid = { version = "1.8.0", features = ["serde", "v4"] }
fully_pub = "0.1.4"
log = "0.4.22"
env_logger = "0.11.3"
env_logger = "0.11.5"
tower-http = { version = "0.5.2", features = ["fs"] }
tokio-cron-scheduler = "0.10.2"
argh = "0.1.12"

24
Dockerfile Normal file
View file

@ -0,0 +1,24 @@
FROM rust:1.80-alpine3.20 as builder
WORKDIR /usr/src/autotasker
COPY . .
RUN apk add musl-dev
RUN cargo install --locked --path .
FROM alpine:3.20
RUN apk add sqlite
COPY --from=builder /usr/local/cargo/bin/autotasker /usr/local/bin/autotasker
RUN mkdir -p /usr/local/src/autotasker/migrations
RUN mkdir -p /usr/local/lib/autotasker/assets
RUN mkdir -p /var/lib/autotasker
RUN mkdir -p /etc/autotasker
COPY --from=builder /usr/src/autotasker/migrations/all.sql /usr/local/src/autotasker/migrations
COPY --from=builder /usr/src/autotasker/init_db.sh /usr/local/bin/autotasker_init_db.sh
COPY --from=builder /usr/src/autotasker/assets /usr/local/lib/autotasker/assets
USER 1000
ENV RUST_LOG=info
ENV RUST_BACKTRACE=1
CMD ["autotasker", "--listen-host", "127.0.0.1", "--listen-port", "8080"]

View file

@ -7,7 +7,7 @@ Autotasker free software licenced under [GPLv3](https://www.gnu.org/licenses/gpl
## Getting started
- Create a config file to define your tasks.
- Deploy the server (using git clone + cargo or docker or binary + systemd service).
- Deploy the server with docker or using git clone + cargo install binary + systemd service.
- Enjoy!
## Philosophy
@ -35,9 +35,35 @@ Unix-like
- `Task` is a configured command to be run.
- `TaskRun` refer to a singular task run.
## Deploy & Usage
### Standard
You can use standard rust and cargo command to run the server.
You can start the daemon with `autotasker --config /path/to/config --database /path/to/database.db`.
### Docker
Build the alpine image:
just docker-build
Initialize the db:
just docker-init-db
Run with config and db volume:
just docker-run
## Getting started on development
This project use a `justfile`. [just](https://just.systems/man/en/) is a handy way to save and run project-specific commands.
## Built with
- [Axum](https://lib.rs/crates/axum) for web server.
- [Tera](https://lib.rs/crates/tera) for templating.
- [Sqlx](https://lib.rs/crates/sqlx) to interact with Sqlite.
- [Sqlite](https://sqlite.org/) for storing state and logs.

View file

@ -3,6 +3,8 @@
## TODO
- [x] Add CSS badge and color code on job status
- [x] Add `Dockerfile`
- [ ] Add cli arg to change listen address and port
- [ ] Add tasks timeout
- [ ] Add details on runtime
- [ ] Implement basic auth with OAuth2, find a minimal oauth2
@ -11,7 +13,6 @@
- [ ] Don't use long UUID, but only ids
- [ ] Validating config file
- validate schedule CRON syntax
- [ ] Add `Dockerfile` and docker-compose example
- [ ] Add CI/CD to build docker image
- [ ] Add configuration options to limit the logs capture head and tail
- In a limited mode, we would filter out logs and only keep the head and tail AND Errors or stacktrace

9
init_db.sh Executable file
View file

@ -0,0 +1,9 @@
#!/bin/sh
DEFAULT_DB_PATH="/var/lib/autotasker/autotasker.db"
DEFAULT_MIGRATION_PATH="/usr/local/src/autotasker/migrations/all.sql"
DB_PATH="${DB_PATH:-$DEFAULT_DB_PATH}"
MIGRATION_PATH="${MIGRATION_PATH:-$DEFAULT_MIGRATION_PATH}"
sqlite3 $DB_PATH < $MIGRATION_PATH

View file

@ -4,3 +4,13 @@ export RUST_LOG := "trace"
watch-run:
cargo-watch -x 'run -- --config config.yaml'
docker-run:
docker run -p 3085:8080 -v ./config:/etc/autotasker -v ./db:/var/lib/autotasker autotasker
docker-init-db:
docker run -v ./config:/etc/autotasker -v ./db:/var/lib/autotasker autotasker /usr/local/bin/autotasker_init_db.sh
docker-build:
docker build -t autotasker .

View file

@ -37,6 +37,21 @@ struct CliFlags {
/// path to YAML config file to use to configure autotasker
#[argh(option)]
config: Option<String>,
/// path to the Sqlite3 DB file to use
#[argh(option)]
database: Option<String>,
/// path to the static assets dir
#[argh(option)]
static_assets: Option<String>,
/// HTTP listen host
#[argh(option, default="String::from(\"localhost\")")]
listen_host: String,
/// HTTP listen port
#[argh(option, default="8085")]
listen_port: u32
}
fn get_config(path: String) -> Result<Config> {
@ -50,18 +65,20 @@ fn get_config(path: String) -> Result<Config> {
fn build_templating_env() -> Environment<'static> {
let mut templating_env = Environment::new();
templating_env
.add_template("layouts/base.html", include_str!("./templates/layouts/base.html"))
.unwrap();
for path in fs::read_dir("./src/templates/pages").unwrap() {
let file_name = path.unwrap().file_name().into_string().unwrap();
let path = format!("./src/templates/pages/{}", file_name);
let content: &'static str = Box::leak(fs::read_to_string(&path).unwrap().into_boxed_str());
let path: &'static str = Box::leak(format!("pages/{}", file_name).into_boxed_str());
templating_env
.add_template(path, content)
.unwrap();
}
let _ = templating_env
.add_template("layouts/base.html", include_str!("./templates/layouts/base.html"));
let _ = templating_env
.add_template("pages/home.html", include_str!("./templates/pages/home.html"));
let _ = templating_env
.add_template("pages/list_tasks.html", include_str!("./templates/pages/list_tasks.html"));
let _ = templating_env
.add_template("pages/list_task_runs.html", include_str!("./templates/pages/list_task_runs.html"));
let _ = templating_env
.add_template("pages/task_run_details.html", include_str!("./templates/pages/task_run_details.html"));
let _ = templating_env
.add_template("pages/run_task.html", include_str!("./templates/pages/run_task.html"));
// TODO: better loading with embed https://docs.rs/minijinja-embed/latest/minijinja_embed/
templating_env.add_global("gl", context! {
instance => context! {
version => "1.243".to_string()
@ -70,13 +87,19 @@ fn build_templating_env() -> Environment<'static> {
templating_env
}
const DEFAULT_DB_PATH: &'static str = &"/var/lib/autotasker/autotasker.db";
const DEFAULT_ASSETS_PATH: &'static str = &"/usr/local/lib/autotasker/assets";
const DEFAULT_CONFIG_PATH: &'static str = &"/etc/autotasker/config.yaml";
#[tokio::main]
async fn main() -> Result<()> {
let flags: CliFlags = argh::from_env();
env_logger::init();
info!("Starting autotasker");
let pool = prepare_database().await.context("Prepare db")?;
let pool = prepare_database(
&flags.database.unwrap_or(DEFAULT_DB_PATH.to_string())
).await.context("Could not prepare db")?;
// start channel to talk to executor daemon
let (executor_tx, executor_rx) = mpsc::channel::<ExecutorOrder>(32);
@ -85,7 +108,7 @@ async fn main() -> Result<()> {
let config_path = match flags.config {
Some(v) => v,
None => "/etc/autotasker/config.yaml".to_string()
None => DEFAULT_CONFIG_PATH.to_string()
};
info!("Using config file at {}", &config_path);
let config: Config = get_config(config_path).expect("Cannot get config");
@ -125,10 +148,10 @@ async fn main() -> Result<()> {
get(controllers::get_task_run),
)
.route("/webhooks/:id/:token", get(controllers::handle_webhook))
.nest_service("/assets", ServeDir::new("./assets"))
.nest_service("/assets", ServeDir::new(flags.static_assets.unwrap_or(DEFAULT_ASSETS_PATH.to_string())))
.with_state(state);
let listen_addr = "0.0.0.0:8085";
let listen_addr = format!("{}:{}", flags.listen_host, flags.listen_port);
info!("Starting web server on http://{}", &listen_addr);
let listener = tokio::net::TcpListener::bind(listen_addr).await.unwrap();
axum::serve(listener, app).await?;
@ -138,13 +161,13 @@ async fn main() -> Result<()> {
Ok(())
}
async fn prepare_database() -> Result<Pool<Sqlite>> {
let conn_str = "sqlite:./tmp/dbs/autotasker.db";
async fn prepare_database(sqlite_db_path: &str) -> Result<Pool<Sqlite>> {
let conn_str = format!("sqlite:{}", sqlite_db_path);
let pool = SqlitePoolOptions::new()
.max_connections(50)
.connect_with(
SqliteConnectOptions::from_str(conn_str)?
SqliteConnectOptions::from_str(&conn_str)?
.log_statements(log::LevelFilter::Trace)
)
.await