Skip to content
Merged
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
6 changes: 6 additions & 0 deletions src/main/groovy/io/seqera/wave/core/RouteHandler.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ class RouteHandler {
if( !request ) {
throw new NotFoundException("Unknown Wave container for token '$token'")
}
// freeze and mirror requests publish the image to the target registry and are
// meant to be pulled directly from there - they must not be served via the Wave
// proxy token path (see also ContainerRequest.durable)
if( request.durable() ) {
throw new NotFoundException("Container '$token' is a freeze/mirror image and must be pulled directly from '${request.containerImage}'")
}
// the image name (without tag) must match
final coords = request.coordinates()
if( image != coords.image )
Expand Down
4 changes: 3 additions & 1 deletion src/main/groovy/io/seqera/wave/util/ContainerHelper.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,9 @@ class ContainerHelper {
}

static SubmitContainerTokenResponse makeResponseV1(ContainerRequest data, TokenData token, String waveImage) {
final target = waveImage
// freeze/mirror images are published to the target registry and must be pulled
// directly from there - the Wave proxy token path does not serve them (see RouteHandler)
final target = data.durable() ? data.containerImage : waveImage
final build = data.buildNew ? data.buildId : null
return new SubmitContainerTokenResponse(data.requestId, token.value, target, token.expiration, data.containerImage, build, null, null, null, null, null)
}
Expand Down
20 changes: 20 additions & 0 deletions src/test/groovy/io/seqera/wave/core/RouteHandlerTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,24 @@ class RouteHandlerTest extends Specification {

}

@Unroll
def 'should reject proxy pull for durable freeze/mirror requests' () {
given:
def tokenService = Mock(ContainerRequestService)

when:
new RouteHandler(tokenService).parse(REQ_PATH)
then:
1 * tokenService.getRequest(TOKEN) >> REQUEST
and:
def e = thrown(NotFoundException)
// the error must point the caller to the direct image reference
e.message.contains(IMAGE)

where:
REQ_PATH | TOKEN | IMAGE | REQUEST
'/v2/wt/a1/library/ubuntu/manifests/latest' | 'a1' | 'quay.io/org/ubuntu:1.0' | ContainerRequest.of(containerImage: 'quay.io/org/ubuntu:1.0', freeze: true)
'/v2/wt/b2/library/ubuntu/blobs/latest' | 'b2' | 'quay.io/org/ubuntu:1.0' | ContainerRequest.of(containerImage: 'quay.io/org/ubuntu:1.0', type: ContainerRequest.Type.Mirror)
}

}
26 changes: 25 additions & 1 deletion src/test/groovy/io/seqera/wave/util/ContainerHelperTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -253,12 +253,36 @@ class ContainerHelperTest extends Specification {
result.cached == null
result.freeze == null

where:
where:
NEW_BUILD | EXPECTED_BUILD_ID
false | null
true | '123'
}

@Unroll
def 'should create response v1 for durable request returning the direct image' () {
given:
def data = ContainerRequest.of(
containerImage: 'quay.io/org/container:1.0',
freeze: IS_FREEZE,
type: TYPE )
def token = new TokenData('123abc', Instant.now().plusSeconds(100))
def target = 'wave.com/wt/123abc/org/container:1.0'

when:
def result = ContainerHelper.makeResponseV1(data, token, target)
then:
// freeze/mirror images must resolve to the direct target-registry image,
// not the Wave proxy token url (which no longer serves durable images)
result.targetImage == 'quay.io/org/container:1.0'
result.containerImage == 'quay.io/org/container:1.0'

where:
TYPE | IS_FREEZE
null | true
ContainerRequest.Type.Mirror | false
}

@Unroll
def 'should create response v2' () {
given:
Expand Down
Loading