diff --git a/VERSION b/VERSION index 84cc52946..ec6d649be 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.18.0 +1.18.1 diff --git a/build.gradle b/build.gradle index 4114c479e..264a0501f 100644 --- a/build.gradle +++ b/build.gradle @@ -88,7 +88,7 @@ dependencies { testImplementation 'org.testcontainers:mysql:1.17.3' // -- - implementation 'ch.qos.logback:logback-classic:1.5.13' + implementation 'ch.qos.logback:logback-classic:1.5.16' // rate limit implementation 'com.coveo:spillway:3.0.0' diff --git a/changelog.txt b/changelog.txt index 291aae6fb..034fdf21a 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,10 @@ # Wave changelog +1.18.1 - 21 Feb 2025 +- Add denyHosts to pairing websocket [f5369eed] +- Use virtual threads for build, scan and mirror jobs (#742) [9dfce1f7] +- removed metrics from open-api (#798) [740831aa] +- Bump ch.qos.logback:logback-classic 1.5.16 (#799) [7bf703d6] + 1.18.0 - 18 Feb 2025 - Add Scan image request (#796) [a950d1cf] - Improve caching for AWS ECR and Auth lookup (#783) [b1a76d15] diff --git a/src/main/groovy/io/seqera/wave/service/builder/impl/BuildStateStoreImpl.groovy b/src/main/groovy/io/seqera/wave/service/builder/impl/BuildStateStoreImpl.groovy index d9562ce43..963629aaa 100644 --- a/src/main/groovy/io/seqera/wave/service/builder/impl/BuildStateStoreImpl.groovy +++ b/src/main/groovy/io/seqera/wave/service/builder/impl/BuildStateStoreImpl.groovy @@ -50,7 +50,7 @@ class BuildStateStoreImpl extends AbstractStateStore implements Buil private ExecutorService ioExecutor - BuildStateStoreImpl(StateProvider provider, BuildConfig buildConfig, @Named(TaskExecutors.IO) ExecutorService ioExecutor) { + BuildStateStoreImpl(StateProvider provider, BuildConfig buildConfig, @Named(TaskExecutors.BLOCKING) ExecutorService ioExecutor) { super(provider, new MoshiEncodeStrategy() {}) this.buildConfig = buildConfig this.ioExecutor = ioExecutor diff --git a/src/main/groovy/io/seqera/wave/service/builder/impl/ContainerBuildServiceImpl.groovy b/src/main/groovy/io/seqera/wave/service/builder/impl/ContainerBuildServiceImpl.groovy index c277c7f22..d7d98cfe3 100644 --- a/src/main/groovy/io/seqera/wave/service/builder/impl/ContainerBuildServiceImpl.groovy +++ b/src/main/groovy/io/seqera/wave/service/builder/impl/ContainerBuildServiceImpl.groovy @@ -87,7 +87,7 @@ class ContainerBuildServiceImpl implements ContainerBuildService, JobHandler denyHosts + @OnOpen void onOpen(String service, String token, String endpoint, WebSocketSession session) { log.debug "Opening pairing session - endpoint: ${endpoint} [sessionId: $session.id]" + if( isDenyHost(endpoint) ) { + log.warn "Pairing not allowed for endpoint: ${endpoint}" + session.close(CloseReason.POLICY_VIOLATION) + return + } + // check for a valid connection token if( licenseManager && !isLicenseTokenValid(token, endpoint) && closeSessionOnInvalidLicenseToken ) { session.close(CloseReason.POLICY_VIOLATION) @@ -150,4 +160,11 @@ class PairingWebSocket { return true } + protected boolean isDenyHost(String endpoint) { + for( String it : (denyHosts ?: List.of()) ) { + if( endpoint.contains(it) ) + return true + } + return false + } } diff --git a/src/main/groovy/io/seqera/wave/service/scan/ContainerScanServiceImpl.groovy b/src/main/groovy/io/seqera/wave/service/scan/ContainerScanServiceImpl.groovy index 6538dd487..f38eda5f2 100644 --- a/src/main/groovy/io/seqera/wave/service/scan/ContainerScanServiceImpl.groovy +++ b/src/main/groovy/io/seqera/wave/service/scan/ContainerScanServiceImpl.groovy @@ -64,7 +64,7 @@ class ContainerScanServiceImpl implements ContainerScanService, JobHandler200MB - %d{MMM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n >> wt=%X{requestId}%n + %d{MMM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n diff --git a/src/test/groovy/io/seqera/wave/service/builder/BuildStoreLocalTest.groovy b/src/test/groovy/io/seqera/wave/service/builder/BuildStoreLocalTest.groovy index e5190a0a0..68116a60d 100644 --- a/src/test/groovy/io/seqera/wave/service/builder/BuildStoreLocalTest.groovy +++ b/src/test/groovy/io/seqera/wave/service/builder/BuildStoreLocalTest.groovy @@ -46,7 +46,7 @@ class BuildStoreLocalTest extends Specification { private BuildConfig buildConfig @Inject - @Named(TaskExecutors.IO) + @Named(TaskExecutors.BLOCKING) ExecutorService ioExecutor BuildResult zeroResult = BuildResult.create('0') diff --git a/src/test/groovy/io/seqera/wave/service/pairing/PairingWebSocketTest.groovy b/src/test/groovy/io/seqera/wave/service/pairing/PairingWebSocketTest.groovy new file mode 100644 index 000000000..43b2f47d0 --- /dev/null +++ b/src/test/groovy/io/seqera/wave/service/pairing/PairingWebSocketTest.groovy @@ -0,0 +1,62 @@ +/* + * 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 . + */ + +package io.seqera.wave.service.pairing + +import spock.lang.Specification + +import io.micronaut.context.ApplicationContext +import io.micronaut.test.extensions.spock.annotation.MicronautTest +import io.seqera.wave.service.pairing.socket.PairingWebSocket + +/** + * + * @author Paolo Di Tommaso + */ +@MicronautTest +class PairingWebSocketTest extends Specification { + + def 'should allow any host' () { + given: + def ctx = ApplicationContext.run() + def pairing = ctx.getBean(PairingWebSocket) + + expect: + !pairing.isDenyHost('foo') + !pairing.isDenyHost('seqera.io') + !pairing.isDenyHost('ngrok') + + cleanup: + ctx.close() + } + + def 'should disallowed deny hosts' () { + given: + def ctx = ApplicationContext.run(['wave.denyHosts': ['ngrok','hctal']]) + def pairing = ctx.getBean(PairingWebSocket) + + expect: + pairing.isDenyHost('ngrok') + pairing.isDenyHost('hctal') + and: + !pairing.isDenyHost('seqera.io') + + cleanup: + ctx.close() + } +} diff --git a/typespec/models/MetricsResponse.tsp b/typespec/models/MetricsResponse.tsp deleted file mode 100644 index f17da0d26..000000000 --- a/typespec/models/MetricsResponse.tsp +++ /dev/null @@ -1,11 +0,0 @@ -@doc("Response payload for metrics.") -model MetricsResponse { - count: int64; - metric: "builds" | "fusion" | "pulls"; - orgs: Orgs; -} - -model Orgs { - key: string; - value: int64; -} \ No newline at end of file diff --git a/typespec/models/models.tsp b/typespec/models/models.tsp index 3062198af..999efa0b5 100644 --- a/typespec/models/models.tsp +++ b/typespec/models/models.tsp @@ -3,7 +3,6 @@ import "./ContainerResponse.tsp"; import "./BuildStatusResponse.tsp"; import "./ContainerInspectRequest.tsp"; import "./ContainerInspectResponse.tsp"; -import "./MetricsResponse.tsp"; import "./WaveScanRecord.tsp"; import "./WaveBuildRecord.tsp"; import "./ValidateRegistryCredsRequest.tsp"; diff --git a/typespec/routes.tsp b/typespec/routes.tsp index fb10d16c4..dd1a8678c 100644 --- a/typespec/routes.tsp +++ b/typespec/routes.tsp @@ -20,7 +20,7 @@ namespace wave { @statusCode statusCode: 200; }; - @route("/container/{requestId}") + @route("/{requestId}") @get op getContainerDetails(@path requestId: string): { @body response: WaveContainerRecord; @statusCode statusCode: 200; @@ -89,28 +89,6 @@ namespace wave { } - @route("/v1alpha2/metrics") - interface MetricsService { - - @route("/builds") - @get op getBuildMetrics(@query date?: string, @query org?: string): { - @body response: MetricsResponse; - @statusCode statusCode: 200; - }; - - @route("/pulls") - @get op getPullMetrics(@query date?: string, @query org?: string): { - @body response: MetricsResponse; - @statusCode statusCode: 200; - }; - - @route("/fusion/pulls") - @get op getFusionPullMetrics(@query date?: string, @query org?: string): { - @body response: MetricsResponse; - @statusCode statusCode: 200; - }; - } - @route("/v1alpha2/validate-creds") @post op validateCredsV2(@body request: ValidateRegistryCredsRequest): boolean;