Compare commits
26 commits
4dbdbee457
...
f2cfa79c61
Author | SHA1 | Date | |
---|---|---|---|
Nikodem Rabuliński | f2cfa79c61 | ||
6139576a3c | |||
4168282329 | |||
ee8f374737 | |||
71396b9518 | |||
759dbc9f7e | |||
717cc95983 | |||
14cb5f9e46 | |||
9a6b2cbf1d | |||
bc22e00a3b | |||
cb1b80e989 | |||
2a6b9c592b | |||
4ffeb50f50 | |||
a1a521c32f | |||
ff3ce2c0b8 | |||
6603ee14ed | |||
ec0469cad0 | |||
0558269391 | |||
3907b31157 | |||
a4f2cae9dd | |||
18dedcc30b | |||
f18f581188 | |||
a2a2011b5f | |||
d3ffcf885c | |||
2705d1d90b | |||
47f17e0900 |
|
@ -13,7 +13,7 @@ charset = utf-8
|
||||||
# Rust
|
# Rust
|
||||||
[*.rs]
|
[*.rs]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 4
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
[*.{yaml,yml,md,nix}]
|
[*.{yaml,yml,md,nix}]
|
||||||
|
|
1
.github/install-attic-ci.sh
vendored
1
.github/install-attic-ci.sh
vendored
|
@ -25,7 +25,6 @@ let
|
||||||
value = common // {
|
value = common // {
|
||||||
inherit outputName;
|
inherit outputName;
|
||||||
outPath = maybeStorePath (builtins.getAttr outputName outputs);
|
outPath = maybeStorePath (builtins.getAttr outputName outputs);
|
||||||
drvPath = maybeStorePath (builtins.getAttr outputName outputs);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
outputsList = map outputToAttrListElement outputNames;
|
outputsList = map outputToAttrListElement outputNames;
|
||||||
|
|
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
@ -11,7 +11,7 @@ jobs:
|
||||||
matrix:
|
matrix:
|
||||||
os:
|
os:
|
||||||
- ubuntu-latest
|
- ubuntu-latest
|
||||||
- macos-11
|
- macos-latest
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
2013
Cargo.lock
generated
2013
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -6,7 +6,7 @@ publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-stream = { version = "0.3.5", optional = true }
|
async-stream = { version = "0.3.5", optional = true }
|
||||||
base64 = "0.21.2"
|
base64 = "0.22.1"
|
||||||
bytes = "1.4.0"
|
bytes = "1.4.0"
|
||||||
displaydoc = "0.2.4"
|
displaydoc = "0.2.4"
|
||||||
digest = "0.10.7"
|
digest = "0.10.7"
|
||||||
|
@ -53,7 +53,7 @@ default = [ "nix_store", "tokio" ]
|
||||||
# Native libnixstore bindings.
|
# Native libnixstore bindings.
|
||||||
#
|
#
|
||||||
# When disabled, the native Rust portions of nix_store can still be used.
|
# When disabled, the native Rust portions of nix_store can still be used.
|
||||||
nix_store = [ "dep:cxx", "dep:cxx-build" ]
|
nix_store = [ "dep:cxx", "dep:cxx-build", "tokio/rt" ]
|
||||||
|
|
||||||
# Tokio.
|
# Tokio.
|
||||||
#
|
#
|
||||||
|
|
|
@ -9,36 +9,28 @@ fn main() {
|
||||||
|
|
||||||
#[cfg(feature = "nix_store")]
|
#[cfg(feature = "nix_store")]
|
||||||
fn build_bridge() {
|
fn build_bridge() {
|
||||||
// Temporary workaround for issue in <https://github.com/NixOS/nix/pull/8484>
|
let libstore = pkg_config::Config::new()
|
||||||
let hacky_include = {
|
.probe("lix-store")
|
||||||
let dir = tempfile::tempdir().expect("Failed to create temporary directory for workaround");
|
.unwrap();
|
||||||
std::fs::write(dir.path().join("uds-remote-store.md"), "\"\"").unwrap();
|
|
||||||
dir
|
let libmain = pkg_config::Config::new()
|
||||||
};
|
.probe("lix-main")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let libutil = pkg_config::Config::new()
|
||||||
|
.probe("lix-util")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
cxx_build::bridge("src/nix_store/bindings/mod.rs")
|
cxx_build::bridge("src/nix_store/bindings/mod.rs")
|
||||||
.file("src/nix_store/bindings/nix.cpp")
|
.file("src/nix_store/bindings/nix.cpp")
|
||||||
.flag("-std=c++2a")
|
.flag("-std=c++2a")
|
||||||
.flag("-O2")
|
.flag("-O2")
|
||||||
.flag("-include")
|
.flag("-include")
|
||||||
.flag("nix/config.h")
|
.flag("lix/config.h")
|
||||||
.flag("-idirafter")
|
.includes(&libmain.include_paths)
|
||||||
.flag(hacky_include.path().to_str().unwrap())
|
.includes(&libutil.include_paths)
|
||||||
// In Nix 2.19+, nix/args/root.hh depends on being able to #include "root.hh" (which is in its parent directory), for some reason
|
.includes(&libstore.include_paths)
|
||||||
.flag("-I")
|
|
||||||
.flag(concat!(env!("NIX_INCLUDE_PATH"), "/nix"))
|
|
||||||
.compile("nixbinding");
|
.compile("nixbinding");
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=src/nix_store/bindings");
|
println!("cargo:rerun-if-changed=src/nix_store/bindings");
|
||||||
|
|
||||||
// the -l flags must be after -lnixbinding
|
|
||||||
pkg_config::Config::new()
|
|
||||||
.atleast_version("2.4")
|
|
||||||
.probe("nix-store")
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
pkg_config::Config::new()
|
|
||||||
.atleast_version("2.4")
|
|
||||||
.probe("nix-main")
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,7 +140,7 @@ void CNixStore::nar_from_path(RVec<unsigned char> base_name, RBox<AsyncWriteSend
|
||||||
nix::StorePath store_path(sv);
|
nix::StorePath store_path(sv);
|
||||||
|
|
||||||
// exceptions will be thrown into Rust
|
// exceptions will be thrown into Rust
|
||||||
this->store->narFromPath(store_path, sink);
|
sink << this->store->narFromPath(store_path);
|
||||||
sink.eof();
|
sink.eof();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,14 +13,14 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <nix/store-api.hh>
|
#include <libstore/store-api.hh>
|
||||||
#include <nix/local-store.hh>
|
#include <libstore/local-store.hh>
|
||||||
#include <nix/remote-store.hh>
|
#include <libstore/remote-store.hh>
|
||||||
#include <nix/uds-remote-store.hh>
|
#include <libstore/uds-remote-store.hh>
|
||||||
#include <nix/hash.hh>
|
#include <libutil/hash.hh>
|
||||||
#include <nix/path.hh>
|
#include <libstore/path.hh>
|
||||||
#include <nix/serialise.hh>
|
#include <libutil/serialise.hh>
|
||||||
#include <nix/shared.hh>
|
#include <libmain/shared.hh>
|
||||||
#include <rust/cxx.h>
|
#include <rust/cxx.h>
|
||||||
|
|
||||||
template<class T> using RVec = rust::Vec<T>;
|
template<class T> using RVec = rust::Vec<T>;
|
||||||
|
|
|
@ -31,7 +31,6 @@ let
|
||||||
value = common // {
|
value = common // {
|
||||||
inherit outputName;
|
inherit outputName;
|
||||||
outPath = maybeStorePath (builtins.getAttr outputName outputs);
|
outPath = maybeStorePath (builtins.getAttr outputName outputs);
|
||||||
drvPath = maybeStorePath (builtins.getAttr outputName outputs);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
outputsList = map outputToAttrListElement outputNames;
|
outputsList = map outputToAttrListElement outputNames;
|
||||||
|
|
|
@ -12,7 +12,7 @@ path = "src/main.rs"
|
||||||
attic = { path = "../attic" }
|
attic = { path = "../attic" }
|
||||||
|
|
||||||
anyhow = "1.0.71"
|
anyhow = "1.0.71"
|
||||||
async-channel = "1.8.0"
|
async-channel = "2.3.1"
|
||||||
bytes = "1.4.0"
|
bytes = "1.4.0"
|
||||||
clap = { version = "4.3", features = ["derive"] }
|
clap = { version = "4.3", features = ["derive"] }
|
||||||
clap_complete = "4.3.0"
|
clap_complete = "4.3.0"
|
||||||
|
@ -26,7 +26,7 @@ indicatif = "0.17.3"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
notify = { version = "6.0.0", default-features = false, features = ["macos_kqueue"] }
|
notify = { version = "6.0.0", default-features = false, features = ["macos_kqueue"] }
|
||||||
regex = "1.8.3"
|
regex = "1.8.3"
|
||||||
reqwest = { version = "0.11.18", default-features = false, features = ["json", "rustls-tls", "rustls-tls-native-roots", "stream"] }
|
reqwest = { version = "0.12.4", default-features = false, features = ["json", "rustls-tls", "rustls-tls-native-roots", "stream"] }
|
||||||
serde = { version = "1.0.163", features = ["derive"] }
|
serde = { version = "1.0.163", features = ["derive"] }
|
||||||
serde_json = "1.0.96"
|
serde_json = "1.0.96"
|
||||||
toml = "0.8.8"
|
toml = "0.8.8"
|
||||||
|
|
|
@ -81,6 +81,7 @@ impl ServerTokenConfig {
|
||||||
match self {
|
match self {
|
||||||
ServerTokenConfig::Raw { token } => Ok(token.clone()),
|
ServerTokenConfig::Raw { token } => Ok(token.clone()),
|
||||||
ServerTokenConfig::File { token_file } => Ok(read_to_string(token_file)
|
ServerTokenConfig::File { token_file } => Ok(read_to_string(token_file)
|
||||||
|
.map(|t| t.trim().to_string())
|
||||||
.with_context(|| format!("Failed to read token from {token_file}"))?),
|
.with_context(|| format!("Failed to read token from {token_file}"))?),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,9 +62,6 @@ let
|
||||||
|
|
||||||
ATTIC_DISTRIBUTOR = "attic";
|
ATTIC_DISTRIBUTOR = "attic";
|
||||||
|
|
||||||
# See comment in `attic/build.rs`
|
|
||||||
NIX_INCLUDE_PATH = "${lib.getDev nix}/include";
|
|
||||||
|
|
||||||
# See comment in `attic-tests`
|
# See comment in `attic-tests`
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
|
|
||||||
|
@ -139,9 +136,6 @@ let
|
||||||
checkPhaseCargoCommand = "cargoWithProfile test --no-run --message-format=json >cargo-test.json";
|
checkPhaseCargoCommand = "cargoWithProfile test --no-run --message-format=json >cargo-test.json";
|
||||||
doInstallCargoArtifacts = false;
|
doInstallCargoArtifacts = false;
|
||||||
|
|
||||||
# See comment in `attic/build.rs`
|
|
||||||
NIX_INCLUDE_PATH = "${lib.getDev nix}/include";
|
|
||||||
|
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
runHook preInstall
|
runHook preInstall
|
||||||
|
|
||||||
|
|
152
flake.lock
152
flake.lock
|
@ -7,11 +7,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1702918879,
|
"lastModified": 1721322122,
|
||||||
"narHash": "sha256-tWJqzajIvYcaRWxn+cLUB9L9Pv4dQ3Bfit/YjU5ze3g=",
|
"narHash": "sha256-a0G1NvyXGzdwgu6e1HQpmK5R5yLsfxeBe07nNDyYd+g=",
|
||||||
"owner": "ipetkov",
|
"owner": "ipetkov",
|
||||||
"repo": "crane",
|
"repo": "crane",
|
||||||
"rev": "7195c00c272fdd92fc74e7d5a0a2844b9fadb2fb",
|
"rev": "8a68b987c476a33e90f203f0927614a75c3f47ea",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -23,11 +23,11 @@
|
||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1673956053,
|
"lastModified": 1696426674,
|
||||||
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||||
"owner": "edolstra",
|
"owner": "edolstra",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -37,12 +37,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-utils": {
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1667395993,
|
"lastModified": 1710146030,
|
||||||
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -51,13 +54,87 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-utils_2": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710146030,
|
||||||
|
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flakey-profile": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1712898590,
|
||||||
|
"narHash": "sha256-FhGIEU93VHAChKEXx905TSiPZKga69bWl1VB37FK//I=",
|
||||||
|
"owner": "lf-",
|
||||||
|
"repo": "flakey-profile",
|
||||||
|
"rev": "243c903fd8eadc0f63d205665a92d4df91d42d9d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "lf-",
|
||||||
|
"repo": "flakey-profile",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lix": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1721371213,
|
||||||
|
"narHash": "sha256-7SdrlNe5DBlK5uLBhPPxVRWI50N1PFz3zMBeDYiX0Qs=",
|
||||||
|
"ref": "refs/heads/main",
|
||||||
|
"rev": "aba5f19680b2f4c29d7ce2ff5e2a89128c1cb26d",
|
||||||
|
"revCount": 15985,
|
||||||
|
"type": "git",
|
||||||
|
"url": "ssh://git@git.lix.systems/lix-project/lix"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "ssh://git@git.lix.systems/lix-project/lix"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lix-module": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils_2",
|
||||||
|
"flakey-profile": "flakey-profile",
|
||||||
|
"lix": [
|
||||||
|
"lix"
|
||||||
|
],
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1720695775,
|
||||||
|
"narHash": "sha256-8Oqzl9QPjEe/n8y0R2tC6+2v/H6xBgABHXOJwxmnBg0=",
|
||||||
|
"ref": "refs/heads/main",
|
||||||
|
"rev": "d70318fb946a0e720dfdd1fb10b0645c14e2a02a",
|
||||||
|
"revCount": 94,
|
||||||
|
"type": "git",
|
||||||
|
"url": "ssh://git@git.lix.systems/lix-project/nixos-module"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "ssh://git@git.lix.systems/lix-project/nixos-module"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1711401922,
|
"lastModified": 1721373214,
|
||||||
"narHash": "sha256-QoQqXoj8ClGo0sqD/qWKFWezgEwUL0SUh37/vY2jNhc=",
|
"narHash": "sha256-crpGeGQGFlnCsMyCE5eheyjzo3xo03o1FXJ2sAbm7No=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "07262b18b97000d16a4bdb003418bd2fb067a932",
|
"rev": "af9c15bc7a314c226d7d5d85e159f7a73e8d9fae",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -67,29 +144,44 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-stable": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1711460390,
|
|
||||||
"narHash": "sha256-akSgjDZL6pVHEfSE6sz1DNSXuYX6hq+P/1Z5IoYWs7E=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "44733514b72e732bd49f5511bd0203dea9b9a434",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixos-23.11",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"crane": "crane",
|
"crane": "crane",
|
||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
"flake-utils": "flake-utils",
|
"flake-utils": "flake-utils",
|
||||||
"nixpkgs": "nixpkgs",
|
"lix": "lix",
|
||||||
"nixpkgs-stable": "nixpkgs-stable"
|
"lix-module": "lix-module",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
39
flake.nix
39
flake.nix
|
@ -3,7 +3,15 @@
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-23.11";
|
lix = {
|
||||||
|
url = "git+ssh://git@git.lix.systems/lix-project/lix";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
lix-module = {
|
||||||
|
url = "git+ssh://git@git.lix.systems/lix-project/nixos-module";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
inputs.lix.follows = "lix";
|
||||||
|
};
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
|
||||||
crane = {
|
crane = {
|
||||||
|
@ -17,7 +25,7 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs, nixpkgs-stable, flake-utils, crane, ... }: let
|
outputs = { self, nixpkgs, lix-module, flake-utils, crane, ... }: let
|
||||||
supportedSystems = flake-utils.lib.defaultSystems ++ [ "riscv64-linux" ];
|
supportedSystems = flake-utils.lib.defaultSystems ++ [ "riscv64-linux" ];
|
||||||
|
|
||||||
makeCranePkgs = pkgs: let
|
makeCranePkgs = pkgs: let
|
||||||
|
@ -26,16 +34,10 @@
|
||||||
in flake-utils.lib.eachSystem supportedSystems (system: let
|
in flake-utils.lib.eachSystem supportedSystems (system: let
|
||||||
pkgs = import nixpkgs {
|
pkgs = import nixpkgs {
|
||||||
inherit system;
|
inherit system;
|
||||||
overlays = [];
|
overlays = [lix-module.overlays.default];
|
||||||
};
|
};
|
||||||
cranePkgs = makeCranePkgs pkgs;
|
cranePkgs = makeCranePkgs pkgs;
|
||||||
|
|
||||||
pkgsStable = import nixpkgs-stable {
|
|
||||||
inherit system;
|
|
||||||
overlays = [];
|
|
||||||
};
|
|
||||||
cranePkgsStable = makeCranePkgs pkgsStable;
|
|
||||||
|
|
||||||
inherit (pkgs) lib;
|
inherit (pkgs) lib;
|
||||||
in rec {
|
in rec {
|
||||||
packages = {
|
packages = {
|
||||||
|
@ -55,17 +57,7 @@
|
||||||
} // (lib.optionalAttrs (system != "x86_64-darwin") {
|
} // (lib.optionalAttrs (system != "x86_64-darwin") {
|
||||||
# Unfortunately, x86_64-darwin fails to evaluate static builds
|
# Unfortunately, x86_64-darwin fails to evaluate static builds
|
||||||
# TODO: Make this work with Crane
|
# TODO: Make this work with Crane
|
||||||
attic-static = (pkgs.pkgsStatic.callPackage ./package.nix {
|
attic-static = (pkgs.pkgsStatic.callPackage ./package.nix { }).overrideAttrs (old: {
|
||||||
nix = pkgs.pkgsStatic.nix.overrideAttrs (old: {
|
|
||||||
patches = (old.patches or []) ++ [
|
|
||||||
# To be submitted
|
|
||||||
(pkgs.fetchpatch {
|
|
||||||
url = "https://github.com/NixOS/nix/compare/3172c51baff5c81362fcdafa2e28773c2949c660...6b09a02536d5946458b537dfc36b7d268c9ce823.diff";
|
|
||||||
hash = "sha256-LFLq++J2XitEWQ0o57ihuuUlYk2PgUr11h7mMMAEe3c=";
|
|
||||||
})
|
|
||||||
];
|
|
||||||
});
|
|
||||||
}).overrideAttrs (old: {
|
|
||||||
nativeBuildInputs = (old.nativeBuildInputs or []) ++ [
|
nativeBuildInputs = (old.nativeBuildInputs or []) ++ [
|
||||||
pkgs.nukeReferences
|
pkgs.nukeReferences
|
||||||
];
|
];
|
||||||
|
@ -127,12 +119,8 @@
|
||||||
linuxPackages.perf
|
linuxPackages.perf
|
||||||
]);
|
]);
|
||||||
|
|
||||||
NIX_PATH = "nixpkgs=${pkgs.path}";
|
|
||||||
RUST_SRC_PATH = "${pkgs.rustPlatform.rustcSrc}/library";
|
RUST_SRC_PATH = "${pkgs.rustPlatform.rustcSrc}/library";
|
||||||
|
|
||||||
# See comment in `attic/build.rs`
|
|
||||||
NIX_INCLUDE_PATH = "${lib.getDev pkgs.nix}/include";
|
|
||||||
|
|
||||||
ATTIC_DISTRIBUTOR = "dev";
|
ATTIC_DISTRIBUTOR = "dev";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -163,8 +151,7 @@
|
||||||
flake = self;
|
flake = self;
|
||||||
};
|
};
|
||||||
unstableTests = makeIntegrationTests pkgs;
|
unstableTests = makeIntegrationTests pkgs;
|
||||||
stableTests = lib.mapAttrs' (name: lib.nameValuePair "stable-${name}") (makeIntegrationTests pkgsStable);
|
in lib.optionalAttrs pkgs.stdenv.isLinux unstableTests;
|
||||||
in lib.optionalAttrs pkgs.stdenv.isLinux (unstableTests // stableTests);
|
|
||||||
}) // {
|
}) // {
|
||||||
overlays = {
|
overlays = {
|
||||||
default = final: prev: let
|
default = final: prev: let
|
||||||
|
|
|
@ -33,6 +33,6 @@ let
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}) (lib.cartesianProductOfSets matrix));
|
}) (lib.cartesianProduct matrix));
|
||||||
in {
|
in {
|
||||||
} // basicTests
|
} // basicTests
|
||||||
|
|
|
@ -49,9 +49,6 @@ in rustPlatform.buildRustPackage rec {
|
||||||
|
|
||||||
ATTIC_DISTRIBUTOR = "attic";
|
ATTIC_DISTRIBUTOR = "attic";
|
||||||
|
|
||||||
# See comment in `attic/build.rs`
|
|
||||||
NIX_INCLUDE_PATH = "${lib.getDev nix}/include";
|
|
||||||
|
|
||||||
# Recursive Nix is not stable yet
|
# Recursive Nix is not stable yet
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
|
|
||||||
|
|
|
@ -25,11 +25,11 @@ attic-token = { path = "../token" }
|
||||||
anyhow = "1.0.71"
|
anyhow = "1.0.71"
|
||||||
async-stream = "0.3.5"
|
async-stream = "0.3.5"
|
||||||
async-trait = "0.1.68"
|
async-trait = "0.1.68"
|
||||||
aws-config = "0.57.1"
|
aws-config = "1.5.0"
|
||||||
aws-sdk-s3 = "0.35.0"
|
aws-sdk-s3 = "1.32.0"
|
||||||
axum = "0.6.18"
|
axum = "0.7.5"
|
||||||
axum-macros = "0.3.7"
|
axum-macros = "0.4.1"
|
||||||
base64 = "0.21.2"
|
base64 = "0.22.1"
|
||||||
bytes = "1.4.0"
|
bytes = "1.4.0"
|
||||||
chrono = "0.4.24"
|
chrono = "0.4.24"
|
||||||
clap = { version = "4.3", features = ["derive"] }
|
clap = { version = "4.3", features = ["derive"] }
|
||||||
|
@ -40,6 +40,7 @@ enum-as-inner = "0.6.0"
|
||||||
fastcdc = "3.0.3"
|
fastcdc = "3.0.3"
|
||||||
futures = "0.3.28"
|
futures = "0.3.28"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
|
http-body-util = "0.1.1"
|
||||||
humantime = "2.1.0"
|
humantime = "2.1.0"
|
||||||
humantime-serde = "1.1.1"
|
humantime-serde = "1.1.1"
|
||||||
itoa = "=1.0.5"
|
itoa = "=1.0.5"
|
||||||
|
@ -53,7 +54,7 @@ serde_json = "1.0.96"
|
||||||
serde_with = "3.0.0"
|
serde_with = "3.0.0"
|
||||||
tokio-util = { version = "0.7.8", features = [ "io" ] }
|
tokio-util = { version = "0.7.8", features = [ "io" ] }
|
||||||
toml = "0.8.8"
|
toml = "0.8.8"
|
||||||
tower-http = { version = "0.4.0", features = [ "catch-panic", "trace" ] }
|
tower-http = { version = "0.5.2", features = [ "catch-panic", "trace" ] }
|
||||||
tracing = "0.1.37"
|
tracing = "0.1.37"
|
||||||
tracing-error = "0.2.0"
|
tracing-error = "0.2.0"
|
||||||
tracing-subscriber = { version = "0.3.17", features = [ "json" ] }
|
tracing-subscriber = { version = "0.3.17", features = [ "json" ] }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! HTTP middlewares for access control.
|
//! HTTP middlewares for access control.
|
||||||
|
|
||||||
use axum::{http::Request, middleware::Next, response::Response};
|
use axum::{extract::Request, middleware::Next, response::Response};
|
||||||
use sea_orm::DatabaseConnection;
|
use sea_orm::DatabaseConnection;
|
||||||
use tokio::sync::OnceCell;
|
use tokio::sync::OnceCell;
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ impl AuthState {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs auth.
|
/// Performs auth.
|
||||||
pub async fn apply_auth<B>(req: Request<B>, next: Next<B>) -> Response {
|
pub async fn apply_auth(req: Request, next: Next) -> Response {
|
||||||
let token: Option<Token> = req
|
let token: Option<Token> = req
|
||||||
.headers()
|
.headers()
|
||||||
.get("Authorization")
|
.get("Authorization")
|
||||||
|
|
|
@ -10,7 +10,7 @@ use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
body::StreamBody,
|
body::Body,
|
||||||
extract::{Extension, Path},
|
extract::{Extension, Path},
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
response::{IntoResponse, Redirect, Response},
|
response::{IntoResponse, Redirect, Response},
|
||||||
|
@ -18,6 +18,7 @@ use axum::{
|
||||||
Router,
|
Router,
|
||||||
};
|
};
|
||||||
use futures::stream::BoxStream;
|
use futures::stream::BoxStream;
|
||||||
|
use http_body_util::BodyExt;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use tokio_util::io::ReaderStream;
|
use tokio_util::io::ReaderStream;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
@ -217,7 +218,11 @@ async fn get_nar(
|
||||||
Download::Url(url) => Ok(Redirect::temporary(&url).into_response()),
|
Download::Url(url) => Ok(Redirect::temporary(&url).into_response()),
|
||||||
Download::AsyncRead(stream) => {
|
Download::AsyncRead(stream) => {
|
||||||
let stream = ReaderStream::new(stream);
|
let stream = ReaderStream::new(stream);
|
||||||
let body = StreamBody::new(stream);
|
let body = Body::from_stream(stream).map_err(|e| {
|
||||||
|
tracing::error!("Stream error: {e}");
|
||||||
|
e
|
||||||
|
}).into_inner();
|
||||||
|
|
||||||
Ok(body.into_response())
|
Ok(body.into_response())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -250,7 +255,11 @@ async fn get_nar(
|
||||||
// TODO: Make num_prefetch configurable
|
// TODO: Make num_prefetch configurable
|
||||||
// The ideal size depends on the average chunk size
|
// The ideal size depends on the average chunk size
|
||||||
let merged = merge_chunks(chunks, streamer, storage, 2);
|
let merged = merge_chunks(chunks, streamer, storage, 2);
|
||||||
let body = StreamBody::new(merged);
|
let body = Body::from_stream(merged).map_err(|e| {
|
||||||
|
tracing::error!("Stream error: {e}");
|
||||||
|
e
|
||||||
|
}).into_inner();
|
||||||
|
|
||||||
Ok(body.into_response())
|
Ok(body.into_response())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@ use anyhow::anyhow;
|
||||||
use async_compression::tokio::bufread::{BrotliEncoder, XzEncoder, ZstdEncoder};
|
use async_compression::tokio::bufread::{BrotliEncoder, XzEncoder, ZstdEncoder};
|
||||||
use async_compression::Level as CompressionLevel;
|
use async_compression::Level as CompressionLevel;
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{BodyStream, Extension, Json},
|
body::Body,
|
||||||
|
extract::{Extension, Json},
|
||||||
http::HeaderMap,
|
http::HeaderMap,
|
||||||
};
|
};
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
|
@ -120,8 +121,9 @@ pub(crate) async fn upload_path(
|
||||||
Extension(state): Extension<State>,
|
Extension(state): Extension<State>,
|
||||||
Extension(req_state): Extension<RequestState>,
|
Extension(req_state): Extension<RequestState>,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
stream: BodyStream,
|
body: Body,
|
||||||
) -> ServerResult<Json<UploadPathResult>> {
|
) -> ServerResult<Json<UploadPathResult>> {
|
||||||
|
let stream = body.into_data_stream();
|
||||||
let mut stream = StreamReader::new(
|
let mut stream = StreamReader::new(
|
||||||
stream.map(|r| r.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))),
|
stream.map(|r| r.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))),
|
||||||
);
|
);
|
||||||
|
|
|
@ -158,6 +158,15 @@ async fn run_reap_orphan_chunks(state: &State) -> Result<()> {
|
||||||
let db = state.database().await?;
|
let db = state.database().await?;
|
||||||
let storage = state.storage().await?;
|
let storage = state.storage().await?;
|
||||||
|
|
||||||
|
let orphan_chunk_limit = match db.get_database_backend() {
|
||||||
|
// Arbitrarily chosen sensible value since there's no good default to choose from for MySQL
|
||||||
|
sea_orm::DatabaseBackend::MySql => 1000,
|
||||||
|
// Panic limit set by sqlx for postgresql: https://github.com/launchbadge/sqlx/issues/671#issuecomment-687043510
|
||||||
|
sea_orm::DatabaseBackend::Postgres => u64::from(u16::MAX),
|
||||||
|
// Default statement limit imposed by sqlite: https://www.sqlite.org/limits.html#max_variable_number
|
||||||
|
sea_orm::DatabaseBackend::Sqlite => 500,
|
||||||
|
};
|
||||||
|
|
||||||
// find all orphan chunks...
|
// find all orphan chunks...
|
||||||
let orphan_chunk_ids = Query::select()
|
let orphan_chunk_ids = Query::select()
|
||||||
.from(Chunk)
|
.from(Chunk)
|
||||||
|
@ -190,6 +199,7 @@ async fn run_reap_orphan_chunks(state: &State) -> Result<()> {
|
||||||
|
|
||||||
let orphan_chunks: Vec<chunk::Model> = Chunk::find()
|
let orphan_chunks: Vec<chunk::Model> = Chunk::find()
|
||||||
.filter(chunk::Column::State.eq(ChunkState::Deleted))
|
.filter(chunk::Column::State.eq(ChunkState::Deleted))
|
||||||
|
.limit(orphan_chunk_limit)
|
||||||
.all(db)
|
.all(db)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ pub mod nix_manifest;
|
||||||
pub mod oobe;
|
pub mod oobe;
|
||||||
mod storage;
|
mod storage;
|
||||||
|
|
||||||
|
use std::future::IntoFuture;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -38,6 +39,7 @@ use axum::{
|
||||||
Router,
|
Router,
|
||||||
};
|
};
|
||||||
use sea_orm::{query::Statement, ConnectionTrait, Database, DatabaseConnection};
|
use sea_orm::{query::Statement, ConnectionTrait, Database, DatabaseConnection};
|
||||||
|
use tokio::net::TcpListener;
|
||||||
use tokio::sync::OnceCell;
|
use tokio::sync::OnceCell;
|
||||||
use tokio::time;
|
use tokio::time;
|
||||||
use tower_http::catch_panic::CatchPanicLayer;
|
use tower_http::catch_panic::CatchPanicLayer;
|
||||||
|
@ -105,9 +107,28 @@ impl StateInner {
|
||||||
async fn database(&self) -> ServerResult<&DatabaseConnection> {
|
async fn database(&self) -> ServerResult<&DatabaseConnection> {
|
||||||
self.database
|
self.database
|
||||||
.get_or_try_init(|| async {
|
.get_or_try_init(|| async {
|
||||||
Database::connect(&self.config.database.url)
|
let db = Database::connect(&self.config.database.url)
|
||||||
.await
|
.await
|
||||||
.map_err(ServerError::database_error)
|
.map_err(ServerError::database_error);
|
||||||
|
if let Ok(DatabaseConnection::SqlxSqlitePoolConnection(ref conn)) = db {
|
||||||
|
// execute some sqlite-specific performance optimizations
|
||||||
|
// see https://phiresky.github.io/blog/2020/sqlite-performance-tuning/ for
|
||||||
|
// more details
|
||||||
|
// intentionally ignore errors from this: this is purely for performance,
|
||||||
|
// not for correctness, so we can live without this
|
||||||
|
_ = conn
|
||||||
|
.execute_unprepared(
|
||||||
|
"
|
||||||
|
pragma journal_mode=WAL;
|
||||||
|
pragma synchronous=normal;
|
||||||
|
pragma temp_store=memory;
|
||||||
|
pragma mmap_size = 30000000000;
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
db
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
@ -221,14 +242,13 @@ pub async fn run_api_server(cli_listen: Option<SocketAddr>, config: Config) -> R
|
||||||
|
|
||||||
eprintln!("Listening on {:?}...", listen);
|
eprintln!("Listening on {:?}...", listen);
|
||||||
|
|
||||||
let (server_ret, _) = tokio::join!(
|
let listener = TcpListener::bind(&listen).await?;
|
||||||
axum::Server::bind(&listen).serve(rest.into_make_service()),
|
|
||||||
async {
|
let (server_ret, _) = tokio::join!(axum::serve(listener, rest).into_future(), async {
|
||||||
if state.config.database.heartbeat {
|
if state.config.database.heartbeat {
|
||||||
let _ = state.run_db_heartbeat().await;
|
let _ = state.run_db_heartbeat().await;
|
||||||
}
|
}
|
||||||
},
|
},);
|
||||||
);
|
|
||||||
|
|
||||||
server_ret?;
|
server_ret?;
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{Extension, Host},
|
extract::{Extension, Host, Request},
|
||||||
http::{HeaderValue, Request},
|
http::HeaderValue,
|
||||||
middleware::Next,
|
middleware::Next,
|
||||||
response::Response,
|
response::Response,
|
||||||
};
|
};
|
||||||
|
@ -14,11 +14,11 @@ use crate::error::{ErrorKind, ServerResult};
|
||||||
use attic::api::binary_cache::ATTIC_CACHE_VISIBILITY;
|
use attic::api::binary_cache::ATTIC_CACHE_VISIBILITY;
|
||||||
|
|
||||||
/// Initializes per-request state.
|
/// Initializes per-request state.
|
||||||
pub async fn init_request_state<B>(
|
pub async fn init_request_state(
|
||||||
Extension(state): Extension<State>,
|
Extension(state): Extension<State>,
|
||||||
Host(host): Host,
|
Host(host): Host,
|
||||||
mut req: Request<B>,
|
mut req: Request,
|
||||||
next: Next<B>,
|
next: Next,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
// X-Forwarded-Proto is an untrusted header
|
// X-Forwarded-Proto is an untrusted header
|
||||||
let client_claims_https =
|
let client_claims_https =
|
||||||
|
@ -45,11 +45,11 @@ pub async fn init_request_state<B>(
|
||||||
///
|
///
|
||||||
/// We also require that all request have a Host header in
|
/// We also require that all request have a Host header in
|
||||||
/// the first place.
|
/// the first place.
|
||||||
pub async fn restrict_host<B>(
|
pub async fn restrict_host(
|
||||||
Extension(state): Extension<State>,
|
Extension(state): Extension<State>,
|
||||||
Host(host): Host,
|
Host(host): Host,
|
||||||
req: Request<B>,
|
req: Request,
|
||||||
next: Next<B>,
|
next: Next,
|
||||||
) -> ServerResult<Response> {
|
) -> ServerResult<Response> {
|
||||||
let allowed_hosts = &state.config.allowed_hosts;
|
let allowed_hosts = &state.config.allowed_hosts;
|
||||||
|
|
||||||
|
@ -61,10 +61,10 @@ pub async fn restrict_host<B>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the `X-Attic-Cache-Visibility` header in responses.
|
/// Sets the `X-Attic-Cache-Visibility` header in responses.
|
||||||
pub(crate) async fn set_visibility_header<B>(
|
pub(crate) async fn set_visibility_header(
|
||||||
Extension(req_state): Extension<RequestState>,
|
Extension(req_state): Extension<RequestState>,
|
||||||
req: Request<B>,
|
req: Request,
|
||||||
next: Next<B>,
|
next: Next,
|
||||||
) -> ServerResult<Response> {
|
) -> ServerResult<Response> {
|
||||||
let mut response = next.run(req).await;
|
let mut response = next.run(req).await;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use aws_config::BehaviorVersion;
|
||||||
use aws_sdk_s3::{
|
use aws_sdk_s3::{
|
||||||
config::Builder as S3ConfigBuilder,
|
config::Builder as S3ConfigBuilder,
|
||||||
config::{Credentials, Region},
|
config::{Credentials, Region},
|
||||||
|
@ -91,7 +92,7 @@ impl S3Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn config_builder(config: &S3StorageConfig) -> ServerResult<S3ConfigBuilder> {
|
async fn config_builder(config: &S3StorageConfig) -> ServerResult<S3ConfigBuilder> {
|
||||||
let shared_config = aws_config::load_from_env().await;
|
let shared_config = aws_config::load_defaults(BehaviorVersion::v2024_03_28()).await;
|
||||||
let mut builder = S3ConfigBuilder::from(&shared_config);
|
let mut builder = S3ConfigBuilder::from(&shared_config);
|
||||||
|
|
||||||
if let Some(credentials) = &config.credentials {
|
if let Some(credentials) = &config.credentials {
|
||||||
|
|
|
@ -8,9 +8,10 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
attic = { path = "../attic", default-features = false }
|
attic = { path = "../attic", default-features = false }
|
||||||
|
|
||||||
base64 = "0.21.2"
|
base64 = "0.22.1"
|
||||||
chrono = "0.4.24"
|
chrono = "0.4.24"
|
||||||
displaydoc = "0.2.4"
|
displaydoc = "0.2.4"
|
||||||
|
indexmap = { version = "2.2.6", features = ["serde"] }
|
||||||
jwt-simple = "0.11.5"
|
jwt-simple = "0.11.5"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
regex = "1.8.3"
|
regex = "1.8.3"
|
||||||
|
|
|
@ -83,12 +83,12 @@ pub mod util;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
|
|
||||||
use base64::{engine::general_purpose::STANDARD as BASE64_STANDARD, Engine};
|
use base64::{engine::general_purpose::STANDARD as BASE64_STANDARD, Engine};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use displaydoc::Display;
|
use displaydoc::Display;
|
||||||
|
use indexmap::IndexMap;
|
||||||
pub use jwt_simple::{
|
pub use jwt_simple::{
|
||||||
algorithms::{HS256Key, MACLike},
|
algorithms::{HS256Key, MACLike},
|
||||||
claims::{Claims, JWTClaims},
|
claims::{Claims, JWTClaims},
|
||||||
|
@ -146,7 +146,7 @@ pub struct AtticAccess {
|
||||||
/// Cache permissions.
|
/// Cache permissions.
|
||||||
///
|
///
|
||||||
/// Keys here may include wildcards.
|
/// Keys here may include wildcards.
|
||||||
caches: HashMap<CacheNamePattern, CachePermission>,
|
caches: IndexMap<CacheNamePattern, CachePermission>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Permission to a single cache.
|
/// Permission to a single cache.
|
||||||
|
@ -274,7 +274,7 @@ impl Token {
|
||||||
&mut self,
|
&mut self,
|
||||||
pattern: CacheNamePattern,
|
pattern: CacheNamePattern,
|
||||||
) -> &mut CachePermission {
|
) -> &mut CachePermission {
|
||||||
use std::collections::hash_map::Entry;
|
use indexmap::map::Entry;
|
||||||
|
|
||||||
let access = self.attic_access_mut();
|
let access = self.attic_access_mut();
|
||||||
match access.caches.entry(pattern) {
|
match access.caches.entry(pattern) {
|
||||||
|
|
|
@ -21,6 +21,8 @@ fn test_basic() {
|
||||||
"exp": 4102324986,
|
"exp": 4102324986,
|
||||||
"https://jwt.attic.rs/v1": {
|
"https://jwt.attic.rs/v1": {
|
||||||
"caches": {
|
"caches": {
|
||||||
|
"all-*": {"r":1},
|
||||||
|
"all-ci-*": {"w":1},
|
||||||
"cache-rw": {"r":1,"w":1},
|
"cache-rw": {"r":1,"w":1},
|
||||||
"cache-ro": {"r":1},
|
"cache-ro": {"r":1},
|
||||||
"team-*": {"r":1,"w":1,"cc":1}
|
"team-*": {"r":1,"w":1,"cc":1}
|
||||||
|
@ -29,7 +31,29 @@ fn test_basic() {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJtZW93IiwiZXhwIjo0MTAyMzI0OTg2LCJodHRwczovL2p3dC5hdHRpYy5ycy92MSI6eyJjYWNoZXMiOnsiY2FjaGUtcnciOnsiciI6MSwidyI6MX0sImNhY2hlLXJvIjp7InIiOjF9LCJ0ZWFtLSoiOnsiciI6MSwidyI6MSwiY2MiOjF9fX19.UlsIM9bQHr9SXGAcSQcoVPo9No8Zhh6Y5xfX8vCmKmA";
|
let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjQxMDIzMjQ5ODYsImh0dHBzOi8vand0LmF0dGljLnJzL3YxIjp7ImNhY2hlcyI6eyJhbGwtKiI6eyJyIjoxfSwiYWxsLWNpLSoiOnsidyI6MX0sImNhY2hlLXJvIjp7InIiOjF9LCJjYWNoZS1ydyI6eyJyIjoxLCJ3IjoxfSwidGVhbS0qIjp7ImNjIjoxLCJyIjoxLCJ3IjoxfX19LCJpYXQiOjE3MTY2NjA1ODksInN1YiI6Im1lb3cifQ.8vtxp_1OEYdcnkGPM4c9ORXooJZV7DOTS4NRkMKN8mw";
|
||||||
|
|
||||||
|
// NOTE(cole-h): check that we get a consistent iteration order when getting permissions for
|
||||||
|
// caches -- this depends on the order of the fields in the token, but should otherwise be
|
||||||
|
// consistent between iterations
|
||||||
|
let mut was_ever_wrong = false;
|
||||||
|
for _ in 0..=1_000 {
|
||||||
|
// NOTE(cole-h): we construct a new Token every iteration in order to get different "random
|
||||||
|
// state"
|
||||||
|
let decoded = Token::from_jwt(token, &dec_key).unwrap();
|
||||||
|
let perm_all_ci = decoded.get_permission_for_cache(&cache! { "all-ci-abc" });
|
||||||
|
|
||||||
|
// NOTE(cole-h): if the iteration order of the token is inconsistent, the permissions may be
|
||||||
|
// retrieved from the `all-ci-*` pattern (which only allows writing/pushing), even though
|
||||||
|
// the `all-*` pattern (which only allows reading/pulling) is specified first
|
||||||
|
if perm_all_ci.require_pull().is_err() || perm_all_ci.require_push().is_ok() {
|
||||||
|
was_ever_wrong = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(
|
||||||
|
!was_ever_wrong,
|
||||||
|
"Iteration order should be consistent to prevent random auth failures (and successes)"
|
||||||
|
);
|
||||||
|
|
||||||
let decoded = Token::from_jwt(token, &dec_key).unwrap();
|
let decoded = Token::from_jwt(token, &dec_key).unwrap();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue