Skip to content

Commit 59075b2

Browse files
committed
feat: basic configured cache use & test
1 parent 9ccf704 commit 59075b2

File tree

6 files changed

+76
-27
lines changed

6 files changed

+76
-27
lines changed

src/lake/Lake/CLI/Main.lean

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ def LakeOptions.computeEnv (opts : LakeOptions) : EIO CliError Lake.Env := do
104104
Env.compute (← opts.getLakeInstall) (← opts.getLeanInstall) opts.elanInstall?
105105
opts.noCache |>.adapt fun msg => .invalidEnv msg
106106

107-
/-- Make a `LoadConfig` from a `LakeOptions`. -/
108107
def LakeOptions.mkLoadConfigCore
109108
(opts : LakeOptions) (wsDir : FilePath) (lakeEnv : Lake.Env) (lakeConfig : LakeConfig)
110109
: LoadConfig where
@@ -120,7 +119,6 @@ def LakeOptions.mkLoadConfigCore
120119
updateDeps := opts.updateDeps
121120
updateToolchain := opts.updateToolchain
122121

123-
/-- Make a `LoadConfig` from a `LakeOptions`. -/
124122
def LakeOptions.mkLoadConfig' (opts : LakeOptions) : LogIO LoadConfig := do
125123
let some wsDir ← resolvePath? opts.rootDir
126124
| error <| toString <| CliError.missingRootDir opts.rootDir
@@ -399,6 +397,14 @@ namespace lake
399397

400398
namespace cache
401399

400+
def serviceNotFound (service : String) (configuredServices : Array CacheService) : String :=
401+
let msg := s!"service `{service}` not found in system configuration"
402+
if configuredServices.isEmpty then
403+
s!"{msg}; no services configured"
404+
else
405+
let msg := s!"{msg}; configured services:\n"
406+
configuredServices.foldl (· ++ s!" {·.name?}") msg
407+
402408
@[inline] private def cacheToolchain (pkg : Package) (toolchain : String) : String :=
403409
if pkg.bootstrap then "" else toolchain
404410

@@ -431,15 +437,20 @@ protected def get : CliM PUnit := do
431437
let platform := opts.platform?.getD System.Platform.target
432438
let toolchain := opts.toolchain?.getD ws.lakeEnv.toolchain
433439
let service : CacheService ← id do
434-
match ws.lakeEnv.cacheArtifactEndpoint?, ws.lakeEnv.cacheRevisionEndpoint? with
435-
| some artifactEndpoint, some revisionEndpoint =>
436-
return .downloadService artifactEndpoint revisionEndpoint ws.lakeEnv.cacheService?
437-
| none, none =>
438-
return .reservoirService ws.lakeEnv.reservoirApiUrl
439-
| some artifactEndpoint, none =>
440-
error (invalidEndpointConfig artifactEndpoint "")
441-
| none, some revisionEndpoint =>
442-
error (invalidEndpointConfig "" revisionEndpoint)
440+
if let some service := opts.service? then
441+
let some service := ws.lakeConfig.cache.services.find? (·.name? == some service)
442+
| error (serviceNotFound service ws.lakeConfig.cache.services)
443+
return service
444+
else
445+
match ws.lakeEnv.cacheArtifactEndpoint?, ws.lakeEnv.cacheRevisionEndpoint? with
446+
| some artifactEndpoint, some revisionEndpoint =>
447+
return .downloadService artifactEndpoint revisionEndpoint ws.lakeEnv.cacheService?
448+
| none, none =>
449+
return .reservoirService ws.lakeEnv.reservoirApiUrl
450+
| some artifactEndpoint, none =>
451+
error (invalidEndpointConfig artifactEndpoint "")
452+
| none, some revisionEndpoint =>
453+
error (invalidEndpointConfig "" revisionEndpoint)
443454
if let some remoteScope := opts.scope? then
444455
if !opts.repoScope && service.isReservoir then
445456
-- `--scope` with Reservoir would imply downloading artifacts for a different package.
@@ -516,11 +527,18 @@ protected def put : CliM PUnit := do
516527
let platform := cachePlatform pkg (opts.platform?.getD System.Platform.target)
517528
let toolchain := cacheToolchain pkg (opts.toolchain?.getD ws.lakeEnv.toolchain)
518529
let service : CacheService ← id do
519-
match ws.lakeEnv.cacheKey?, ws.lakeEnv.cacheArtifactEndpoint?, ws.lakeEnv.cacheRevisionEndpoint? with
520-
| some key, some artifactEndpoint, some revisionEndpoint =>
521-
return .uploadService key artifactEndpoint revisionEndpoint
522-
| key?, artifactEndpoint?, revisionEndpoint? =>
523-
error (invalidEndpointConfig key? artifactEndpoint? revisionEndpoint?)
530+
if let some service := opts.service? then
531+
let some service := ws.lakeConfig.cache.services.find? (·.name? == some service)
532+
| error (serviceNotFound service ws.lakeConfig.cache.services)
533+
let some key := ws.lakeEnv.cacheKey?
534+
| error "uploads require an authentication key configured through `LAKE_CACHE_KEY`"
535+
return service.withKey key
536+
else
537+
match ws.lakeEnv.cacheKey?, ws.lakeEnv.cacheArtifactEndpoint?, ws.lakeEnv.cacheRevisionEndpoint? with
538+
| some key, some artifactEndpoint, some revisionEndpoint =>
539+
return .uploadService key artifactEndpoint revisionEndpoint
540+
| key?, artifactEndpoint?, revisionEndpoint? =>
541+
error (invalidEndpointConfig key? artifactEndpoint? revisionEndpoint?)
524542
let service := service.withRepoScope opts.repoScope
525543
let repo := GitRepo.mk pkg.dir
526544
if (← repo.hasDiff) then

