Skip to content

Commit

Permalink
feat: Add Memcached authentication (#2117)
Browse files Browse the repository at this point in the history
* Add new env variables for memcached.
* Unify memcached endpoint env var name.
  • Loading branch information
AJIOB authored Mar 4, 2024
1 parent 73249b9 commit e747592
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 12 deletions.
57 changes: 56 additions & 1 deletion .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
12 changes: 10 additions & 2 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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).

Expand Down
6 changes: 5 additions & 1 deletion docs/Memcached.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Memcached

Set `SCCACHE_MEMCACHED` to a [Memcached](https://memcached.org/) url in format `tcp://<hostname>:<port> ...` to store the cache in a Memcached instance.
Set `SCCACHE_MEMCACHED_ENDPOINT` to a [Memcached](https://memcached.org/) url in format `tcp://<hostname>:<port> ...` 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.

Expand Down
2 changes: 1 addition & 1 deletion docs/Redis.md
Original file line number Diff line number Diff line change
Expand Up @@ -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://[[<username>]:<passwd>@]<hostname>[:port][/?db=<db>]`.

## Deprecated API Examples
Expand Down
12 changes: 10 additions & 2 deletions src/cache/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
16 changes: 15 additions & 1 deletion src/cache/memcached.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,23 @@ use crate::errors::*;
pub struct MemcachedCache;

impl MemcachedCache {
pub fn build(url: &str, key_prefix: &str, expiration: u32) -> Result<Operator> {
pub fn build(
url: &str,
username: Option<&str>,
password: Option<&str>,
key_prefix: &str,
expiration: u32,
) -> Result<Operator> {
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()));

Expand Down
36 changes: 32 additions & 4 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,

/// Password to authenticate with.
pub password: Option<String>,

/// the expiration time in seconds.
///
/// Default to 24 hours (86400)
Expand Down Expand Up @@ -698,7 +705,12 @@ fn config_from_env() -> Result<EnvConfig> {
}

// ======= 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);
Expand All @@ -707,13 +719,21 @@ fn config_from_env() -> Result<EnvConfig> {

Some(MemcachedCacheConfig {
url,
username,
password,
expiration,
key_prefix,
})
} else {
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()
Expand Down Expand Up @@ -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()),
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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(),
}),
Expand Down

0 comments on commit e747592

Please sign in to comment.