summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2020-09-27 22:50:31 +0200
committerMatthias Beyer <matthias.beyer@atos.net>2020-10-12 15:14:17 +0200
commitb542789c87e2acdc1de3d52bd0670ace325a87d3 (patch)
tree13e3817c59f1db797b71f8c13c07a4a826c6b651
Initial import
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
-rw-r--r--.gitignore2
-rw-r--r--Cargo.toml24
-rw-r--r--README.md3
-rw-r--r--config.toml35
-rw-r--r--shell.nix33
-rw-r--r--src/config/mod.rs81
-rw-r--r--src/main.rs51
-rw-r--r--src/package/mod.rs5
-rw-r--r--src/package/package.rs95
-rw-r--r--src/package/util.rs25
-rw-r--r--src/phase/mod.rs14
-rw-r--r--src/util/mod.rs77
12 files changed, 445 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..96ef6c0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/target
+Cargo.lock
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..2a5c2b7
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "yabos"
+version = "0.1.0"
+authors = ["Matthias Beyer <mail@beyermatthias.de>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+log = "0.3"
+tracing = "0.1"
+toml = "0.5"
+serde = "1"
+config = "0.9"
+anyhow = "1"
+getset = "0.1"
+
+url = { version = "2", features = ["serde"] }
+tokio = { version = "0.2", features = ["full"] }
+shiplift = { git = "https://github.com/softprops/shiplift", branch = "master" }
+
+[dev-dependencies]
+env_logger = "0.7"
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..80a2760
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# YABOS
+
+Yet Another Build Orchestration System
diff --git a/config.toml b/config.toml
new file mode 100644
index 0000000..456b4ac
--- /dev/null
+++ b/config.toml
@@ -0,0 +1,35 @@
+# repository of package definitions
+repository = "/tmp/path"
+
+# Phases which can be configured in the packages
+
+# This also defines the _order_ in which the phases are executed
+# Each phase gets a pre_ and a post_ phase added automatically.
+# So for [ "foo", "bar" ], the phases are executed in this order:
+# pre_foo
+# foo
+# post_foo
+# pre_bar
+# bar
+# post_bar
+#
+# Phases which are not listed here are not executed at all.
+available_phases = [ "unpack", "patch", "configure", "build", "fixup", "pack" ]
+
+[docker]
+# Images which can be used to build
+# images not listed here are automatically rejected
+images = [ "debian:bullseye" ]
+
+# List of docker endpoints
+[[docker.endpoints]]
+name = "testhostname"
+uri = "http://0.0.0.0:8095"
+endpoint_type = "http"
+
+
+[containers]
+# environment variables which are allowed during container start
+# This way, errors (typos) when passing environment to a build can be prevented
+allowed_env = [ "PATH" ]
+
diff --git a/shell.nix b/shell.nix
new file mode 100644
index 0000000..341c15b
--- /dev/null
+++ b/shell.nix
@@ -0,0 +1,33 @@
+{ ... }:
+
+let
+ moz_overlay = import (
+ builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz
+ );
+
+ pkgs = import <nixpkgs> { overlays = [ moz_overlay ]; };
+in
+pkgs.mkShell {
+ buildInputs = with pkgs; [
+ rustChannels.stable.rust-std
+ rustChannels.stable.rust
+ rustChannels.stable.rustc
+ rustChannels.stable.cargo
+
+ cmake
+ curl
+ gcc
+ openssl
+ pkgconfig
+ which
+ zlib
+ ];
+
+ shellHook = ''
+ alias docker='docker --host=tcp://localhost:8095'
+ '';
+
+ LIBCLANG_PATH = "${pkgs.llvmPackages.libclang}/lib";
+}
+
+
diff --git a/src/config/mod.rs b/src/config/mod.rs
new file mode 100644
index 0000000..1c29817
--- /dev/null
+++ b/src/config/mod.rs
@@ -0,0 +1,81 @@
+use std::path::PathBuf;
+use std::fmt::Debug;
+use std::ops::Deref;
+
+use anyhow::Result;
+use getset::Getters;
+use serde::Deserialize;
+
+use crate::phase::PhaseName;
+use crate::util::EnvironmentVariableName;
+use crate::util::docker::ImageName;
+
+#[derive(Debug, Getters, Deserialize)]
+pub struct NotValidatedConfiguration {
+ #[getset(get = "pub")]
+ repository: PathBuf,
+
+ #[getset(get = "pub")]
+ docker: DockerConfig,
+
+ #[getset(get = "pub")]
+ containers: ContainerConfig,
+
+ #[getset(get = "pub")]
+ available_phases: Vec<PhaseName>,
+}
+
+impl NotValidatedConfiguration {
+ pub fn validate(self) -> Result<Configuration> {
+ unimplemented!()
+ }
+}
+
+#[derive(Debug)]
+pub struct Configuration(NotValidatedConfiguration);
+
+impl Deref for Configuration {
+ type Target = NotValidatedConfiguration;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+
+#[derive(Debug, Getters, Deserialize)]
+pub struct DockerConfig {
+
+ #[getset(get = "pub")]
+ images: Vec<ImageName>,
+
+ #[getset(get = "pub")]
+ endpoints: Vec<Endpoint>,
+}
+
+#[derive(Debug, Getters, Deserialize)]
+pub struct ContainerConfig {
+ #[getset(get = "pub")]
+ allowed_env: Vec<EnvironmentVariableName>,
+}
+
+
+#[derive(Debug, Getters, Deserialize)]
+pub struct Endpoint {
+ #[getset(get = "pub")]
+ name: String,
+
+ #[getset(get = "pub")]
+ uri: String,
+
+ #[getset(get = "pub")]
+ endpoint_type: EndpointType,
+}
+
+#[derive(Debug, Deserialize, Eq, PartialEq)]
+pub enum EndpointType {
+ Socket,
+ Http,
+}
+
+
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..b9abb1d
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,51 @@
+#[macro_use] extern crate log;
+
+use std::str::FromStr;
+use anyhow::Result;
+use anyhow::Error;
+
+mod util;
+mod package;
+mod phase;
+mod config;
+use crate::config::DockerConfig;
+use crate::config::Endpoint;
+use crate::config::EndpointType;
+
+#[tokio::main]
+async fn main() -> Result<()> {
+ let mut config = ::config::Config::default();
+ config
+ .merge(::config::File::with_name("config"))?
+ .merge(::config::Environment::with_prefix("YABOS"))?;
+ // Add in settings from the environment (with a prefix of YABOS)
+ // Eg.. `YABOS_DEBUG=1 ./target/app` would set the `debug` key
+ //
+
+ let docker_config = config.get::<DockerConfig>("docker")?;
+
+ let iter = docker_config
+ .endpoints()
+ .iter()
+ .map(|ep| {
+ match ep.endpoint_type() {
+ EndpointType::Http => {
+ shiplift::Uri::from_str(ep.uri())
+ .map(|uri| shiplift::Docker::host(uri))
+ .map_err(Error::from)
+ }
+
+ EndpointType::Socket => {
+ Ok(shiplift::Docker::unix(ep.uri()))
+ }
+ }
+ });
+
+ for d in iter {
+ let v = d?.version().await?;
+ println!("Docker: {}", v.version);
+ println!("API : {}", v.api_version);
+ }
+
+ Ok(())
+}
diff --git a/src/package/mod.rs b/src/package/mod.rs
new file mode 100644
index 0000000..783bf21
--- /dev/null
+++ b/src/package/mod.rs
@@ -0,0 +1,5 @@
+mod package;
+pub use package::*;
+
+mod util;
+pub use util::*;
diff --git a/src/package/package.rs b/src/package/package.rs
new file mode 100644
index 0000000..ff5cdd9
--- /dev/null
+++ b/src/package/package.rs
@@ -0,0 +1,95 @@
+use std::path::PathBuf;
+use std::collections::HashMap;
+
+use url::Url;
+use getset::Getters;
+use serde::Deserialize;
+
+use crate::phase::{PhaseName, Phase};
+use crate::package::util::*;
+use crate::util::docker::ImageName;
+
+#[derive(Debug, Deserialize, Getters)]
+pub struct Package {
+ #[getset(get = "pub")]
+ name: PackageName,
+
+ #[getset(get = "pub")]
+ version: PackageVersion,
+
+ #[getset(get = "pub")]
+ version_is_semver: bool,
+
+ #[getset(get = "pub")]
+ source_url: Url,
+
+ #[getset(get = "pub")]
+ source_hash: SourceHash,
+
+ #[getset(get = "pub")]
+ system_dependencies: Vec<SystemDependency>,
+
+ #[getset(get = "pub")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ system_dependencies_script: Option<PathBuf>,
+
+ #[getset(get = "pub")]
+ build_dependencies: Vec<BuildDependency>,
+
+ #[getset(get = "pub")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ build_dependencies_script: Option<PathBuf>,
+
+ #[getset(get = "pub")]
+ dependencies: Vec<Dependency>,
+
+ #[getset(get = "pub")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ dependencies_script: Option<PathBuf>,
+
+ #[getset(get = "pub")]
+ patches: Vec<PathBuf>,
+
+ #[getset(get = "pub")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ environment: Option<HashMap<String, String>>,
+
+ #[getset(get = "pub")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ flags: Option<PackageFlags>,
+
+ #[getset(get = "pub")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ deny_on_images: Option<Vec<ImageName>>,
+
+ #[getset(get = "pub")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ phases: Option<HashMap<PhaseName, Phase>>,
+}
+
+#[derive(Debug, Deserialize)]
+pub struct SourceHash {
+ #[serde(rename = "type")]
+ hashtype: HashType,
+
+ #[serde(rename = "hash")]
+ value: HashValue,
+}
+
+#[derive(Debug, Deserialize)]
+pub enum HashType {
+ #[serde(rename = "sha1")]
+ Sha1,
+
+ #[serde(rename = "sha256")]
+ Sha256,
+
+ #[serde(rename = "sha512")]
+ Sha512,
+}
+
+#[derive(Debug, Deserialize)]
+pub struct PackageFlags {
+ build_parallel: bool,
+}
+
diff --git a/src/package/util.rs b/src/package/util.rs
new file mode 100644
index 0000000..08f55fe
--- /dev/null
+++ b/src/package/util.rs
@@ -0,0 +1,25 @@
+//! Utility types for the package definitions
+//!
+//! These types exist only for the purpose of strong typing
+//! and cannot do anything special.
+
+use serde::Deserialize;
+
+#[derive(Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
+pub struct PackageName(String);
+
+#[derive(Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
+pub struct PackageVersion(String);
+
+#[derive(Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
+pub struct SystemDependency(String);
+
+#[derive(Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
+pub struct BuildDependency(String);
+
+#[derive(Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
+pub struct Dependency(String);
+
+#[derive(Deserialize, Debug, Hash, Eq, PartialEq)]
+pub struct HashValue(String);
+
diff --git a/src/phase/mod.rs b/src/phase/mod.rs
new file mode 100644
index 0000000..44581bb
--- /dev/null
+++ b/src/phase/mod.rs
@@ -0,0 +1,14 @@
+use std::path::PathBuf;
+use std::collections::HashMap;
+
+use serde::Deserialize;
+
+#[derive(Debug, Deserialize, Eq, PartialEq, Hash)]
+pub struct PhaseName(String);
+
+#[derive(Debug, Deserialize, Eq, PartialEq)]
+pub enum Phase {
+ Path(PathBuf),
+ Text(String),
+}
+
diff --git a/src/util/mod.rs b/src/util/mod.rs
new file mode 100644
index 0000000..84ea114
--- /dev/null
+++ b/src/util/mod.rs
@@ -0,0 +1,77 @@
+use serde::Deserialize;
+
+#[derive(Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
+pub struct EnvironmentVariableName(String);
+
+pub mod docker {
+ use anyhow::Result;
+ use anyhow::anyhow;
+ use serde::Deserialize;
+
+ #[derive(Deserialize, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
+ pub struct ImageName(String);
+
+ /// Check whether a string is a valid docker tag name
+ ///
+ /// From the docker spec:
+ ///
+ /// > A tag name must be valid ASCII and may contain lowercase and uppercase letters, digits,
+ /// > underscores, periods and dashes. A tag name may not start with a period or a dash and may
+ /// > contain a maximum of 128 characters.
+ ///
+ /// Returns Ok(()) if `s` is a valid docker tag name, otherwise an explanatory error message
+ pub fn is_valid_tag_name(s: &str) -> Result<()> {
+ let valid_chars = s.chars().all(|c| {
+ c == '_' ||
+ c == ':' ||
+ c == '-' ||
+ c.is_ascii_alphanumeric()
+ });
+
+ if !valid_chars {
+ return Err(anyhow!("Invalid characters"))
+ }
+
+ if s.chars().count() > 128 {
+ return Err(anyhow!("Too long"))
+ }
+
+
+ if s.chars().next().map(|c| c == '.' || c == '-').unwrap_or(false) {
+ return Err(anyhow!("Starts with invalid character"))
+ }
+
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod docker_test {
+ extern crate env_logger;
+ fn setup_logging() {
+ let _ = env_logger::try_init();
+ }
+
+ use super::docker::*;
+
+ #[test]
+ fn is_valid_tag_name_test_1() {
+ setup_logging();
+ let test = |s| {
+ debug!("check if valid: '{}'", s);
+ let e = is_valid_tag_name(s);
+ debug!("Result = {:?}", e);
+ e
+ };
+
+ assert!(test("foo").is_ok());
+ assert!(test("foo:bar").is_ok());
+ assert!(test("foo123").is_ok());
+ assert!(test("1f23oo").is_ok());
+ assert!(test(":foo").is_ok());
+ assert!(test(".foo").is_err());
+ assert!(test("-foo").is_err());
+ }
+
+}
+