Skip to content

Commit 634ddbe

Browse files
committed
kms: Add RPC to clear image cache
1 parent 532e9b4 commit 634ddbe

File tree

7 files changed

+70
-8
lines changed

7 files changed

+70
-8
lines changed

kms/dstack-app/compose-dev.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ services:
6666
- /var/run/dstack.sock:/var/run/dstack.sock
6767
environment:
6868
- IMAGE_DOWNLOAD_URL=${IMAGE_DOWNLOAD_URL}
69+
- ADMIN_TOKEN_HASH=${ADMIN_TOKEN_HASH}
6970
ports:
7071
- 8000:8000
7172
depends_on:

kms/dstack-app/deploy-to-vmm.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ GIT_REV=HEAD
4040
4141
# The DStack OS image name to use for the KMS app
4242
OS_IMAGE=dstack-0.5.0
43+
44+
# The admin token for the KMS app
45+
ADMIN_TOKEN=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
4346
EOF
4447
echo "Please edit the .env file and set the required variables, then run this script again."
4548
exit 1
@@ -69,6 +72,8 @@ COMPOSE_TMP=$(mktemp)
6972

7073
GIT_REV=$(git rev-parse $GIT_REV)
7174

75+
ADMIN_TOKEN_HASH=$(echo -n $ADMIN_TOKEN | sha256sum | cut -d' ' -f1)
76+
7277
cp compose-dev.yaml "$COMPOSE_TMP"
7378

