[Draft] Implement Early deletion by Submitter #37 #38

Merged
ben merged 15 commits from ben/Jobboerse:main into main 2023-01-28 18:00:00 +01:00
76 changed files with 177 additions and 116 deletions
Showing only changes of commit 11fda40e3b - Show all commits

move root package to packages/jobboerse

for more consistency and also fix docker builds

more descriptive startup errors
Bennet Bleßmann 2022-10-08 03:18:40 +02:00 committed by Bennet Bleßmann
Signed by: ben
GPG key ID: 3BE1A1A3CBC3CF99

View file

@ -42,65 +42,5 @@ toml = "0.5.9"
tokio = "1.21.2"
url = { version = "2.3.1", features = ["serde"] }
[package]
name = "jobboerse"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
repository.workspace = true
homepage.workspace = true
license.workspace = true
readme.workspace = true
categories = ["command-line-utilities", "web-programming::http-server"] # https://crates.io/category_slugs max. 5
keywords = ["jobbörse", "webserver", "application"] # max 5. matching [a-zA-Z][a-zA-Z0-9-_]{,19}
description = "This package provides a webserver for joboffser postings"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
dev_mode = []
[dependencies]
actix-files = { workspace = true }
actix-web = { workspace = true }
actix-session = { workspace = true, features = ["cookie-session"] }
actix-multipart = { workspace = true }
better_toml_datetime = { workspace = true }
cargo-bundle-licenses = { workspace = true, default-features = false }
chrono = { workspace = true, default-features = false, features = ["std","clock"] }
chrono-tz = { workspace = true }
clap = { workspace = true, features = ["derive", "env"] }
futures-util = { workspace = true }
handlebars = { workspace = true, features = ["dir_source"] }
http = { workspace = true }
lettre = { workspace = true, default-features = false, features = ["sendmail-transport", "tokio1", "builder", "serde"] }
# use rustls a native tls library rather than openssl,
# as depending on c dependencies can get annoying even when vendoring
ldap3 = { workspace = true, default-features = false, features = ["tls-rustls"] }
listenfd = { workspace = true }
log = { workspace = true }
mime_guess = { workspace = true }
multipart_helper = { workspace = true }
pretty_env_logger = { workspace = true }
rand = { workspace = true }
serde = { workspace = true, features = ["derive"] } # https://doc.rust-lang.org/cargo/reference/manifest.html
serde_json = { workspace = true }
tempfile = { workspace = true }
thiserror = { workspace = true }
toml = { workspace = true }
tokio = { workspace = true }
url = { workspace = true, features = ["serde"] }
[build-dependencies]
cargo-bundle-licenses = { workspace = true, default-features = false }
pretty_env_logger = { workspace = true }
log = { workspace = true }
# manually specify autobin here as otherwise some command freak out if we don't copy the src/ folder
[[bin]]
name = "jobboerse"
path = "src/main.rs"
[profile.release]
strip = true

View file

@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- upgrade/update dependencies
- msrv is now 1.64 as we now take advantage of packages inheriting from the workspace
- move root jobboerse package to packages dir
- startup errors are more descriptive
### Removed

View file

