Skip to content

Enable Cloud Storage Prefix for Remote Build Cache #68

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,10 @@ fun BuildCacheKey.blobKey(): String {
// a single `/`.
return hashCode.replace(slashes, "/")
}

fun String.withPrefix(prefix: String?): String {
if (prefix.isNullOrBlank()) return this

val sanitizedPrefix = prefix.trimEnd('/', '\\')
return "$sanitizedPrefix/$this"
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,20 @@ import java.nio.file.Files
*/
class FileSystemStorageService(
override val bucketName: String,
private val prefix: String?,
override val isPush: Boolean,
override val isEnabled: Boolean
) : StorageService {

private val location = Files.createTempDirectory("tmp$bucketName").toFile()
private val location: File = createTempDirectory()

private fun createTempDirectory(): File {
val baseDir = prefix?.takeIf { it.isNotBlank() }?.let {
File(it).apply { if (!exists()) mkdirs() }
} ?: File(System.getProperty(JAVA_IO_TMPDIR))

return Files.createTempDirectory(baseDir.toPath(), "tmp$bucketName").toFile()
}

override fun load(cacheKey: String): InputStream? {
if (!isEnabled) {
Expand Down Expand Up @@ -84,6 +93,8 @@ class FileSystemStorageService(
}

companion object {
private const val JAVA_IO_TMPDIR = "java.io.tmpdir"

private fun File.deleteRecursively() {
val files = listFiles()
for (file in files) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ abstract class RemoteGradleBuildCache : AbstractBuildCache() {
*/
lateinit var bucketName: String

/**
* The prefix to use when storing cache entries in the bucket.
* It becomes new root for all cache entries.
* If not specified, the cache entries will be stored at the root of the bucket.
*/
lateinit var prefix: String

/**
* The type of credentials to use to connect to authenticate to your project instance.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,23 @@ class FileStorageServiceTest {
fun testStoreBlob() {
val storageService = FileSystemStorageService(
bucketName = BUCKET_NAME,
prefix = null,
isPush = true,
isEnabled = true
)
storageService.use {
val cacheKey = "test-store.txt"
val contents = "The quick brown fox jumped over the lazy dog"
val result = storageService.store(cacheKey, contents.toByteArray(Charsets.UTF_8))
assert(result)
}
}

@Test
fun testStoreBlob_withPrefix() {
val storageService = FileSystemStorageService(
bucketName = BUCKET_NAME,
prefix = "test-prefix",
isPush = true,
isEnabled = true
)
Expand All @@ -39,6 +56,7 @@ class FileStorageServiceTest {
fun testLoadBlob() {
val storageService = FileSystemStorageService(
bucketName = BUCKET_NAME,
prefix = null,
isPush = true,
isEnabled = true
)
Expand All @@ -57,6 +75,7 @@ class FileStorageServiceTest {
fun testStoreBlob_noPushSupport() {
val storageService = FileSystemStorageService(
bucketName = BUCKET_NAME,
prefix = null,
isPush = false,
isEnabled = true
)
Expand All @@ -72,6 +91,7 @@ class FileStorageServiceTest {
fun testStoreBlob_disabled() {
val storageService = FileSystemStorageService(
bucketName = BUCKET_NAME,
prefix = null,
isPush = true,
isEnabled = false
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package androidx.build.gradle.gcpbuildcache

import androidx.build.gradle.core.FileSystemStorageService
import androidx.build.gradle.core.blobKey
import androidx.build.gradle.core.withPrefix
import org.gradle.api.logging.Logging
import org.gradle.caching.BuildCacheEntryReader
import org.gradle.caching.BuildCacheEntryWriter
Expand All @@ -37,6 +38,7 @@ import java.io.ByteArrayOutputStream
internal class GcpBuildCacheService(
private val projectId: String,
private val bucketName: String,
private val prefix: String?,
gcpCredentials: GcpCredentials,
messageOnAuthenticationFailure: String,
isPush: Boolean,
Expand All @@ -46,9 +48,21 @@ internal class GcpBuildCacheService(

private val storageService = if (inTestMode) {
// Use an implementation backed by the File System when in test mode.
FileSystemStorageService(bucketName, isPush, isEnabled)
FileSystemStorageService(
bucketName = bucketName,
prefix = prefix,
isPush = isPush,
isEnabled = isEnabled,
)
} else {
GcpStorageService(projectId, bucketName, gcpCredentials, messageOnAuthenticationFailure, isPush, isEnabled)
GcpStorageService(
projectId = projectId,
bucketName = bucketName,
gcpCredentials = gcpCredentials,
messageOnAuthenticationFailure = messageOnAuthenticationFailure,
isPush = isPush,
isEnabled = isEnabled
)
}

override fun close() {
Expand All @@ -57,7 +71,9 @@ internal class GcpBuildCacheService(

override fun load(key: BuildCacheKey, reader: BuildCacheEntryReader): Boolean {
logger.info("Loading ${key.blobKey()}")
val cacheKey = key.blobKey()
val cacheKey = key
.blobKey()
.apply { if (prefix != null) withPrefix(prefix) }
val input = storageService.load(cacheKey) ?: return false
reader.readFrom(input)
return true
Expand All @@ -66,7 +82,9 @@ internal class GcpBuildCacheService(
override fun store(key: BuildCacheKey, writer: BuildCacheEntryWriter) {
if (writer.size == 0L) return // do not store empty entries into the cache
logger.info("Storing ${key.blobKey()}")
val cacheKey = key.blobKey()
val cacheKey = key
.blobKey()
.apply { if (prefix != null) withPrefix(prefix) }
val output = ByteArrayOutputStream()
output.use {
writer.writeTo(output)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class GcpBuildCacheServiceFactory : BuildCacheServiceFactory<GcpBuildCache> {
.type("GCP-backed")
.config("projectId", buildCache.projectId)
.config("bucketName", buildCache.bucketName)
.config("prefix", buildCache.prefix)
.config("isPushSupported", "${buildCache.isPush}")
.config("isEnabled", "${buildCache.isEnabled}")
.config(
Expand All @@ -40,12 +41,13 @@ class GcpBuildCacheServiceFactory : BuildCacheServiceFactory<GcpBuildCache> {
)

val service = GcpBuildCacheService(
buildCache.projectId,
buildCache.bucketName,
buildCache.credentials,
buildCache.messageOnAuthenticationFailure,
buildCache.isPush,
buildCache.isEnabled
projectId = buildCache.projectId,
bucketName = buildCache.bucketName,
prefix = buildCache.prefix,
gcpCredentials = buildCache.credentials,
messageOnAuthenticationFailure = buildCache.messageOnAuthenticationFailure,
isPush = buildCache.isPush,
isEnabled = buildCache.isEnabled
)
service.validateConfiguration()
return service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package androidx.build.gradle.s3buildcache

import androidx.build.gradle.core.FileSystemStorageService
import androidx.build.gradle.core.blobKey
import androidx.build.gradle.core.withPrefix
import org.gradle.api.logging.Logging
import org.gradle.caching.BuildCacheEntryReader
import org.gradle.caching.BuildCacheEntryWriter
Expand All @@ -42,6 +43,7 @@ class S3BuildCacheService(
credentials: S3Credentials,
region: String,
bucketName: String,
private val prefix: String?,
isPush: Boolean,
isEnabled: Boolean,
reducedRedundancy: Boolean,
Expand All @@ -52,23 +54,39 @@ class S3BuildCacheService(
clientOptions(credentials(credentials), region)
}
private val storageService = if (inTestMode) {
FileSystemStorageService(bucketName, isPush, isEnabled)
FileSystemStorageService(
bucketName = bucketName,
prefix = prefix,
isPush = isPush,
isEnabled = isEnabled,
)
} else {
S3StorageService(bucketName, isPush, isEnabled, client, region, reducedRedundancy)
S3StorageService(
bucketName = bucketName,
isPush = isPush,
isEnabled = isEnabled,
client = client,
region = region,
reducedRedundancy = reducedRedundancy
)
}

override fun load(key: BuildCacheKey, reader: BuildCacheEntryReader): Boolean {
logger.info("Loading ${key.blobKey()}")
val cacheKey = key.blobKey()
val cacheKey = key
.blobKey()
.apply { if (prefix != null) withPrefix(prefix) }
val input = storageService.load(cacheKey) ?: return false
reader.readFrom(input)
return true
}

override fun store(key: BuildCacheKey, writer: BuildCacheEntryWriter) {
if (writer.size == 0L) return // do not store empty entries into the cache
val cacheKey = key
.blobKey()
.apply { if (prefix != null) withPrefix(prefix) }
logger.info("Storing ${key.blobKey()}")
val cacheKey = key.blobKey()
val output = ByteArrayOutputStream()
output.use {
writer.writeTo(output)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class S3BuildCacheServiceFactory : BuildCacheServiceFactory<S3BuildCache> {
.type("AWS-S3-backed")
.config("region", buildCache.region)
.config("bucketName", buildCache.bucketName)
.config("prefix", buildCache.prefix)
.config("reducedRedundancy", "${buildCache.reducedRedundancy}")
.config("isPushSupported", "${buildCache.isPush}")
.config("isEnabled", "${buildCache.isEnabled}")
Expand All @@ -41,6 +42,7 @@ class S3BuildCacheServiceFactory : BuildCacheServiceFactory<S3BuildCache> {
val service = S3BuildCacheService(
region = buildCache.region,
bucketName = buildCache.bucketName,
prefix = buildCache.prefix,
isPush = buildCache.isPush,
isEnabled = buildCache.isEnabled,
reducedRedundancy = buildCache.reducedRedundancy,
Expand Down