src/lake/Lake/Config/Cache.lean

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -358,12 +358,12 @@ def uploadS3
358358
let code ← id do
359359
match (data.get? "response_code" <|> data.get? "http_code") with
360360
| .ok (some code) => return code
361-
| .ok none => error s!"curl's JSON output did not contain a response code"
362-
| .error e => error s!"curl's JSON output contained an invalid JSON response code: {e}"
361+
| .ok none => error s!"curl's JSON output did not contain a response code; JSON received:\n{out.stderr}"
362+
| .error e => error s!"curl's JSON output contained an invalid JSON response code: {e}; JSON received:\n{out.stderr}"
363363
unless code == 200 do
364364
error s!"failed to upload artifact, error {code}; received:\n{out.stdout}"
365365
| .error e =>
366-
error s!"curl produced invalid JSON output: {e}"
366+
error s!"curl produced invalid JSON output: {e}; received:\n{out.stderr}"
367367

368368
/--
369369
Configuration of a remote cache service (e.g., Reservoir or an S3 bucket).
@@ -375,7 +375,7 @@ the desired functions by using `CacheService`'s smart constructors
375375
-/
376376
public structure CacheService where
377377
private mk ::
378-
private name? : Option String := none
378+
name? : Option String := none
379379
/- S3 Bucket -/
380380
private key : String := ""
381381
private artifactEndpoint : String := ""
@@ -411,6 +411,10 @@ namespace CacheService
411411
(artifactEndpoint : String) (name? : Option String := none)
412412
: CacheService := {name?, artifactEndpoint}
413413

414+
/-- Reconfigures the cache service to use the provided key (for uploads).-/
415+
@[inline] public def withKey (service : CacheService) (key : String) : CacheService :=
416+
{service with key}
417+
414418
/--
415419
Reconfigures the cache service to interpret scopes as repositories (or not if `false`).
416420

src/lake/Lake/Config/LakeConfig.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace Lake
1616

1717
public configuration CacheConfig where
1818
defaultService : Name := .anonymous
19-
services : Array CacheService := #[]
19+
services, service : Array CacheService := #[]
2020
deriving Inhabited
2121

2222
public configuration LakeConfig where