@ -1,14 +1,13 @@
FROM alpine AS source
WORKDIR /source
COPY build.rs Cargo.lock Cargo.toml deny.toml THIRDPARTY.toml ./
COPY src ./src
COPY static ./static
COPY templates ./templates
COPY Cargo.lock Cargo.toml deny.toml ./
COPY packages ./packages
FROM rust:alpine AS rust-with-musl-dev
FROM rust:1.64-alpine AS rust-with-musl-dev
RUN apk add --no-cache musl-dev
FROM rust-with-musl-dev AS check-deny
WORKDIR /deny
@ -18,7 +17,7 @@ RUN apk add --no-cache perl make
RUN cargo install cargo-deny --locked
COPY Cargo.toml deny.toml ./
COPY packages ./packages
RUN cargo deny check
FROM rust-with-musl-dev AS prepare-build
@ -26,25 +25,15 @@ WORKDIR /build
RUN mkdir /jobboerse
# only copy Cargo.{toml,lock} to reduce dependencies of the fetch step
COPY Cargo.toml Cargo.lock ./
COPY packages ./packages
RUN cargo fetch
COPY build.rs THIRDPARTY.toml ./
COPY src/ ./src/
COPY static/ ./static/
COPY templates/ ./templates/
COPY scripts/ ./scripts/
FROM prepare-build AS build
ARG BUILD_FLAGS=""
RUN sh ./scripts/adjust_thirdparty.sh
RUN diff THIRDPARTY.toml.bak THIRDPARTY.toml || true
RUN cargo build --verbose --release --frozen $BUILD_FLAGS
FROM alpine AS run
@ -54,25 +43,32 @@ ARG GID=1999
EXPOSE 8080/tcp
RUN echo $UID $GID
RUN addgroup -g $GID -S jobboerse-server
RUN adduser -u $UID -G jobboerse-server -D -Hh /jobboerse jobboerse-server
RUN mkdir -p /jobboerse/job_offers
RUN chown -R jobboerse-server:jobboerse-server /jobboerse
VOLUME ["/config"]
VOLUME ["/jobboerse/job_offers"]
RUN addgroup -g $GID -S jobboerse-server
RUN adduser -u $UID -G jobboerse-server -D -Hh /jobboerse jobboerse-server
RUN chown -R jobboerse-server:jobboerse-server /jobboerse
WORKDIR /jobboerse
COPY --from=build /build/target/release/jobboerse ./
RUN chmod a+x /jobboerse/jobboerse
RUN chmod a+r -R /jobboerse /config
COPY --from=build /build/static/ ./static/
COPY --from=build /build/templates/ ./templates/
COPY --from=build /build/packages/jobboerse/static/ ./static/
COPY --from=build /build/packages/jobboerse/templates/ ./templates/
RUN chmod a+r -R /jobboerse /config
USER jobboerse-server
RUN chown -R jobboerse-server:jobboerse-server /jobboerse
ENTRYPOINT ["/jobboerse/jobboerse"]
CMD ["--config", "/config/config.toml", "--mode", "production"]

24
Dockerfile-pkgbuild Normal file
View file

@ -0,0 +1,24 @@
FROM archlinux:base-devel AS source
EXPOSE 8080/tcp
VOLUME [ "/var/lib/jobboerse/job_offers" ]
RUN useradd -mU dev
RUN echo -e "\ndev ALL=(ALL:ALL) NOPASSWD: ALL" >> /etc/sudoers
RUN pacman -Syu --noconfirm
COPY . /src_dir
RUN chown -R dev:dev /src_dir
USER dev
WORKDIR /src_dir/dist/arch/devel
RUN sudo -u dev -- makepkg -siC --noconfirm
WORKDIR /usr/lib/jobboerse/
ENTRYPOINT ["/usr/bin/jobboerse"]
CMD ["--config", "/etc/jobboerse/config.toml", "--mode", "production"]

View file

