Skip to content
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
@@ -0,0 +1,107 @@
/*
* Wave, containers provisioning service
* Copyright (c) 2023-2024, Seqera Labs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package io.seqera.wave.configuration

import java.time.Duration

import groovy.transform.ToString
import io.micronaut.context.annotation.Value
import io.seqera.wave.util.DurationUtils
import jakarta.inject.Inject
import jakarta.inject.Singleton
/**
* Configuration to be used by {@link io.seqera.wave.service.request.ContainerRequestService}
*
* @author: Paolo Di Tommaso <paolo.ditommaso@gmail.com>
*
*/
@ToString(includePackage = false, includeNames = true)
@Singleton
class ContainerRequestConfig {

@Inject
Cache cache

@Inject
Watcher watcher

/**
* Model container token caching configuration settings
*/
@ToString(includePackage = false, includeNames = true)
@Singleton
static class Cache {

/**
* The default duration of a container request time-to-live.
* This determines how long a container token is valid, and
* therefore an ephemeral container can be accessed.
*/
@Value('${wave.tokens.cache.duration:3h}')
Duration duration

/**
* The maximum duration of a container request time-to-live.
* This determines how long a container token is valid, and
* therefore an ephemeral container can be accessed.
*/
@Value('${wave.tokens.cache.max-duration:2d}')
Duration maxDuration

/**
* This method returns the period of time between two consecutive check events.
* The interval determines how frequently a refresh operation is triggered.
* A shorter interval means more frequent checks, while a longer interval reduces checks frequency.
*/
@Value('${wave.tokens.cache.check-interval:30h}')
Duration checkInterval

}

/**
* Model container request watcher configuration settings
*/
@ToString(includePackage = false, includeNames = true)
static class Watcher {

/**
* Determine the delay after which the container request watcher service is run
*/
@Value('${wave.tokens.watcher.interval:10s}')
Duration interval

/**
* Determine the delay after which the watcher service is launched after the bootstrap
*/
@Value('${wave.tokens.watcher.delay:5s}')
Duration delay

/**
* Determine the number of container requests that are processed in watcher cycle
*/
@Value('${wave.tokens.watcher.count:250}')
int count

Duration getDelayRandomized() {
DurationUtils.randomDuration(getDelay(), 0.4f)
}

}

}
10 changes: 6 additions & 4 deletions src/main/groovy/io/seqera/wave/controller/ViewController.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ import io.seqera.wave.service.inspect.ContainerInspectService
import io.seqera.wave.service.logs.BuildLogService
import io.seqera.wave.service.mirror.ContainerMirrorService
import io.seqera.wave.service.mirror.MirrorResult
import io.seqera.wave.service.persistence.PersistenceService
import io.seqera.wave.service.persistence.WaveBuildRecord
import io.seqera.wave.service.persistence.WaveScanRecord
import io.seqera.wave.service.request.ContainerRequestService
import io.seqera.wave.service.scan.ContainerScanService
import io.seqera.wave.service.scan.ScanEntry
import io.seqera.wave.service.scan.ScanType
Expand Down Expand Up @@ -82,7 +82,7 @@ class ViewController {
private String serverUrl

@Inject
private PersistenceService persistenceService
private ContainerRequestService containerService

@Inject
@Nullable
Expand Down Expand Up @@ -275,8 +275,10 @@ class ViewController {

@View("container-view")
@Get('/containers/{token}')
HttpResponse viewContainer(String token) {
final data = persistenceService.loadContainerRequest(token)
HttpResponse<?> viewContainer(String token) {
final data = containerService.loadContainerRecord(token)
if( !data )
throw new NotFoundException("Unknown container token: $token")
// return the response
final binding = new HashMap(20)
if( !data )
Expand Down
11 changes: 11 additions & 0 deletions src/main/groovy/io/seqera/wave/encoder/DateTimeAdapter.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package io.seqera.wave.encoder

import java.time.Duration
import java.time.Instant
import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter

import com.squareup.moshi.FromJson
Expand Down Expand Up @@ -58,4 +59,14 @@ class DateTimeAdapter {
final val0 = value.contains('.') ? Math.round(value.toDouble() * 1_000_000_000) : value.toLong()
return value != null ? Duration.ofNanos(val0) : null
}

@ToJson
String serializeOffsetDateTime(OffsetDateTime value) {
return value.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
}

@FromJson
OffsetDateTime deserializeOffsetDateTime(String value) {
return OffsetDateTime.parse(value, DateTimeFormatter.ISO_OFFSET_DATE_TIME)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,134 +48,134 @@ class WaveContainerRecord {
* This is container token and it is named as id for surrealdb requirement
*/
@PostgresIgnore
final String id
String id

/**
* The Tower user associated with the request
*/
final User user
User user

/**
* The Tower workspace associated with the request
*/
final Long workspaceId
Long workspaceId

/**
* The container image requested. this can be null null when a build request was submitted
*/
final String containerImage
String containerImage

/**
* The container file (aka Dockerfile) content associated with the request
*/
final String containerFile
String containerFile

/**
* The container config associated with the request
*/
final ContainerConfig containerConfig
ContainerConfig containerConfig

/**
* The conda file associated with the request
*/
final String condaFile
String condaFile

/**
* The container arch platform
*/
final String platform
String platform

/**
* The Tower endpoint associated with the request
*/
final String towerEndpoint
String towerEndpoint

/**
* The repository where the build image is uploaded
*/
final String buildRepository
String buildRepository

/**
* The repository where container layers are cached
*/
final String cacheRepository
String cacheRepository

/**
* The request fingerprint
*/
final String fingerprint
String fingerprint

/**
* The request timestamp
*/
final Instant timestamp
Instant timestamp

/**
* The time zone id where the request was originated
*/
final String zoneId
String zoneId

/**
* The IP address originating the request
*/
final String ipAddress
String ipAddress

/**
* The container image associated with this Wave container, it can be the container image
* as provide by the user, or a container image built by Wave
*/
final String sourceImage
String sourceImage

/**
* The container SHA256 digest of the container image associated with this request
*/
final String sourceDigest
String sourceDigest

/**
* The resulting Wave container image name
*/
final String waveImage
String waveImage

/**
* The resulting Wave container image digest
*/
final String waveDigest
String waveDigest

/**
* The timestamp of the Wave container expiration
*/
final Instant expiration
Instant expiration

/**
* The ID of the build if the Wave request triggered a container build, null otherwise
*/
final String buildId
String buildId

/**
* Whenever a new build was triggered for this Wave request, or the container was built by a previous request
*/
final Boolean buildNew
Boolean buildNew

/**
* Whenever the request is a Wave container freeze
*/
final Boolean freeze
Boolean freeze

/**
* Whenever the request is for container with fusion
*/
@JsonDeserialize(using = FusionVersionStringDeserializer.class)
final String fusionVersion
String fusionVersion

/**
* Whenever it's a "mirror" build request
*/
final Boolean mirror
Boolean mirror

/**
* The scan id associated with this request
*/
final String scanId
String scanId

WaveContainerRecord(SubmitContainerTokenRequest request, ContainerRequest data, String waveImage, String addr, Instant expiration) {
this.id = data.requestId
Expand Down
Loading