Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ username = "alice"
password = "secret12"
# Mutually exclusive with username & password. Bearer token value
token = "token123"
# Skip PROPFIND/MKCOL calls before writes, for servers that don't support them
disable_create_dir = false

[cache.oss]
bucket = "name"
Expand Down Expand Up @@ -325,6 +327,7 @@ When using Entra ID, credentials are read from the ambient `AZURE_*` environment
* `SCCACHE_WEBDAV_PASSWORD` a password to authenticate with webdav service (optional).
* `SCCACHE_WEBDAV_TOKEN` a token to authenticate with webdav service (optional) - may be used instead of login & password.
* `SCCACHE_WEBDAV_RW_MODE` allows to use webdav backend in read-only mode if set to `READ_ONLY`
* `SCCACHE_WEBDAV_DISABLE_CREATE_DIR` skip PROPFIND/MKCOL calls before writes, for servers that don't support them (optional).

#### OSS

Expand Down
9 changes: 9 additions & 0 deletions docs/Webdav.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,12 @@ Sccache is able to load credentials from the following sources:

- Set `SCCACHE_WEBDAV_USERNAME`/`SCCACHE_WEBDAV_PASSWORD` to specify the username/password pair for basic authentication.
- Set `SCCACHE_WEBDAV_TOKEN` to specify the token value for bearer token authentication.

## Disable Create Dir

Some WebDAV servers don't support the `PROPFIND`/`MKCOL` methods that opendal's WebDAV
backend otherwise issues before every write to ensure the parent directory exists. Sonatype
Nexus raw repositories are one example: they only implement `GET`/`HEAD`/`PUT`/`DELETE` and
reject `MKCOL` with `405 Method Not Allowed`, which makes every cache write fail.

Set `SCCACHE_WEBDAV_DISABLE_CREATE_DIR=true` to skip these calls during writes.
1 change: 1 addition & 0 deletions src/cache/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,7 @@ pub fn build_single_cache(
c.username.as_deref(),
c.password.as_deref(),
c.token.as_deref(),
c.disable_create_dir,
)
.map_err(|err| anyhow!("create webdav cache failed: {err:?}"))?;

Expand Down
4 changes: 3 additions & 1 deletion src/cache/webdav.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ impl WebdavCache {
username: Option<&str>,
password: Option<&str>,
token: Option<&str>,
disable_create_dir: bool,
) -> Result<Operator> {
let builder = Webdav::default()
.endpoint(endpoint)
.root(key_prefix)
.username(username.unwrap_or_default())
.password(password.unwrap_or_default())
.token(token.unwrap_or_default());
.token(token.unwrap_or_default())
.disable_create_dir(disable_create_dir);

let op = Operator::new(builder)?
.with_context(OperationContext::new().with_http_transport(set_user_agent()))
Expand Down
62 changes: 62 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,10 @@ pub struct WebdavCacheConfig {
pub token: Option<String>,
#[serde(default)]
pub rw_mode: CacheModeConfig,
/// Skip PROPFIND/MKCOL calls before writes, for servers (e.g. Nexus raw
/// repositories) that only implement GET/HEAD/PUT/DELETE.
#[serde(default)]
pub disable_create_dir: bool,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
Expand Down Expand Up @@ -1136,6 +1140,8 @@ fn config_from_env() -> Result<EnvConfig> {
let token = string_from_env_var("SCCACHE_WEBDAV_TOKEN");
let rw_mode =
cache_mode_from_env_var("SCCACHE_WEBDAV_RW_MODE").unwrap_or(CacheModeConfig::ReadWrite);
let disable_create_dir =
bool_from_env_var("SCCACHE_WEBDAV_DISABLE_CREATE_DIR")?.unwrap_or(false);
Comment thread
ChriFo marked this conversation as resolved.

Some(WebdavCacheConfig {
endpoint,
Expand All @@ -1144,6 +1150,7 @@ fn config_from_env() -> Result<EnvConfig> {
password,
token,
rw_mode,
disable_create_dir,
})
} else {
None
Expand Down Expand Up @@ -2490,6 +2497,59 @@ fn test_s3_sse_kms_from_env() {
}
}

#[test]
#[serial(config_from_env)]
#[cfg(feature = "webdav")]
fn test_webdav_disable_create_dir_from_env() {
unsafe {
env::set_var("SCCACHE_WEBDAV_ENDPOINT", "https://example.com/webdav");
env::set_var("SCCACHE_WEBDAV_DISABLE_CREATE_DIR", "true");
}

let cfg = config_from_env();

unsafe {
env::remove_var("SCCACHE_WEBDAV_ENDPOINT");
env::remove_var("SCCACHE_WEBDAV_DISABLE_CREATE_DIR");
}

let env_cfg = cfg.unwrap();
match env_cfg.cache.webdav {
Some(WebdavCacheConfig {
disable_create_dir, ..
}) => {
assert!(disable_create_dir);
}
None => unreachable!(),
}
}

#[test]
#[serial(config_from_env)]
#[cfg(feature = "webdav")]
fn test_webdav_disable_create_dir_defaults_to_false() {
unsafe {
env::set_var("SCCACHE_WEBDAV_ENDPOINT", "https://example.com/webdav");
env::remove_var("SCCACHE_WEBDAV_DISABLE_CREATE_DIR");
}

let cfg = config_from_env();

unsafe {
env::remove_var("SCCACHE_WEBDAV_ENDPOINT");
}

let env_cfg = cfg.unwrap();
match env_cfg.cache.webdav {
Some(WebdavCacheConfig {
disable_create_dir, ..
}) => {
assert!(!disable_create_dir);
}
None => unreachable!(),
}
}

#[test]
#[serial(config_from_env)]
#[cfg(feature = "azure")]
Expand Down Expand Up @@ -2848,6 +2908,7 @@ key_prefix = "webdavprefix"
username = "webdavusername"
password = "webdavpassword"
token = "webdavtoken"
disable_create_dir = true

[cache.oss]
bucket = "name"
Expand Down Expand Up @@ -2934,6 +2995,7 @@ key_prefix = "cosprefix"
password: Some("webdavpassword".to_string()),
token: Some("webdavtoken".to_string()),
rw_mode: CacheModeConfig::ReadWrite,
disable_create_dir: true,
}),
oss: Some(OSSCacheConfig {
bucket: "name".to_owned(),
Expand Down
Loading