7479
subvar() {
@@ -79,6 +84,7 @@ subvar ETH_RPC_URL
7984
subvar KMS_CONTRACT_ADDR
8085
subvar GIT_REV
8186
subvar IMAGE_DOWNLOAD_URL
87+
subvar ADMIN_TOKEN_HASH
8288

8389
echo "Docker compose file:"
8490
cat "$COMPOSE_TMP"

kms/dstack-app/entrypoint.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
set -e
33

44
cat <<EOF > ./kms.toml
5+
[core]
6+
admin_token_hash = "${ADMIN_TOKEN_HASH}"
7+
58
[core.image]
69
verify = true
710
cache_dir = "./images"

kms/kms.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ mandatory = false
2121
[core]
2222
cert_dir = "/etc/kms/certs"
2323
subject_postfix = ".dstack"
24+
admin_token_hash = ""
2425

2526
[core.image]
2627
verify = true

kms/rpc/proto/kms_rpc.proto

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ service KMS {
8989
rpc GetTempCaCert(google.protobuf.Empty) returns (GetTempCaCertResponse);
9090
// Sign a certificate
9191
rpc SignCert(SignCertRequest) returns (SignCertResponse);
92+
// Clear the image cache
93+
rpc ClearImageCache(ClearImageCacheRequest) returns (google.protobuf.Empty);
94+
}
95+
96+
message ClearImageCacheRequest {
97+
string token = 1;
98+
string image_hash = 2;
99+
string config_hash = 3;
92100
}
93101

94102
message BootstrapRequest {

kms/src/config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ pub(crate) struct KmsConfig {
3434
pub auth_api: AuthApi,
3535
pub onboard: OnboardConfig,
3636
pub image: ImageConfig,
37+
#[serde(with = "serde_human_bytes")]
38+
pub admin_token_hash: Vec<u8>,
3739
}
3840

3941
impl KmsConfig {

kms/src/main_service.rs

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
use std::{ffi::OsStr, path::Path, sync::Arc};
1+
use std::{
2+
ffi::OsStr,
3+
path::{Path, PathBuf},
4+
sync::Arc,
5+
};
26

37
use anyhow::{bail, Context, Result};
48
use dstack_kms_rpc::{
59
kms_server::{KmsRpc, KmsServer},
6-
AppId, AppKeyResponse, GetAppKeyRequest, GetKmsKeyRequest, GetMetaResponse,
7-
GetTempCaCertResponse, KmsKeyResponse, KmsKeys, PublicKeyResponse, SignCertRequest,
8-
SignCertResponse,
10+
AppId, AppKeyResponse, ClearImageCacheRequest, GetAppKeyRequest, GetKmsKeyRequest,
11+
GetMetaResponse, GetTempCaCertResponse, KmsKeyResponse, KmsKeys, PublicKeyResponse,
12+
SignCertRequest, SignCertResponse,
913
};
1014
use dstack_types::VmConfig;
1115
use fs_err as fs;
@@ -149,8 +153,33 @@ impl RpcHandler {
149153
.await
150154
}
151155

156+
fn image_cache_dir(&self) -> PathBuf {
157+
self.state.config.image.cache_dir.join("images")
158+
}
159+
160+
fn mr_cache_dir(&self) -> PathBuf {
161+
self.state.config.image.cache_dir.join("computed")
162+
}
163+
164+
fn remove_cache_dir(&self, parent_dir: &PathBuf, sub_dir: &str) -> Result<()> {
165+
if sub_dir.is_empty() {
166+
return Ok(());
167+
}
168+
if sub_dir == "all" {
169+
fs::remove_dir_all(parent_dir)?;
170+
} else {
171+
let path = parent_dir.join(sub_dir);
172+
if path.is_dir() {
173+
fs::remove_dir_all(path)?;
174+
} else {
175+
fs::remove_file(path)?;
176+
}
177+
}
178+
Ok(())
179+
}
180+
152181
fn get_cached_mrs(&self, key: &str) -> Result<Mrs> {
153-
let path = self.state.config.image.cache_dir.join("computed").join(key);
182+
let path = self.mr_cache_dir().join(key);
154183
if !path.exists() {
155184
bail!("Cached MRs not found");
156185
}
@@ -161,7 +190,7 @@ impl RpcHandler {
161190
}
162191

163192
fn cache_mrs(&self, key: &str, mrs: &Mrs) -> Result<()> {
164-
let path = self.state.config.image.cache_dir.join("computed").join(key);
193+
let path = self.mr_cache_dir().join(key);
165194
fs::create_dir_all(path.parent().unwrap()).context("Failed to create cache directory")?;
166195
safe_write::safe_write(
167196
&path,
@@ -194,7 +223,7 @@ impl RpcHandler {
194223
}
195224

196225
// Create a directory for the image if it doesn't exist
197-
let image_dir = self.state.config.image.cache_dir.join(&hex_os_image_hash);
226+
let image_dir = self.image_cache_dir().join(&hex_os_image_hash);
198227
// Check if metadata.json exists, if not download the image
199228
let metadata_path = image_dir.join("metadata.json");
200229
if !metadata_path.exists() {
@@ -253,7 +282,7 @@ impl RpcHandler {
253282
.replace("{OS_IMAGE_HASH}", hex_os_image_hash);
254283

255284
// Create a temporary directory for extraction within the cache directory
256-
let cache_dir = self.state.config.image.cache_dir.join("tmp");
285+
let cache_dir = self.image_cache_dir().join("tmp");
257286
fs::create_dir_all(&cache_dir).context("Failed to create cache directory")?;
258287
let auto_delete_temp_dir = tempfile::Builder::new()
259288
.prefix("tmp-download-")
@@ -578,6 +607,18 @@ impl KmsRpc for RpcHandler {
578607
],
579608
})
580609
}
610+
611+
async fn clear_image_cache(self, request: ClearImageCacheRequest) -> Result<()> {
612+
let token_hash = sha2::Sha256::new_with_prefix(&request.token).finalize();
613+
if token_hash.as_slice() != self.state.config.admin_token_hash.as_slice() {
614+
bail!("Invalid token");
615+
}
616+
self.remove_cache_dir(&self.image_cache_dir(), &request.image_hash)
617+
.context("Failed to clear image cache")?;
618+
self.remove_cache_dir(&self.mr_cache_dir(), &request.config_hash)
619+
.context("Failed to clear MR cache")?;
620+
Ok(())
621+
}
581622
}
582623

583624
impl RpcCall<KmsState> for RpcHandler {

0 commit comments

Comments
 (0)