diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 2ae461596..1839d5525 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -288,7 +288,7 @@ jobs: ${SCCACHE_PATH} --show-stats | grep -e "Cache hits\s*[1-9]" - memcached: + memcached-deprecated: runs-on: ubuntu-latest needs: build @@ -343,6 +343,61 @@ jobs: ${SCCACHE_PATH} --show-stats | grep -e "Cache hits\s*[1-9]" + memcached: + runs-on: ubuntu-latest + needs: build + + # Setup memcached server + services: + memcached: + image: bitnami/memcached + env: + # memcache's max item size is 1MiB, But our tests + # will produce larger file. + # + # Specify the setting here to make our test happy. + MEMCACHED_MAX_ITEM_SIZE: 16777216 + ports: + - 11211:11211 + + env: + SCCACHE_MEMCACHED_ENDPOINT: "tcp://127.0.0.1:11211" + RUSTC_WRAPPER: /home/runner/.cargo/bin/sccache + + steps: + - name: Clone repository + uses: actions/checkout@v4 + + - name: Install rust + uses: ./.github/actions/rust-toolchain + with: + toolchain: "stable" + + - uses: actions/download-artifact@v4 + with: + name: integration-tests + path: /home/runner/.cargo/bin/ + - name: Chmod for binary + run: chmod +x ${SCCACHE_PATH} + + - name: Test + run: cargo clean && cargo build + + - name: Output + run: | + ${SCCACHE_PATH} --show-stats + + ${SCCACHE_PATH} --show-stats | grep memcached + + - name: Test Twice for Cache Read + run: cargo clean && cargo build + + - name: Output + run: | + ${SCCACHE_PATH} --show-stats + + ${SCCACHE_PATH} --show-stats | grep -e "Cache hits\s*[1-9]" + webdav: runs-on: ubuntu-latest needs: build diff --git a/docs/Configuration.md b/docs/Configuration.md index 7f75dc958..24207270e 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -57,7 +57,12 @@ cache_to = "sccache-latest" cache_from = "sccache-" [cache.memcached] -url = "127.0.0.1:11211" +# Deprecated alias for `endpoint` +# url = "127.0.0.1:11211" +endpoint = "tcp://127.0.0.1:11211" +# Username and password for authentication +username = "user" +password = "passwd" # Entry expiration time in seconds. Default is 86400 (24 hours) expiration = 3600 key_prefix = "/custom/prefix/if/need" @@ -169,7 +174,10 @@ The full url appears then as `redis://user:passwd@1.2.3.4:6379/?db=1`. #### memcached -* `SCCACHE_MEMCACHED` memcached url. +* `SCCACHE_MEMCACHED` is a deprecated alias for `SCCACHE_MEMCACHED_ENDPOINT`. +* `SCCACHE_MEMCACHED_ENDPOINT` memcached url. +* `SCCACHE_MEMCACHED_USERNAME` memcached username (optional). +* `SCCACHE_MEMCACHED_PASSWORD` memcached password (optional). * `SCCACHE_MEMCACHED_EXPIRATION` ttl for memcached cache, don't set for default behavior. * `SCCACHE_MEMCACHED_KEY_PREFIX` key prefix (optional). diff --git a/docs/Memcached.md b/docs/Memcached.md index db7c261a2..c476a4169 100644 --- a/docs/Memcached.md +++ b/docs/Memcached.md @@ -1,6 +1,10 @@ # Memcached -Set `SCCACHE_MEMCACHED` to a [Memcached](https://memcached.org/) url in format `tcp://: ...` to store the cache in a Memcached instance. +Set `SCCACHE_MEMCACHED_ENDPOINT` to a [Memcached](https://memcached.org/) url in format `tcp://: ...` to store the cache in a Memcached instance. + +`SCCACHE_MEMCACHED` is a deprecated alias for `SCCACHE_MEMCACHED_ENDPOINT` for unifying the variable name with other remote storages. + +Set `SCCACHE_MEMCACHED_USERNAME` and `SCCACHE_MEMCACHED_PASSWORD` if you want to authenticate to Memcached. Set `SCCACHE_MEMCACHED_EXPIRATION` to the default expiration seconds of memcached. The default value is `86400` (1 day) and can up to `2592000` (30 days). Set this value to `0` will disable the expiration. memcached will purge the cache entry while it exceed 30 days or meets LRU rules. diff --git a/docs/Redis.md b/docs/Redis.md index b744658bf..2b0dbda1e 100644 --- a/docs/Redis.md +++ b/docs/Redis.md @@ -23,7 +23,7 @@ Set `SCCACHE_REDIS_EXPIRATION` in seconds if you don't want your cache to live f Set `SCCACHE_REDIS_KEY_PREFIX` if you want to prefix all cache keys. This can be useful when sharing a Redis instance with another application or cache. -`SCCACHE_REDIS` is deprecated, use `SCCACHE_REDIS_ENDPOINT` instead. +`SCCACHE_REDIS` is deprecated for security reasons, use `SCCACHE_REDIS_ENDPOINT` instead. See mozilla/sccache#2083 for details. If you really want to use `SCCACHE_REDIS`, you should URL in format `redis://[[]:@][:port][/?db=]`. ## Deprecated API Examples diff --git a/src/cache/cache.rs b/src/cache/cache.rs index 8ff83fe60..9d61788c8 100644 --- a/src/cache/cache.rs +++ b/src/cache/cache.rs @@ -601,13 +601,21 @@ pub fn storage_from_config( #[cfg(feature = "memcached")] CacheType::Memcached(config::MemcachedCacheConfig { ref url, + ref username, + ref password, ref expiration, ref key_prefix, }) => { debug!("Init memcached cache with url {url}"); - let storage = MemcachedCache::build(url, key_prefix, *expiration) - .map_err(|err| anyhow!("create memcached cache failed: {err:?}"))?; + let storage = MemcachedCache::build( + url, + username.as_deref(), + password.as_deref(), + key_prefix, + *expiration, + ) + .map_err(|err| anyhow!("create memcached cache failed: {err:?}"))?; return Ok(Arc::new(storage)); } #[cfg(feature = "redis")] diff --git a/src/cache/memcached.rs b/src/cache/memcached.rs index 03edada7b..b5735fc90 100644 --- a/src/cache/memcached.rs +++ b/src/cache/memcached.rs @@ -25,9 +25,23 @@ use crate::errors::*; pub struct MemcachedCache; impl MemcachedCache { - pub fn build(url: &str, key_prefix: &str, expiration: u32) -> Result { + pub fn build( + url: &str, + username: Option<&str>, + password: Option<&str>, + key_prefix: &str, + expiration: u32, + ) -> Result { let mut builder = Memcached::default(); builder.endpoint(url); + + if let Some(username) = username { + builder.username(username); + } + if let Some(password) = password { + builder.password(password); + } + builder.root(key_prefix); builder.default_ttl(Duration::from_secs(expiration.into())); diff --git a/src/config.rs b/src/config.rs index 4a6f366bc..4173bcaac 100644 --- a/src/config.rs +++ b/src/config.rs @@ -228,11 +228,18 @@ fn default_memcached_cache_expiration() -> u32 { DEFAULT_MEMCACHED_CACHE_EXPIRATION } -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)] #[serde(deny_unknown_fields)] pub struct MemcachedCacheConfig { + #[serde(alias = "endpoint")] pub url: String, + /// Username to authenticate with. + pub username: Option, + + /// Password to authenticate with. + pub password: Option, + /// the expiration time in seconds. /// /// Default to 24 hours (86400) @@ -698,7 +705,12 @@ fn config_from_env() -> Result { } // ======= memcached ======= - let memcached = if let Ok(url) = env::var("SCCACHE_MEMCACHED") { + let memcached = if let Ok(url) = + env::var("SCCACHE_MEMCACHED").or_else(|_| env::var("SCCACHE_MEMCACHED_ENDPOINT")) + { + let username = env::var("SCCACHE_MEMCACHED_USERNAME").ok(); + let password = env::var("SCCACHE_MEMCACHED_PASSWORD").ok(); + let expiration = number_from_env_var("SCCACHE_MEMCACHED_EXPIRATION") .transpose()? .unwrap_or(DEFAULT_MEMCACHED_CACHE_EXPIRATION); @@ -707,6 +719,8 @@ fn config_from_env() -> Result { Some(MemcachedCacheConfig { url, + username, + password, expiration, key_prefix, }) @@ -714,6 +728,12 @@ fn config_from_env() -> Result { None }; + if env::var_os("SCCACHE_MEMCACHED").is_some() + && env::var_os("SCCACHE_MEMCACHED_ENDPOINT").is_some() + { + bail!("You mustn't set both SCCACHE_MEMCACHED and SCCACHE_MEMCACHED_ENDPOINT. Please, use only SCCACHE_MEMCACHED_ENDPOINT."); + } + // ======= GCP/GCS ======= if (env::var("SCCACHE_GCS_CREDENTIALS_URL").is_ok() || env::var("SCCACHE_GCS_OAUTH_URL").is_ok() @@ -1250,6 +1270,7 @@ fn config_overrides() { url: "memurl".to_owned(), expiration: 24 * 3600, key_prefix: String::new(), + ..Default::default() }), redis: Some(RedisCacheConfig { url: Some("myredisurl".to_owned()), @@ -1434,7 +1455,12 @@ enabled = true version = "sccache" [cache.memcached] -url = "127.0.0.1:11211" +# Deprecated alias for `endpoint` +# url = "127.0.0.1:11211" +endpoint = "tcp://127.0.0.1:11211" +# Username and password for authentication +username = "user" +password = "passwd" expiration = 90000 key_prefix = "/custom/prefix/if/need" @@ -1506,7 +1532,9 @@ no_credentials = true key_prefix: "/my/redis/cache".into(), }), memcached: Some(MemcachedCacheConfig { - url: "127.0.0.1:11211".to_owned(), + url: "tcp://127.0.0.1:11211".to_owned(), + username: Some("user".to_owned()), + password: Some("passwd".to_owned()), expiration: 25 * 3600, key_prefix: "/custom/prefix/if/need".into(), }),