Skip to content

Commit

Permalink
Merge branch 'master' into 478-use-buildkit-for-container-image-builds
Browse files Browse the repository at this point in the history
  • Loading branch information
pditommaso authored May 31, 2024
2 parents a691ce7 + dd195de commit b7283ac
Show file tree
Hide file tree
Showing 34 changed files with 761 additions and 317 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ class BuildConfig {
@Value('${wave.build.reserved-words:[]}')
Set<String> reservedWords

@Value('${wave.build.record.duration:5d}')
Duration recordDuration

@PostConstruct
private void init() {
log.debug("Builder config: " +
Expand All @@ -95,6 +98,7 @@ class BuildConfig {
"status-delay=${statusDelay}; " +
"status-duration=${statusDuration}; " +
"compress-caching=$compressCaching; " +
"record-duration=${recordDuration}; " +
"cleanup=${cleanup}; ")
}

Expand Down
10 changes: 5 additions & 5 deletions src/main/groovy/io/seqera/wave/controller/BuildController.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ import io.micronaut.http.annotation.Produces
import io.micronaut.http.server.types.files.StreamedFile
import io.micronaut.scheduling.TaskExecutors
import io.micronaut.scheduling.annotation.ExecuteOn
import io.seqera.wave.api.BuildStatusResponse
import io.seqera.wave.service.builder.ContainerBuildService
import io.seqera.wave.service.logs.BuildLogService
import io.seqera.wave.service.persistence.PersistenceService
import io.seqera.wave.service.persistence.WaveBuildRecord
import jakarta.inject.Inject
import io.seqera.wave.api.BuildStatusResponse
/**
* Implements a controller for container builds
*
Expand All @@ -46,15 +46,15 @@ import io.seqera.wave.api.BuildStatusResponse
class BuildController {

@Inject
private PersistenceService persistenceService
private ContainerBuildService buildService

@Inject
@Nullable
BuildLogService logService

@Get("/v1alpha1/builds/{buildId}")
HttpResponse<WaveBuildRecord> getBuildRecord(String buildId){
final record = persistenceService.loadBuild(buildId)
final record = buildService.getBuildRecord(buildId)
return record
? HttpResponse.ok(record)
: HttpResponse.<WaveBuildRecord>notFound()
Expand All @@ -73,7 +73,7 @@ class BuildController {

@Get("/v1alpha1/builds/{buildId}/status")
HttpResponse<BuildStatusResponse> getBuildStatus(String buildId){
final build = persistenceService.loadBuild(buildId)
final build = buildService.getBuildRecord(buildId)
build != null
? HttpResponse.ok(build.toStatusResponse())
: HttpResponse.<BuildStatusResponse>notFound()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,28 +65,25 @@ class MetricsController {
@Get(uri = "/v1alpha2/metrics/builds", produces = MediaType.APPLICATION_JSON)
HttpResponse<?> getBuildsMetrics(@Nullable @QueryValue String date, @Nullable @QueryValue String org) {
if(!date && !org)
return HttpResponse.ok(metricsService.getOrgCount(MetricConstants.PREFIX_BUILDS))
return HttpResponse.ok(metricsService.getAllOrgCount(MetricConstants.PREFIX_BUILDS))
validateQueryParams(date)
final count = metricsService.getBuildsMetrics(date, org)
return HttpResponse.ok(new GetBuildsCountResponse(count))
return HttpResponse.ok(metricsService.getOrgCount(MetricConstants.PREFIX_BUILDS, date, org))
}

@Get(uri = "/v1alpha2/metrics/pulls", produces = MediaType.APPLICATION_JSON)
HttpResponse<?> getPullsMetrics(@Nullable @QueryValue String date, @Nullable @QueryValue String org) {
if(!date && !org)
return HttpResponse.ok(metricsService.getOrgCount(MetricConstants.PREFIX_PULLS))
return HttpResponse.ok(metricsService.getAllOrgCount(MetricConstants.PREFIX_PULLS))
validateQueryParams(date)
final count = metricsService.getPullsMetrics(date, org)
return HttpResponse.ok(new GetPullsCountResponse(count))
return HttpResponse.ok(metricsService.getOrgCount(MetricConstants.PREFIX_PULLS, date, org))
}

@Get(uri = "/v1alpha2/metrics/fusion/pulls", produces = MediaType.APPLICATION_JSON)
HttpResponse<?> getFusionPullsMetrics(@Nullable @QueryValue String date, @Nullable @QueryValue String org) {
if(!date && !org)
return HttpResponse.ok(metricsService.getOrgCount(MetricConstants.PREFIX_FUSION))
return HttpResponse.ok(metricsService.getAllOrgCount(MetricConstants.PREFIX_FUSION))
validateQueryParams(date)
final count = metricsService.getFusionPullsMetrics(date, org)
return HttpResponse.ok(new GetFusionPullsCountResponse(count))
return HttpResponse.ok(metricsService.getOrgCount(MetricConstants.PREFIX_FUSION, date, org))

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,8 +317,9 @@ class RegistryProxyController {
}

MutableHttpResponse<?> fromDownloadResponse(final DelegateResponse resp, RoutePath route, Map<String, List<String>> headers) {
final blobCache = blobCacheService .retrieveBlobCache(route, headers)
log.debug "Blob cache $blobCache"
log.debug "== Blob cache upstream $resp"
final blobCache = blobCacheService .retrieveBlobCache(route, headers, resp.headers)
log.debug "== Blob cache response [succeeded=${blobCache.succeeded()}] $blobCache"
if( !blobCache.succeeded() ) {
final String msg = blobCache.logs ?: "Unable to cache blob ${blobCache.locationUri}"
return badRequest(msg)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import io.micronaut.scheduling.TaskExecutors
import io.micronaut.scheduling.annotation.ExecuteOn
import io.micronaut.views.View
import io.seqera.wave.exception.NotFoundException
import io.seqera.wave.service.builder.ContainerBuildService
import io.seqera.wave.service.logs.BuildLogService
import io.seqera.wave.service.persistence.PersistenceService
import io.seqera.wave.service.persistence.WaveBuildRecord
Expand All @@ -53,14 +54,17 @@ class ViewController {
@Inject
private PersistenceService persistenceService

@Inject
private ContainerBuildService buildService

@Inject
@Nullable
private BuildLogService buildLogService

@View("build-view")
@Get('/builds/{buildId}')
HttpResponse<Map<String,String>> viewBuild(String buildId) {
final record = persistenceService.loadBuild(buildId)
final record = buildService.getBuildRecord(buildId)
if( !record )
throw new NotFoundException("Unknown build id '$buildId'")
return HttpResponse.ok(renderBuildView(record))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package io.seqera.wave.core

import groovy.transform.CompileStatic
import groovy.transform.ToString
import groovy.util.logging.Slf4j
import io.micronaut.cache.annotation.Cacheable
import io.micronaut.context.annotation.Context
Expand Down Expand Up @@ -220,6 +221,7 @@ class RegistryProxyService {
: null
}

@ToString(includeNames = true, includePackage = false)
static class DelegateResponse {
int statusCode
Map<String,List<String>> headers
Expand Down
69 changes: 53 additions & 16 deletions src/main/groovy/io/seqera/wave/service/blob/BlobCacheInfo.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ import groovy.transform.Canonical
import groovy.transform.CompileStatic
import groovy.transform.Memoized
import groovy.transform.ToString
import groovy.util.logging.Slf4j

/**
* Model a blob cache metadata entry
*
* @author Paolo Di Tommaso <[email protected]>
*/
@Slf4j
@ToString(includePackage = false, includeNames = true)
@Canonical
@CompileStatic
Expand All @@ -43,6 +46,21 @@ class BlobCacheInfo {
*/
final Map<String,String> headers

/**
* The blob length
*/
final Long contentLength

/**
* The content type of this blob
*/
final String contentType

/**
* The blob cache control directive
*/
final String cacheControl

/**
* The instant when the cache request was created
*/
Expand Down Expand Up @@ -73,29 +91,37 @@ class BlobCacheInfo {
locationUri && completionTime!=null
}

String getContentType() {
headers?.find(it-> it.key.toLowerCase()=='content-type')?.value
}

String getCacheControl() {
headers?.find(it-> it.key.toLowerCase()=='cache-control')?.value
}

static BlobCacheInfo create(String locationUrl, Map<String,List<String>> headers) {
static BlobCacheInfo create(String locationUrl, Map<String,List<String>> request, Map<String,List<String>> response) {
final headers0 = new LinkedHashMap<String,String>()
for( Map.Entry<String,List<String>> it : headers )
for( Map.Entry<String,List<String>> it : request )
headers0.put( it.key, it.value.join(',') )
new BlobCacheInfo(locationUrl, headers0, Instant.now())
final length = headerLong0(response, 'Content-Length')
final type = headerString0(response, 'Content-Type')
final cache = headerString0(response, 'Cache-Control')
new BlobCacheInfo(locationUrl, headers0, length, type, cache, Instant.now(), null, null, null)
}

static String headerString0(Map<String,List<String>> headers, String name) {
headers?.find(it-> it.key.toLowerCase()==name.toLowerCase())?.value?.first()
}

static BlobCacheInfo create1(String locationUrl, Map<String,String> headers) {
new BlobCacheInfo(locationUrl, headers, Instant.now())
static Long headerLong0(Map<String,List<String>> headers, String name) {
try {
return headerString0(headers,name) as Long
}
catch (NumberFormatException e) {
log.warn "Unexpected content length value - cause: $e"
return null
}
}

BlobCacheInfo cached() {
new BlobCacheInfo(
locationUri,
headers,
contentLength,
contentType,
cacheControl,
creationTime,
creationTime,
0)
Expand All @@ -105,6 +131,9 @@ class BlobCacheInfo {
new BlobCacheInfo(
locationUri,
headers,
contentLength,
contentType,
cacheControl,
creationTime,
Instant.now(),
status,
Expand All @@ -115,25 +144,33 @@ class BlobCacheInfo {
new BlobCacheInfo(
locationUri,
headers,
contentLength,
contentType,
cacheControl,
creationTime,
Instant.now(),
null,
logs)
logs
)
}

BlobCacheInfo withLocation(String uri) {
new BlobCacheInfo(
uri,
headers,
contentLength,
contentType,
cacheControl,
creationTime,
completionTime,
exitStatus,
logs)
logs
)
}

@Memoized
static BlobCacheInfo unknown() {
new BlobCacheInfo(null, null, Instant.ofEpochMilli(0), Instant.ofEpochMilli(0), null) {
new BlobCacheInfo(null, null, null, null, null, Instant.ofEpochMilli(0), Instant.ofEpochMilli(0), null) {
@Override
BlobCacheInfo withLocation(String uri) {
// prevent the change of location for unknown status
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ interface BlobCacheService {
* information.
*
* @param route The HTTP request of a container layer blob
* @param headers The HTTP headers of a container layer blob
* @param requestHeaders The HTTP headers of the upstream request
* @param responseHeaders The HTTP headers of the response providing the blob to be cached
* @return
*/
BlobCacheInfo retrieveBlobCache(RoutePath route, Map<String,List<String>> headers)
BlobCacheInfo retrieveBlobCache(RoutePath route, Map<String,List<String>> requestHeaders, Map<String,List<String>> responseHeaders)

}
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,11 @@ class BlobCacheServiceImpl implements BlobCacheService {
}

@Override
BlobCacheInfo retrieveBlobCache(RoutePath route, Map<String,List<String>> headers) {
BlobCacheInfo retrieveBlobCache(RoutePath route, Map<String,List<String>> requestHeaders, Map<String,List<String>> responseHeaders) {
final uri = blobDownloadUri(route)
log.trace "Container blob download uri: $uri"

final info = BlobCacheInfo.create(uri, headers)
final info = BlobCacheInfo.create(uri, requestHeaders, responseHeaders)
final target = route.targetPath
if( blobStore.storeIfAbsent(target, info) ) {
// start download and caching job
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,17 @@ package io.seqera.wave.service.builder

import java.util.concurrent.CompletableFuture

import groovy.transform.CompileStatic
import io.micronaut.runtime.event.annotation.EventListener
import io.seqera.wave.core.RoutePath
import io.seqera.wave.service.persistence.WaveBuildRecord

/**
* Declare container build service interface
*
* @author Paolo Di Tommaso <[email protected]>
*/
@CompileStatic
interface ContainerBuildService {

/**
Expand Down Expand Up @@ -65,4 +70,66 @@ interface ContainerBuildService {
: null
}


// **************************************************************
// ** build record operations
// **************************************************************

@EventListener
default void onBuildEvent(BuildEvent event) {
saveBuildRecord(event)
}

/**
* Store a build record for the given {@link BuildRequest} object.
*
* This method is expected to store the build record associated with the request
* *only* in the short term store caching system, ie. without hitting the
* long-term SurrealDB storage
*
* @param request The build request that needs to be storage
*/
default void createBuildRecord(BuildRequest request) {
final record0 = WaveBuildRecord.fromEvent(new BuildEvent(request))
createBuildRecord(record0.buildId, record0)
}

/**
* Store the build record associated with the specified event both in the
* short-term cache (redis) and long-term persistence layer (surrealdb)
*
* @param event The {@link BuildEvent} object for which the build record needs to be stored
*/
default void saveBuildRecord(BuildEvent event) {
final record0 = WaveBuildRecord.fromEvent(event)
saveBuildRecord(record0.buildId, record0)
}

/**
* Store a build record object.
*
* This method is expected to store the build record *only* in the short term store cache (redis),
* ie. without hitting the long-term storage (surrealdb)
*
* @param buildId The Id of the build record
* @param value The {@link WaveBuildRecord} to be stored
*/
void createBuildRecord(String buildId, WaveBuildRecord value)

/**
* Store the specified build record both in the short-term cache (redis)
* and long-term persistence layer (surrealdb)
*
* @param buildId The Id of the build record
* @param value The {@link WaveBuildRecord} to be stored
*/
void saveBuildRecord(String buildId, WaveBuildRecord value)

/**
* Retrieve the build record for the specified id.
*
* @param buildId The ID of the build record to be retrieve
* @return The {@link WaveBuildRecord} associated with the corresponding Id, or {@code null} if it cannot be found
*/
WaveBuildRecord getBuildRecord(String buildId)
}
Loading

0 comments on commit b7283ac

Please sign in to comment.