@ -6,7 +6,7 @@ This project contains a web-server application to serve a listing of job offers
Building the binary
-------------------
As the program is writen in rust, a rust toolchain needs to be installed.
As the program is written in rust, a rust toolchain needs to be installed.
Instructions on how to install rustup, the standard toolchain manager for rust, can be found at <https://www.rust-lang.org/tools/install>.
The minimum required rust-toolchain version as of writing is `1.58` see the `rust-version` entry in the `Cargo.toml` file.
@ -45,7 +45,7 @@ Should the config path not exist, then a default config will be used and written
The port 8080 will be used by default, this can be changed with the `--port` flag.
The default log level is `INFO`, logging can be configured via the `RUST_LOG` environment variable as described
in the [`env_logger` documnentation](https://docs.rs/env_logger/0.7.1/env_logger/index.html).
in the [`env_logger` documentation](https://docs.rs/env_logger/0.7.1/env_logger/index.html).
Note: Currently the documentation for version 0.7.1 is relevant, even if it not the newest version.
An update of `pretty_env_logger` should hopefully be available soon to change this.
See [PR 49](https://github.com/seanmonstar/pretty-env-logger/pull/49) in the pretty_env_logger repo, which updates the env_logger dependency.
@ -76,7 +76,7 @@ The `Simple` login provider requires the `file_path` to be set, which points to
a `users` table.
The table keys are then used as usernames and the associated values are expected to be strings containing plaintext passwords.
The `Ldap` login provider takes the follwoing configuration fields.
The `Ldap` login provider takes the following configuration fields.
The `server_address` field specifies a URL under which the ldap server is reached.
The `starttls` options defines whether StartTLS should be used (default is true, though ignored if incompatible with URL).
The `ldap_user_dn` contains a pattern for the user dn for simple bind as well as search `%{username}` is replaced by the ldap-dn-escaped username.
@ -96,7 +96,7 @@ To regenerate this file the cargo-bundle-licenses tool needs to be available.
It can be installed via `cargo install cargo-bundle-licenses`.
At least version 1.0.0, is required, for normalization of license file path located under `$CARGO_HOME`.
It can then be run with `cargo bundle-licenses --format toml --output THIRDPARTY.toml` to re-generate the file.
The script `./scripts/generate_thirparty.sh` does just that.
The script `./scripts/generate_thirdparty.sh` does just that.
This needs to be done when dependencies change to adjust the corresponding entries.
Make sure to look for entries for which the license text is listed as `NOT FOUND` and insert the appropriate license.
@ -117,7 +117,7 @@ Cutting a Release
* Update the version in the root Cargo.toml according to semver, this will be the version to-be-cut
* Update the changelog to reflect all changes since the last release unter `[Unreleased]`
* It's generally recommended to keep the Changelog upto date by adding changes to the unreleased section in the commit that introduces the change
* In the now up-to-date changelog add a new section heading for the version to-be-cut between `[Unreleasd]` and the first entry of the unreleased section
* In the now up-to-date changelog add a new section heading for the version to-be-cut between `[Unreleased]` and the first entry of the unreleased section
* Add a matching link definition at the bottom of a changelog
* Update the version in dist/arch/PKGBUILD to match the version to-be-cut
* run cargo test to update the version in the Cargo.lock file and check that the tests pass

4
dist/arch/PKGBUILD vendored
View file

@ -75,14 +75,14 @@ package()
install -dm0755 "${pkgdir}/usr/lib/${_pkgname}/"
cp -r "static/" "templates/" "${pkgdir}/usr/lib/${_pkgname}/"
cp -r "packages/jobboerse/static/" "packages/jobboerse/templates/" "${pkgdir}/usr/lib/${_pkgname}/"
install -Dm0644 -t "${pkgdir}/usr/share/doc/${_pkgname}" \
README.md \
Changelog.md
install -Dm0644 \
"config/dist-config.toml" \
"packages/jobboerse/config/dist-config.toml" \
"${pkgdir}/etc/${_pkgname}/config.toml"
install -Dm0644 \

View file

@ -86,14 +86,14 @@ package()
install -dm0755 "${pkgdir}/usr/lib/${_pkgname}/"
cp -r "static/" "templates/" "${pkgdir}/usr/lib/${_pkgname}/"
cp -r "packages/jobboerse/static/" "packages/jobboerse/templates/" "${pkgdir}/usr/lib/${_pkgname}/"
install -Dm0644 -t "${pkgdir}/usr/share/doc/${_pkgname}" \
README.md \
Changelog.md
install -Dm0644 \
"config/dist-config.toml" \
"packages/jobboerse/config/dist-config.toml" \
"${pkgdir}/etc/${_pkgname}/config.toml"
install -Dm0644 \

View file

@ -0,0 +1,60 @@
[package]
name = "jobboerse"
categories = ["command-line-utilities", "web-programming::http-server"] # https://crates.io/category_slugs max. 5
keywords = ["jobbörse", "webserver", "application"] # max 5. matching [a-zA-Z][a-zA-Z0-9-_]{,19}
description = "This package provides a webserver for joboffser postings"
include = ["THIRDPARTY.toml", "../APACHE-2.0.LICENSE", "../MIT.LICENSE"]
version.workspace = true
edition.workspace = true
rust-version.workspace = true
repository.workspace = true
homepage.workspace = true
license.workspace = true
readme.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
dev_mode = []
[dependencies]
actix-files = { workspace = true }
actix-web = { workspace = true }
actix-session = { workspace = true, features = ["cookie-session"] }
actix-multipart = { workspace = true }
better_toml_datetime = { workspace = true }
cargo-bundle-licenses = { workspace = true, default-features = false }
chrono = { workspace = true, default-features = false, features = ["std","clock"] }
chrono-tz = { workspace = true }
clap = { workspace = true, features = ["derive", "env"] }
futures-util = { workspace = true }
handlebars = { workspace = true, features = ["dir_source"] }
http = { workspace = true }
lettre = { workspace = true, default-features = false, features = ["sendmail-transport", "tokio1", "builder", "serde"] }
# use rustls a native tls library rather than openssl,
# as depending on c dependencies can get annoying even when vendoring
ldap3 = { workspace = true, default-features = false, features = ["tls-rustls"] }
listenfd = { workspace = true }
log = { workspace = true }
mime_guess = { workspace = true }
multipart_helper = { workspace = true }
pretty_env_logger = { workspace = true }
rand = { workspace = true }
serde = { workspace = true, features = ["derive"] } # https://doc.rust-lang.org/cargo/reference/manifest.html
serde_json = { workspace = true }
tempfile = { workspace = true }
thiserror = { workspace = true }
toml = { workspace = true }
tokio = { workspace = true }
url = { workspace = true, features = ["serde"] }
[build-dependencies]
cargo-bundle-licenses = { workspace = true, default-features = false }
pretty_env_logger = { workspace = true }
log = { workspace = true }
# manually specify autobin here as otherwise some command freak out if we don't copy the src/ folder
[[bin]]
name = "jobboerse"
path = "src/main.rs"

View file

@ -61,13 +61,25 @@ impl ResponseError for PresentationError {
}
#[derive(Error, Debug)]
#[error("the jobboerse server encountered a fatal error during startup: {0}")]
pub(crate) enum SeverInitializationError {
#[error("the jobboerse server encountered a fatal config error during startup: {0}")]
ConfigError(#[from] ConfigError),
#[error("the jobboerse server encountered a fatal license bundle error during startup: {0}")]
LicenseBundleError(#[from] bundle_licenses_lib::format::FormatError),
#[error(
"the jobboerse server encountered a fatal license joboffer load error during startup: {0}"
)]
JobOfferload(#[from] JobofferLoadError),
#[error("the jobboerse server encountered a fatal template error during startup: {0}")]
TemplateError(#[from] handlebars::TemplateError),
IO(#[from] std::io::Error),
#[error("the jobboerse server encountered a fatal io error during startup, could not bind socket: {0}")]
Bind(std::io::Error),
#[error("the jobboerse server encountered a fatal io error during startup, could not listen on socket: {0}")]
TakeListen(std::io::Error),
#[error("the jobboerse server encountered a fatal io error during startup, could not take listen socket: {0}")]
Listen(std::io::Error),
#[error("the jobboerse server encountered a fatal io error during startup, could not start the server: {0}")]
Run(std::io::Error),
}
pub(crate) fn default_error_response(

View file

@ -507,8 +507,8 @@ type JobOfferId = str;
#[derive(Debug, thiserror::Error)]
pub(crate) enum JobofferLoadError {
#[error("{0}")]
IO(#[from] std::io::Error),
#[error("Failed to load Job Offer file at {1}: {0}")]
IO(std::io::Error, PathBuf),
#[error("Failed to deserialize Job Offer {id}: {err}")]
TomlDeserializationError { id: String, err: toml::de::Error },
}
@ -536,12 +536,19 @@ impl JobOffers {
let storage = &config.config.data_storage_path;
info!("Loading Job Offers from {}", storage.display());
let mut dir = tokio::fs::read_dir(storage).await?;
let mut dir = tokio::fs::read_dir(storage)
.await
.map_err(|err| JobofferLoadError::IO(err, storage.clone()))?;
while let Some(entry) = dir.next_entry().await.transpose() {
let entry = entry?;
if entry.file_type().await?.is_dir() {
let entry = entry.map_err(|err| JobofferLoadError::IO(err, storage.clone()))?;
let path = entry.path();
if entry
.file_type()
.await
.map_err(|err| JobofferLoadError::IO(err, path.clone()))?
.is_dir()
{
let index = path.join("index.toml");
if index.is_file() {
if let Some(key) = path
@ -550,7 +557,9 @@ impl JobOffers {
.to_str()
.map(str::to_string)
{
let file_content = tokio::fs::read_to_string(index).await?;
let file_content = tokio::fs::read_to_string(&index)
.await
.map_err(|err| JobofferLoadError::IO(err, index))?;
let job = toml::de::from_str(&file_content).map_err(|err| {
JobofferLoadError::TomlDeserializationError {
id: key.clone(),

View file

@ -14,6 +14,7 @@ use actix_web::cookie::Key;
use actix_web::http::StatusCode;
use actix_web::middleware::{ErrorHandlers, NormalizePath, TrailingSlash};
use actix_web::{get, web, App, HttpServer};
use error::SeverInitializationError;
use handlebars::Handlebars;
use listenfd::ListenFd;
use log::{error, LevelFilter, SetLoggerError};
@ -150,15 +151,21 @@ async fn run() -> Result<(), error::SeverInitializationError> {
app
});
if let Some(l) = listen_fd.take_tcp_listener(0)? {
server.listen(l)?
if let Some(l) = listen_fd
.take_tcp_listener(0)
.map_err(SeverInitializationError::TakeListen)?
{
server.listen(l).map_err(SeverInitializationError::Listen)?
} else {
let ipv6 = SocketAddr::from((Ipv6Addr::UNSPECIFIED, port));
let ipv4 = SocketAddr::from((Ipv4Addr::UNSPECIFIED, port));
server.bind([ipv6, ipv4].as_slice())?
server
.bind([ipv6, ipv4].as_slice())
.map_err(SeverInitializationError::Bind)?
}
.run()
.await?;
.await
.map_err(SeverInitializationError::Run)?;
Ok(())
}

View file

@ -4,5 +4,7 @@ SCRIPT_DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
set -e
echo "Building Docker Image"
source ${SCRIPT_DIR}/build_dev_image.sh
echo "Starting Docker Container"
source ${SCRIPT_DIR}/run_dev_container.sh

View file

@ -3,4 +3,4 @@
#RUST_BACKTRACE=1
systemfd --no-pid -s http::8080 -- \
cargo watch -i ./config/dev-config.toml -x 'run --package jobboerse --bin jobboerse --release --features=dev_mode -- --config=./config/dev-config.toml --mode=development'
cargo watch -i ./packages/jobboerse/config/dev-config.toml -x 'run --package jobboerse --bin jobboerse --release --features=dev_mode -- --config=./packages/jobboerse/config/dev-config.toml --mode=development'

4
scripts/generate_thirdparty.sh Executable file
View file

@ -0,0 +1,4 @@
#!/bin/bash
cargo bundle-licenses --format toml --output packages/jobboerse/THIRDPARTY.toml

View file

@ -1,5 +0,0 @@
#!/bin/bash
cargo bundle-licenses --format toml --output THIRDPARTY.toml

View file

@ -1,3 +1,3 @@
#!/bin/bash
cargo run --package jobboerse --bin jobboerse --release --config=./config/prod-config.toml
cargo run --package jobboerse --bin jobboerse --release --config=./packages/jobboerse/config/prod-config.toml

View file

@ -1,8 +1,10 @@
docker run \
# root is necessary for the volums to be mounted correctly
# otherwise they will be owned by root instead of the correct user
sudo docker run \
--rm \
-p 8080:8080 \
-v "$(pwd)"/job_offers:/jobboerse/job_offers \
-v "$(pwd)"/config:/config \
-v "$(pwd)"/packages/jobboerse/config:/config \
jobboerse:dev \
--config=/config/dev-docker-config.toml \
--mode=development

8
scripts/test-pkgbuild.sh Executable file
View file

@ -0,0 +1,8 @@
#!/bin/bash
docker build -t jobboerse:pkgbuild -f ./Dockerfile-pkgbuild .
docker run --rm \
-p 8080:8080 \
-v "$(pwd)"/job_offers:/var/lib/jobboerse/job_offers \
-v "$(pwd)"/packages/jobboerse/config:/config \
jobboerse:pkgbuild