src/lake/Lake/Load/Toml.lean

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -334,15 +334,17 @@ public protected def CacheService.decodeToml (t : Table) (ref := Syntax.missing)
334334
| "reservoir" => ensureDecode do
335335
let name ← t.tryDecode `name ref
336336
let repoScope ← t.tryDecode `repoScope ref
337-
let apiEndpoint ← t.tryDecode `artifactEndpoint ref
337+
let apiEndpoint ← normalizeUrl <$> t.tryDecode `apiEndpoint ref
338338
return .reservoirService apiEndpoint repoScope (some name)
339339
| "s3" => ensureDecode do
340340
let name ← t.tryDecode `name ref
341-
let artifactEndpoint ← t.tryDecode `artifactEndpoint ref
342-
let revisionEndpoint ← t.tryDecode `revisionEndpoint ref
341+
let artifactEndpoint ← normalizeUrl <$> t.tryDecode `artifactEndpoint ref
342+
let revisionEndpoint ← normalizeUrl <$> t.tryDecode `revisionEndpoint ref
343343
return .downloadService artifactEndpoint revisionEndpoint (some name)
344344
| _ =>
345345
throwDecodeErrorAt typeVal.ref "expected one of 'reservoir' or 's3'"
346+
where
347+
normalizeUrl url := if url.back == '/' then url.dropEnd 1 |>.copy else url
346348

347349
public instance : DecodeToml CacheService := ⟨fun v => do CacheService.decodeToml (← v.decodeTable) v.ref⟩
348350

tests/lake/tests/cache/online-test.sh

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ with_cdn_endpoints() {
3535
}
3636

3737
with_bogus_endpoints() {
38-
LAKE_CACHE_ARTIFACT_ENDPOINT=https://example.com \
39-
LAKE_CACHE_REVISION_ENDPOINT=https://example.com \
38+
LAKE_CACHE_ARTIFACT_ENDPOINT=http://example.com \
39+
LAKE_CACHE_REVISION_ENDPOINT=http://example.com \
4040
"$@"
4141
}
4242

@@ -58,6 +58,12 @@ echo "# TESTS"
5858
test_err "must contain exactly one '/'" cache get --repo='invalid'
5959
test_err 'invalid characters in repository name' cache get --repo='!/invalid'
6060

61+
# Test `--service` validation
62+
test_err 'service `bogus` not found in system configuration' \
63+
cache get --service='bogus'
64+
test_err 'service `bogus` not found in system configuration'\
65+
cache put bogus.jsonl --scope='bogus' --service='bogus'
66+
6167
# Test `cache get` command errors for bad configurations
6268
test_err 'the `--platform` and `--toolchain` options do nothing' \
6369
cache get bogus.jsonl --scope='bogus' --platform='bogus' --wfail
@@ -94,10 +100,14 @@ test_exp -f .lake/outputs.jsonl
94100
test_cmd_eq 3 wc -l < .lake/outputs.jsonl
95101
test_cmd cp -r .lake/cache .lake/cache-backup
96102

97-
# Test fetch from invalid URL
103+
# Test upload to an invalid URL
98104
with_bogus_endpoints test_err "failed to upload artifact" \
99105
cache put .lake/outputs.jsonl --scope='!/test'
100106

107+
# Test upload to an invalid URL via system configuration
108+
LAKE_CONFIG=services.toml test_err "failed to upload artifact" \
109+
cache put .lake/outputs.jsonl --scope='!/test' --service='bogus'
110+
101111
# Test cache put/get with a custom endpoint
102112
with_upload_endpoints test_run cache put .lake/outputs.jsonl --scope='!/test'
103113
test_cmd rm -rf .lake/build "$LAKE_CACHE_DIR"
@@ -117,6 +127,10 @@ test_exp -d $LAKE_CACHE_DIR/revisions/test
117127
# Test `--force-download`
118128
with_cdn_endpoints test_out "downloading" cache get --scope='!/test' --force-download
119129

130+
# Test download service configuration through system configuration
131+
LAKE_CONFIG=services.toml test_out "downloading" \
132+
cache get --scope='!/test' --force-download --service=cdn
133+
120134
# Test cache put/get with a set platform/toolchain
121135
with_upload_endpoints test_run cache put .lake/outputs.jsonl \
122136
--repo='leanprover/test' --platform=foo --toolchain=bar
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[[cache.service]]
2+
name = "cdn"
3+
type = "s3"
4+
artifactEndpoint = "https://reservoir.lean-cache.cloud/a0"
5+
revisionEndpoint = "https://reservoir.lean-cache.cloud/r0"
6+
7+
[[cache.service]]
8+
name = "bogus"
9+
type = "s3"
10+
artifactEndpoint = "http://example.com"
11+
revisionEndpoint = "http://example.com"

0 commit comments

Comments
 (0)