From 5864cbcbfe2eb8c36ca05c3a39c7e5916aeecaec Mon Sep 17 00:00:00 2001 From: Dante Niewenhuis Date: Tue, 5 Mar 2024 13:23:57 +0100 Subject: Updated package versions, updated web server tests. (#207) * Updated all package versions including kotlin. Updated all web-server tests to run. * Changed the java version of the tests. OpenDC now only supports java 19. * small update * test update * new update * updated docker version to 19 * updated docker version to 19 --- opendc-web/opendc-web-client/build.gradle.kts | 2 +- .../org/opendc/web/client/PortfolioResource.kt | 15 +- .../org/opendc/web/client/ScenarioResource.kt | 21 ++- .../org/opendc/web/client/TopologyResource.kt | 21 ++- .../opendc/web/client/auth/OpenIdAuthController.kt | 55 +++--- .../org/opendc/web/client/internal/ClientUtils.kt | 10 +- .../web/client/internal/OAuthTokenRequest.kt | 8 +- .../web/client/internal/OAuthTokenResponse.kt | 2 +- .../web/client/internal/OpenIdConfiguration.kt | 2 +- .../org/opendc/web/client/runner/JobResource.kt | 5 +- .../web/client/transport/HttpTransportClient.kt | 71 +++++--- .../opendc/web/client/transport/TransportClient.kt | 22 ++- opendc-web/opendc-web-proto/build.gradle.kts | 2 +- .../main/kotlin/org/opendc/web/proto/JobState.kt | 2 +- .../main/kotlin/org/opendc/web/proto/Machine.kt | 2 +- .../main/kotlin/org/opendc/web/proto/MemoryUnit.kt | 2 +- .../org/opendc/web/proto/OperationalPhenomena.kt | 2 +- .../kotlin/org/opendc/web/proto/ProcessingUnit.kt | 2 +- .../src/main/kotlin/org/opendc/web/proto/Rack.kt | 2 +- .../src/main/kotlin/org/opendc/web/proto/Room.kt | 2 +- .../main/kotlin/org/opendc/web/proto/RoomTile.kt | 2 +- .../main/kotlin/org/opendc/web/proto/Targets.kt | 4 +- .../src/main/kotlin/org/opendc/web/proto/Trace.kt | 2 +- .../main/kotlin/org/opendc/web/proto/Workload.kt | 6 +- .../main/kotlin/org/opendc/web/proto/runner/Job.kt | 2 +- .../org/opendc/web/proto/runner/Portfolio.kt | 2 +- .../kotlin/org/opendc/web/proto/runner/Scenario.kt | 2 +- .../kotlin/org/opendc/web/proto/runner/Topology.kt | 2 +- .../main/kotlin/org/opendc/web/proto/user/Job.kt | 2 +- .../kotlin/org/opendc/web/proto/user/Portfolio.kt | 8 +- .../kotlin/org/opendc/web/proto/user/Project.kt | 8 +- .../org/opendc/web/proto/user/ProjectRole.kt | 2 +- .../kotlin/org/opendc/web/proto/user/Scenario.kt | 8 +- .../kotlin/org/opendc/web/proto/user/Topology.kt | 8 +- .../main/kotlin/org/opendc/web/proto/user/User.kt | 2 +- .../org/opendc/web/proto/user/UserAccounting.kt | 2 +- .../build.gradle.kts | 2 +- .../web/runner/runtime/OpenDCRunnerRecorder.java | 2 +- opendc-web/opendc-web-runner/Dockerfile | 4 +- opendc-web/opendc-web-runner/build.gradle.kts | 4 +- .../src/cli/kotlin/org/opendc/web/runner/Main.kt | 14 +- .../kotlin/org/opendc/web/runner/JobManager.kt | 16 +- .../kotlin/org/opendc/web/runner/OpenDCRunner.kt | 196 +++++++++++---------- .../opendc/web/runner/internal/JobManagerImpl.kt | 16 +- .../web/runner/internal/WebComputeMonitor.kt | 33 ++-- opendc-web/opendc-web-server/Dockerfile | 4 +- opendc-web/opendc-web-server/build.gradle.kts | 6 +- .../main/java/org/opendc/web/server/model/Job.java | 33 ++-- .../org/opendc/web/server/model/Portfolio.java | 45 +++-- .../java/org/opendc/web/server/model/Project.java | 37 ++-- .../web/server/model/ProjectAuthorization.java | 70 ++++---- .../java/org/opendc/web/server/model/Scenario.java | 34 ++-- .../java/org/opendc/web/server/model/Topology.java | 40 +++-- .../java/org/opendc/web/server/model/Trace.java | 10 +- .../opendc/web/server/model/UserAccounting.java | 14 +- .../java/org/opendc/web/server/model/Workload.java | 6 +- .../opendc/web/server/rest/SchedulerResource.java | 6 +- .../org/opendc/web/server/rest/TraceResource.java | 10 +- .../MissingKotlinParameterExceptionMapper.java | 8 +- .../rest/error/WebApplicationExceptionMapper.java | 10 +- .../opendc/web/server/rest/runner/JobResource.java | 20 +-- .../web/server/rest/user/PortfolioResource.java | 22 +-- .../rest/user/PortfolioScenarioResource.java | 20 +-- .../web/server/rest/user/ProjectResource.java | 26 +-- .../web/server/rest/user/ScenarioResource.java | 16 +- .../web/server/rest/user/TopologyResource.java | 26 +-- .../opendc/web/server/rest/user/UserResource.java | 8 +- .../org/opendc/web/server/service/JobService.java | 2 +- .../web/server/service/UserAccountingService.java | 4 +- .../web/server/util/DevSecurityOverrideFilter.java | 10 +- .../web/server/util/KotlinModuleCustomizer.java | 2 +- .../server/util/QuarkusObjectMapperSupplier.java | 2 +- .../web/server/util/runner/QuarkusJobManager.java | 4 +- .../src/main/resources/application-test.properties | 4 + .../src/main/resources/db/migration/V3.0__core.sql | 160 ----------------- .../main/resources/db/testing/V3.0.1__entities.sql | 24 --- .../src/main/resources/load_data.sql | 124 +++++++++++++ .../web/server/rest/SchedulerResourceTest.java | 5 +- .../opendc/web/server/rest/TraceResourceTest.java | 4 +- .../web/server/rest/runner/JobResourceTest.java | 25 ++- .../server/rest/user/PortfolioResourceTest.java | 141 ++++++++------- .../rest/user/PortfolioScenarioResourceTest.java | 106 +++++------ .../web/server/rest/user/ProjectResourceTest.java | 143 ++++++++------- .../web/server/rest/user/ScenarioResourceTest.java | 57 ++---- .../web/server/rest/user/TopologyResourceTest.java | 120 ++++++------- .../server/service/UserAccountingServiceTest.java | 2 +- .../build.gradle.kts | 2 +- opendc-web/opendc-web-ui/build.gradle.kts | 86 ++++----- 88 files changed, 1079 insertions(+), 1018 deletions(-) delete mode 100644 opendc-web/opendc-web-server/src/main/resources/db/migration/V3.0__core.sql delete mode 100644 opendc-web/opendc-web-server/src/main/resources/db/testing/V3.0.1__entities.sql create mode 100644 opendc-web/opendc-web-server/src/main/resources/load_data.sql (limited to 'opendc-web') diff --git a/opendc-web/opendc-web-client/build.gradle.kts b/opendc-web/opendc-web-client/build.gradle.kts index 77a0afff..55228ef9 100644 --- a/opendc-web/opendc-web-client/build.gradle.kts +++ b/opendc-web/opendc-web-client/build.gradle.kts @@ -22,7 +22,7 @@ description = "Client for the OpenDC web API" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` } diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/PortfolioResource.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/PortfolioResource.kt index 399804e8..f0e49973 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/PortfolioResource.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/PortfolioResource.kt @@ -40,19 +40,28 @@ public class PortfolioResource internal constructor(private val client: Transpor /** * Obtain the portfolio for [project] with [number]. */ - public fun get(project: Long, number: Int): Portfolio? = client.get("projects/$project/portfolios/$number") + public fun get( + project: Long, + number: Int, + ): Portfolio? = client.get("projects/$project/portfolios/$number") /** * Create a new portfolio for [project] with the specified [request]. */ - public fun create(project: Long, request: Portfolio.Create): Portfolio { + public fun create( + project: Long, + request: Portfolio.Create, + ): Portfolio { return checkNotNull(client.post("projects/$project/portfolios", request)) } /** * Delete the portfolio for [project] with [index]. */ - public fun delete(project: Long, index: Int): Portfolio { + public fun delete( + project: Long, + index: Int, + ): Portfolio { return requireNotNull(client.delete("projects/$project/portfolios/$index")) { "Unknown portfolio $index" } } } diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/ScenarioResource.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/ScenarioResource.kt index 7055e752..d43515a9 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/ScenarioResource.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/ScenarioResource.kt @@ -40,24 +40,37 @@ public class ScenarioResource internal constructor(private val client: Transport /** * List all scenarios that belong to the specified [portfolioNumber]. */ - public fun getAll(project: Long, portfolioNumber: Int): List = client.get("projects/$project/portfolios/$portfolioNumber/scenarios") ?: emptyList() + public fun getAll( + project: Long, + portfolioNumber: Int, + ): List = client.get("projects/$project/portfolios/$portfolioNumber/scenarios") ?: emptyList() /** * Obtain the scenario for [project] with [index]. */ - public fun get(project: Long, index: Int): Scenario? = client.get("projects/$project/scenarios/$index") + public fun get( + project: Long, + index: Int, + ): Scenario? = client.get("projects/$project/scenarios/$index") /** * Create a new scenario for [portfolio][portfolioNumber] with the specified [request]. */ - public fun create(project: Long, portfolioNumber: Int, request: Scenario.Create): Scenario { + public fun create( + project: Long, + portfolioNumber: Int, + request: Scenario.Create, + ): Scenario { return checkNotNull(client.post("projects/$project/portfolios/$portfolioNumber", request)) } /** * Delete the scenario for [project] with [index]. */ - public fun delete(project: Long, index: Int): Scenario { + public fun delete( + project: Long, + index: Int, + ): Scenario { return requireNotNull(client.delete("projects/$project/scenarios/$index")) { "Unknown scenario $index" } } } diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/TopologyResource.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/TopologyResource.kt index c37ae8da..34f5ea1b 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/TopologyResource.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/TopologyResource.kt @@ -41,26 +41,39 @@ public class TopologyResource internal constructor(private val client: Transport /** * Obtain the topology for [project] with [index]. */ - public fun get(project: Long, index: Int): Topology? = client.get("projects/$project/topologies/$index") + public fun get( + project: Long, + index: Int, + ): Topology? = client.get("projects/$project/topologies/$index") /** * Create a new topology for [project] with [request]. */ - public fun create(project: Long, request: Topology.Create): Topology { + public fun create( + project: Long, + request: Topology.Create, + ): Topology { return checkNotNull(client.post("projects/$project/topologies", request)) } /** * Update the topology with [index] for [project] using the specified [request]. */ - public fun update(project: Long, index: Int, request: Topology.Update): Topology? { + public fun update( + project: Long, + index: Int, + request: Topology.Update, + ): Topology? { return client.put("projects/$project/topologies/$index", request) } /** * Delete the topology for [project] with [index]. */ - public fun delete(project: Long, index: Long): Topology { + public fun delete( + project: Long, + index: Long, + ): Topology { return requireNotNull(client.delete("projects/$project/topologies/$index")) { "Unknown topology $index" } } } diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/auth/OpenIdAuthController.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/auth/OpenIdAuthController.kt index 7f9cbacd..707dc138 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/auth/OpenIdAuthController.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/auth/OpenIdAuthController.kt @@ -41,39 +41,40 @@ public class OpenIdAuthController( private val clientId: String, private val clientSecret: String, private val audience: String = "https://api.opendc.org/v2/", - private val client: HttpClient = HttpClient.newHttpClient() + private val client: HttpClient = HttpClient.newHttpClient(), ) : AuthController { /** * The Jackson object mapper to convert messages from/to JSON. */ - private val mapper = jacksonObjectMapper() - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + private val mapper = + jacksonObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) /** * The cached [OpenIdConfiguration]. */ private val openidConfig: OpenIdConfiguration get() { - var openidConfig = _openidConfig + var openidConfig = localOpenidConfig if (openidConfig == null) { openidConfig = requestConfig() - _openidConfig = openidConfig + localOpenidConfig = openidConfig } return openidConfig } - private var _openidConfig: OpenIdConfiguration? = null + private var localOpenidConfig: OpenIdConfiguration? = null /** * The cached OAuth token. */ - private var _token: OAuthTokenResponse? = null + private var localToken: OAuthTokenResponse? = null override fun injectToken(request: HttpRequest.Builder) { - var token = _token + var token = localToken if (token == null) { token = requestToken() - _token = token + localToken = token } request.header("Authorization", "Bearer ${token.accessToken}") @@ -83,22 +84,23 @@ public class OpenIdAuthController( * Refresh the current access token. */ override fun refreshToken() { - val refreshToken = _token?.refreshToken + val refreshToken = localToken?.refreshToken if (refreshToken == null) { requestToken() return } - _token = refreshToken(openidConfig, refreshToken) + localToken = refreshToken(openidConfig, refreshToken) } /** * Request the OpenID configuration from the chosen auth domain */ private fun requestConfig(): OpenIdConfiguration { - val request = HttpRequest.newBuilder(URI("https://$domain/.well-known/openid-configuration")) - .GET() - .build() + val request = + HttpRequest.newBuilder(URI("https://$domain/.well-known/openid-configuration")) + .GET() + .build() val response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()) return mapper.readValue(response.body()) } @@ -108,10 +110,11 @@ public class OpenIdAuthController( */ private fun requestToken(openidConfig: OpenIdConfiguration): OAuthTokenResponse { val body = OAuthTokenRequest.ClientCredentials(audience, clientId, clientSecret) - val request = HttpRequest.newBuilder(openidConfig.tokenEndpoint) - .header("Content-Type", "application/json") - .POST(HttpRequest.BodyPublishers.ofByteArray(mapper.writeValueAsBytes(body))) - .build() + val request = + HttpRequest.newBuilder(openidConfig.tokenEndpoint) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofByteArray(mapper.writeValueAsBytes(body))) + .build() val response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()) return mapper.readValue(response.body()) } @@ -119,12 +122,16 @@ public class OpenIdAuthController( /** * Helper method to refresh the auth token. */ - private fun refreshToken(openidConfig: OpenIdConfiguration, refreshToken: String): OAuthTokenResponse { + private fun refreshToken( + openidConfig: OpenIdConfiguration, + refreshToken: String, + ): OAuthTokenResponse { val body = OAuthTokenRequest.RefreshToken(refreshToken, clientId, clientSecret) - val request = HttpRequest.newBuilder(openidConfig.tokenEndpoint) - .header("Content-Type", "application/json") - .POST(HttpRequest.BodyPublishers.ofByteArray(mapper.writeValueAsBytes(body))) - .build() + val request = + HttpRequest.newBuilder(openidConfig.tokenEndpoint) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofByteArray(mapper.writeValueAsBytes(body))) + .build() val response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()) return mapper.readValue(response.body()) } @@ -134,7 +141,7 @@ public class OpenIdAuthController( */ private fun requestToken(): OAuthTokenResponse { val token = requestToken(openidConfig) - _token = token + localToken = token return token } } diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/ClientUtils.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/ClientUtils.kt index 29cf09dc..1ffaa602 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/ClientUtils.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/ClientUtils.kt @@ -35,14 +35,20 @@ internal inline fun TransportClient.get(path: String): T? { /** * Perform a POST request for resource at [path] and convert to type [T]. */ -internal inline fun TransportClient.post(path: String, body: B): T? { +internal inline fun TransportClient.post( + path: String, + body: B, +): T? { return post(path, body, object : TypeReference() {}) } /** * Perform a PUT request for resource at [path] and convert to type [T]. */ -internal inline fun TransportClient.put(path: String, body: B): T? { +internal inline fun TransportClient.put( + path: String, + body: B, +): T? { return put(path, body, object : TypeReference() {}) } diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OAuthTokenRequest.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OAuthTokenRequest.kt index 25341995..1bb06c8f 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OAuthTokenRequest.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OAuthTokenRequest.kt @@ -33,8 +33,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo @JsonSubTypes( value = [ JsonSubTypes.Type(value = OAuthTokenRequest.ClientCredentials::class, name = "client_credentials"), - JsonSubTypes.Type(value = OAuthTokenRequest.RefreshToken::class, name = "refresh_token") - ] + JsonSubTypes.Type(value = OAuthTokenRequest.RefreshToken::class, name = "refresh_token"), + ], ) internal sealed class OAuthTokenRequest { /** @@ -45,7 +45,7 @@ internal sealed class OAuthTokenRequest { @JsonProperty("client_id") val clientId: String, @JsonProperty("client_secret") - val clientSecret: String + val clientSecret: String, ) : OAuthTokenRequest() /** @@ -57,6 +57,6 @@ internal sealed class OAuthTokenRequest { @JsonProperty("client_id") val clientId: String, @JsonProperty("client_secret") - val clientSecret: String + val clientSecret: String, ) : OAuthTokenRequest() } diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OAuthTokenResponse.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OAuthTokenResponse.kt index cd5ccab0..76fe007c 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OAuthTokenResponse.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OAuthTokenResponse.kt @@ -36,5 +36,5 @@ internal data class OAuthTokenResponse( val tokenType: String, val scope: String = "", @JsonProperty("expires_in") - val expiresIn: Long + val expiresIn: Long, ) diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OpenIdConfiguration.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OpenIdConfiguration.kt index 23fbf368..eac1607e 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OpenIdConfiguration.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/internal/OpenIdConfiguration.kt @@ -39,5 +39,5 @@ internal data class OpenIdConfiguration( @JsonProperty("jwks_uri") val jwksUri: URI, @JsonProperty("scopes_supported") - val scopesSupported: Set + val scopesSupported: Set, ) diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/runner/JobResource.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/runner/JobResource.kt index ad3f1c9b..e72f703c 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/runner/JobResource.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/runner/JobResource.kt @@ -44,5 +44,8 @@ public class JobResource internal constructor(private val client: TransportClien /** * Update the job with [id]. */ - public fun update(id: Long, update: Job.Update): Job? = client.post("jobs/$id", update) + public fun update( + id: Long, + update: Job.Update, + ): Job? = client.post("jobs/$id", update) } diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/transport/HttpTransportClient.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/transport/HttpTransportClient.kt index e407380b..f6dca4d1 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/transport/HttpTransportClient.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/transport/HttpTransportClient.kt @@ -43,23 +43,28 @@ import java.nio.file.Paths public class HttpTransportClient( private val baseUrl: URI, private val auth: AuthController?, - private val client: HttpClient = HttpClient.newHttpClient() + private val client: HttpClient = HttpClient.newHttpClient(), ) : TransportClient { /** * The Jackson object mapper to convert messages from/to JSON. */ - private val mapper = jacksonObjectMapper() - .registerModule(JavaTimeModule()) - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + private val mapper = + jacksonObjectMapper() + .registerModule(JavaTimeModule()) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) /** * Obtain a resource at [path] of [targetType]. */ - override fun get(path: String, targetType: TypeReference): T? { - val request = HttpRequest.newBuilder(buildUri(path)) - .GET() - .also { auth?.injectToken(it) } - .build() + override fun get( + path: String, + targetType: TypeReference, + ): T? { + val request = + HttpRequest.newBuilder(buildUri(path)) + .GET() + .also { auth?.injectToken(it) } + .build() val response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()) return when (val code = response.statusCode()) { @@ -81,12 +86,17 @@ public class HttpTransportClient( /** * Update a resource at [path] of [targetType]. */ - override fun post(path: String, body: B, targetType: TypeReference): T? { - val request = HttpRequest.newBuilder(buildUri(path)) - .POST(HttpRequest.BodyPublishers.ofByteArray(mapper.writeValueAsBytes(body))) - .header("Content-Type", "application/json") - .also { auth?.injectToken(it) } - .build() + override fun post( + path: String, + body: B, + targetType: TypeReference, + ): T? { + val request = + HttpRequest.newBuilder(buildUri(path)) + .POST(HttpRequest.BodyPublishers.ofByteArray(mapper.writeValueAsBytes(body))) + .header("Content-Type", "application/json") + .also { auth?.injectToken(it) } + .build() val response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()) return when (val code = response.statusCode()) { @@ -108,12 +118,17 @@ public class HttpTransportClient( /** * Replace a resource at [path] of [targetType]. */ - override fun put(path: String, body: B, targetType: TypeReference): T? { - val request = HttpRequest.newBuilder(buildUri(path)) - .PUT(HttpRequest.BodyPublishers.ofByteArray(mapper.writeValueAsBytes(body))) - .header("Content-Type", "application/json") - .also { auth?.injectToken(it) } - .build() + override fun put( + path: String, + body: B, + targetType: TypeReference, + ): T? { + val request = + HttpRequest.newBuilder(buildUri(path)) + .PUT(HttpRequest.BodyPublishers.ofByteArray(mapper.writeValueAsBytes(body))) + .header("Content-Type", "application/json") + .also { auth?.injectToken(it) } + .build() val response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()) return when (val code = response.statusCode()) { @@ -135,11 +150,15 @@ public class HttpTransportClient( /** * Delete a resource at [path] of [targetType]. */ - override fun delete(path: String, targetType: TypeReference): T? { - val request = HttpRequest.newBuilder(buildUri(path)) - .DELETE() - .also { auth?.injectToken(it) } - .build() + override fun delete( + path: String, + targetType: TypeReference, + ): T? { + val request = + HttpRequest.newBuilder(buildUri(path)) + .DELETE() + .also { auth?.injectToken(it) } + .build() val response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()) return when (val code = response.statusCode()) { diff --git a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/transport/TransportClient.kt b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/transport/TransportClient.kt index af727ca7..ebf3402f 100644 --- a/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/transport/TransportClient.kt +++ b/opendc-web/opendc-web-client/src/main/kotlin/org/opendc/web/client/transport/TransportClient.kt @@ -31,20 +31,34 @@ public interface TransportClient { /** * Obtain a resource at [path] of [targetType]. */ - public fun get(path: String, targetType: TypeReference): T? + public fun get( + path: String, + targetType: TypeReference, + ): T? /** * Update a resource at [path] of [targetType]. */ - public fun post(path: String, body: B, targetType: TypeReference): T? + public fun post( + path: String, + body: B, + targetType: TypeReference, + ): T? /** * Replace a resource at [path] of [targetType]. */ - public fun put(path: String, body: B, targetType: TypeReference): T? + public fun put( + path: String, + body: B, + targetType: TypeReference, + ): T? /** * Delete a resource at [path] of [targetType]. */ - public fun delete(path: String, targetType: TypeReference): T? + public fun delete( + path: String, + targetType: TypeReference, + ): T? } diff --git a/opendc-web/opendc-web-proto/build.gradle.kts b/opendc-web/opendc-web-proto/build.gradle.kts index 4a566346..9b307655 100644 --- a/opendc-web/opendc-web-proto/build.gradle.kts +++ b/opendc-web/opendc-web-proto/build.gradle.kts @@ -22,7 +22,7 @@ description = "Web communication protocol for OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` id("org.kordamp.gradle.jandex") // Necessary for Quarkus to process annotations diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/JobState.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/JobState.kt index 38b8ca42..a8e67ec5 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/JobState.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/JobState.kt @@ -49,5 +49,5 @@ public enum class JobState { /** * The job has failed. */ - FAILED; + FAILED, } diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Machine.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Machine.kt index f5c50cc3..72163f51 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Machine.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Machine.kt @@ -36,5 +36,5 @@ public data class Machine( val memory: List = emptyList(), @JsonProperty("storages") val storage: List = emptyList(), - val rackId: String? = null + val rackId: String? = null, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/MemoryUnit.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/MemoryUnit.kt index 1fc604fa..00560ad6 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/MemoryUnit.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/MemoryUnit.kt @@ -30,5 +30,5 @@ public data class MemoryUnit( val name: String, val speedMbPerS: Double, val sizeMb: Double, - val energyConsumptionW: Double + val energyConsumptionW: Double, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/OperationalPhenomena.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/OperationalPhenomena.kt index f3164f64..28006d27 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/OperationalPhenomena.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/OperationalPhenomena.kt @@ -27,5 +27,5 @@ package org.opendc.web.proto */ public data class OperationalPhenomena( val failures: Boolean, - val interference: Boolean + val interference: Boolean, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/ProcessingUnit.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/ProcessingUnit.kt index 5f79d1bd..86f40516 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/ProcessingUnit.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/ProcessingUnit.kt @@ -30,5 +30,5 @@ public data class ProcessingUnit( val name: String, val clockRateMhz: Double, val numberOfCores: Int, - val energyConsumptionW: Double + val energyConsumptionW: Double, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Rack.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Rack.kt index 131aa184..c997e814 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Rack.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Rack.kt @@ -30,5 +30,5 @@ public data class Rack( val name: String, val capacity: Int, val powerCapacityW: Double, - val machines: List + val machines: List, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Room.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Room.kt index c5499150..5b305168 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Room.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Room.kt @@ -29,5 +29,5 @@ public data class Room( val id: String, val name: String, val tiles: Set, - val topologyId: String? = null + val topologyId: String? = null, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/RoomTile.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/RoomTile.kt index 53cb53cd..666d66ee 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/RoomTile.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/RoomTile.kt @@ -30,5 +30,5 @@ public data class RoomTile( val positionX: Double, val positionY: Double, val rack: Rack? = null, - val roomId: String? = null + val roomId: String? = null, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Targets.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Targets.kt index a0100f72..25516ff0 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Targets.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Targets.kt @@ -22,7 +22,7 @@ package org.opendc.web.proto -import javax.validation.constraints.Min +import jakarta.validation.constraints.Min /** * The targets of a portfolio. @@ -33,5 +33,5 @@ import javax.validation.constraints.Min public data class Targets( val metrics: Set, @field:Min(1) - val repeats: Int = 1 + val repeats: Int = 1, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Trace.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Trace.kt index 1c086cd8..2952a273 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Trace.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Trace.kt @@ -32,5 +32,5 @@ package org.opendc.web.proto public data class Trace( val id: String, val name: String, - val type: String + val type: String, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Workload.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Workload.kt index cc6e0ed8..58daf817 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Workload.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/Workload.kt @@ -22,8 +22,8 @@ package org.opendc.web.proto -import javax.validation.constraints.DecimalMax -import javax.validation.constraints.DecimalMin +import jakarta.validation.constraints.DecimalMax +import jakarta.validation.constraints.DecimalMin /** * The workload to simulate for a scenario. @@ -39,6 +39,6 @@ public data class Workload(val trace: Trace, val samplingFraction: Double) { val trace: String, @DecimalMin(value = "0.001", message = "Sampling fraction must be non-zero") @DecimalMax(value = "1", message = "Sampling fraction cannot exceed one") - val samplingFraction: Double + val samplingFraction: Double, ) } diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Job.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Job.kt index 4f21f0bb..34642436 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Job.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Job.kt @@ -37,7 +37,7 @@ public data class Job( val createdAt: Instant, val updatedAt: Instant, val runtime: Int, - val results: Map? = null + val results: Map? = null, ) { /** * A request to update the state of a job. diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Portfolio.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Portfolio.kt index 5faad5b3..916d8cf0 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Portfolio.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Portfolio.kt @@ -39,5 +39,5 @@ public data class Portfolio( val id: Long, val number: Int, val name: String, - val targets: Targets + val targets: Targets, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Scenario.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Scenario.kt index aeffc4d7..ebc10bb0 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Scenario.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Scenario.kt @@ -38,5 +38,5 @@ public data class Scenario( val workload: Workload, val topology: Topology, val phenomena: OperationalPhenomena, - val schedulerName: String + val schedulerName: String, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Topology.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Topology.kt index bc185aea..4bffdee9 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Topology.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/runner/Topology.kt @@ -36,5 +36,5 @@ public data class Topology( val name: String, val rooms: List, val createdAt: Instant, - val updatedAt: Instant + val updatedAt: Instant, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Job.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Job.kt index de5f8de3..dd2f209e 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Job.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Job.kt @@ -36,5 +36,5 @@ public data class Job( val state: JobState, val createdAt: Instant, val updatedAt: Instant, - val results: Map? = null + val results: Map? = null, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Portfolio.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Portfolio.kt index 99d0f65e..6f433a04 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Portfolio.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Portfolio.kt @@ -22,9 +22,9 @@ package org.opendc.web.proto.user +import jakarta.validation.constraints.NotBlank import org.eclipse.microprofile.openapi.annotations.media.Schema import org.opendc.web.proto.Targets -import javax.validation.constraints.NotBlank /** * A portfolio is the composition of multiple scenarios. @@ -42,7 +42,7 @@ public data class Portfolio( val project: Project, val name: String, val targets: Targets, - val scenarios: List + val scenarios: List, ) { /** * A request to create a new portfolio. @@ -51,7 +51,7 @@ public data class Portfolio( public data class Create( @field:NotBlank(message = "Name must not be empty") val name: String, - val targets: Targets + val targets: Targets, ) /** @@ -67,6 +67,6 @@ public data class Portfolio( val id: Long, val number: Int, val name: String, - val targets: Targets + val targets: Targets, ) } diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Project.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Project.kt index 3a2807ca..635552a9 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Project.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Project.kt @@ -22,9 +22,9 @@ package org.opendc.web.proto.user +import jakarta.validation.constraints.NotBlank import org.eclipse.microprofile.openapi.annotations.media.Schema import java.time.Instant -import javax.validation.constraints.NotBlank /** * A project in OpenDC encapsulates all the datacenter designs and simulation runs for a set of users. @@ -34,11 +34,13 @@ public data class Project( val name: String, val createdAt: Instant, val updatedAt: Instant, - val role: ProjectRole + val role: ProjectRole, ) { /** * A request to create a new project. */ @Schema(name = "Project.Create") - public data class Create(@field:NotBlank(message = "Name must not be empty") val name: String) + public data class Create( + @field:NotBlank(message = "Name must not be empty") val name: String, + ) } diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/ProjectRole.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/ProjectRole.kt index ea6a30ab..0f6de1fc 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/ProjectRole.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/ProjectRole.kt @@ -39,5 +39,5 @@ public enum class ProjectRole { /** * The user owns the project (so he can delete it). */ - OWNER + OWNER, } diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Scenario.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Scenario.kt index b9c7a4cf..e0c790f5 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Scenario.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Scenario.kt @@ -22,10 +22,10 @@ package org.opendc.web.proto.user +import jakarta.validation.constraints.NotBlank import org.eclipse.microprofile.openapi.annotations.media.Schema import org.opendc.web.proto.OperationalPhenomena import org.opendc.web.proto.Workload -import javax.validation.constraints.NotBlank /** * A single scenario to be explored by the simulator. @@ -40,7 +40,7 @@ public data class Scenario( val topology: Topology.Summary, val phenomena: OperationalPhenomena, val schedulerName: String, - val jobs: List + val jobs: List, ) { /** * Create a new scenario. @@ -58,7 +58,7 @@ public data class Scenario( val workload: Workload.Spec, val topology: Long, val phenomena: OperationalPhenomena, - val schedulerName: String + val schedulerName: String, ) /** @@ -81,6 +81,6 @@ public data class Scenario( val topology: Topology.Summary, val phenomena: OperationalPhenomena, val schedulerName: String, - val jobs: List + val jobs: List, ) } diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Topology.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Topology.kt index 73748bb9..0943eaf8 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Topology.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/Topology.kt @@ -22,10 +22,10 @@ package org.opendc.web.proto.user +import jakarta.validation.constraints.NotBlank import org.eclipse.microprofile.openapi.annotations.media.Schema import org.opendc.web.proto.Room import java.time.Instant -import javax.validation.constraints.NotBlank /** * Model for an OpenDC topology. @@ -37,7 +37,7 @@ public data class Topology( val name: String, val rooms: List, val createdAt: Instant, - val updatedAt: Instant + val updatedAt: Instant, ) { /** * Create a new topology for a project. @@ -46,7 +46,7 @@ public data class Topology( public data class Create( @field:NotBlank(message = "Name must not be empty") val name: String, - val rooms: List + val rooms: List, ) /** @@ -70,6 +70,6 @@ public data class Topology( val number: Int, val name: String, val createdAt: Instant, - val updatedAt: Instant + val updatedAt: Instant, ) } diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/User.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/User.kt index f18cda61..33dad4ff 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/User.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/User.kt @@ -27,5 +27,5 @@ package org.opendc.web.proto.user */ public data class User( val userId: String, - val accounting: UserAccounting + val accounting: UserAccounting, ) diff --git a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/UserAccounting.kt b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/UserAccounting.kt index 2441983a..970721eb 100644 --- a/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/UserAccounting.kt +++ b/opendc-web/opendc-web-proto/src/main/kotlin/org/opendc/web/proto/user/UserAccounting.kt @@ -30,5 +30,5 @@ import java.time.LocalDate public data class UserAccounting( val periodEnd: LocalDate, val simulationTime: Int, - val simulationTimeBudget: Int + val simulationTimeBudget: Int, ) diff --git a/opendc-web/opendc-web-runner-quarkus-deployment/build.gradle.kts b/opendc-web/opendc-web-runner-quarkus-deployment/build.gradle.kts index b3f1ec3b..589337f4 100644 --- a/opendc-web/opendc-web-runner-quarkus-deployment/build.gradle.kts +++ b/opendc-web/opendc-web-runner-quarkus-deployment/build.gradle.kts @@ -22,7 +22,7 @@ description = "Quarkus extension for the OpenDC experiment runner" -/* Build configuration */ +// Build configuration plugins { `java-library-conventions` } diff --git a/opendc-web/opendc-web-runner-quarkus/src/main/java/org/opendc/web/runner/runtime/OpenDCRunnerRecorder.java b/opendc-web/opendc-web-runner-quarkus/src/main/java/org/opendc/web/runner/runtime/OpenDCRunnerRecorder.java index 76f2368f..d5d524f1 100644 --- a/opendc-web/opendc-web-runner-quarkus/src/main/java/org/opendc/web/runner/runtime/OpenDCRunnerRecorder.java +++ b/opendc-web/opendc-web-runner-quarkus/src/main/java/org/opendc/web/runner/runtime/OpenDCRunnerRecorder.java @@ -25,8 +25,8 @@ package org.opendc.web.runner.runtime; import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.ShutdownContext; import io.quarkus.runtime.annotations.Recorder; +import jakarta.enterprise.inject.spi.CDI; import java.io.File; -import javax.enterprise.inject.spi.CDI; import org.jboss.logging.Logger; import org.opendc.web.runner.JobManager; import org.opendc.web.runner.OpenDCRunner; diff --git a/opendc-web/opendc-web-runner/Dockerfile b/opendc-web/opendc-web-runner/Dockerfile index 3f393055..22c36c65 100644 --- a/opendc-web/opendc-web-runner/Dockerfile +++ b/opendc-web/opendc-web-runner/Dockerfile @@ -1,4 +1,4 @@ -FROM openjdk:17-slim +FROM openjdk:19-slim MAINTAINER OpenDC Maintainers # Obtain (cache) Gradle wrapper @@ -11,7 +11,7 @@ RUN ./gradlew --version COPY ./ /app/ RUN ./gradlew --no-daemon :opendc-web:opendc-web-runner:installDist -FROM openjdk:17-slim +FROM openjdk:19-slim COPY --from=0 /app/opendc-web/opendc-web-runner/build/install /opt/ COPY --from=0 /app/traces /opt/opendc/traces WORKDIR /opt/opendc diff --git a/opendc-web/opendc-web-runner/build.gradle.kts b/opendc-web/opendc-web-runner/build.gradle.kts index 0a6ce658..38492929 100644 --- a/opendc-web/opendc-web-runner/build.gradle.kts +++ b/opendc-web/opendc-web-runner/build.gradle.kts @@ -22,7 +22,7 @@ description = "Experiment runner for OpenDC" -/* Build configuration */ +// Build configuration plugins { `kotlin-library-conventions` distribution @@ -74,7 +74,7 @@ val createCli by tasks.creating(CreateStartScripts::class) { applicationName = "opendc-runner" mainClass.set("org.opendc.web.runner.cli.MainKt") classpath = cliJar.outputs.files + cliRuntimeClasspath - outputDir = project.buildDir.resolve("scripts") + outputDir = project.layout.buildDirectory.get().asFile.resolve("scripts") } distributions { diff --git a/opendc-web/opendc-web-runner/src/cli/kotlin/org/opendc/web/runner/Main.kt b/opendc-web/opendc-web-runner/src/cli/kotlin/org/opendc/web/runner/Main.kt index 299c4d09..5d35fd98 100644 --- a/opendc-web/opendc-web-runner/src/cli/kotlin/org/opendc/web/runner/Main.kt +++ b/opendc-web/opendc-web-runner/src/cli/kotlin/org/opendc/web/runner/Main.kt @@ -48,7 +48,7 @@ class RunnerCli : CliktCommand(name = "opendc-runner") { private val apiUrl by option( "--api-url", help = "url to the OpenDC API", - envvar = "OPENDC_API_URL" + envvar = "OPENDC_API_URL", ) .convert { URI(it) } .default(URI("https://api.opendc.org/v2")) @@ -59,7 +59,7 @@ class RunnerCli : CliktCommand(name = "opendc-runner") { private val authDomain by option( "--auth-domain", help = "auth domain of the OpenDC API", - envvar = "AUTH0_DOMAIN" + envvar = "AUTH0_DOMAIN", ) .required() @@ -69,7 +69,7 @@ class RunnerCli : CliktCommand(name = "opendc-runner") { private val authAudience by option( "--auth-audience", help = "auth audience of the OpenDC API", - envvar = "AUTH0_AUDIENCE" + envvar = "AUTH0_AUDIENCE", ) .required() @@ -79,7 +79,7 @@ class RunnerCli : CliktCommand(name = "opendc-runner") { private val authClientId by option( "--auth-id", help = "auth client id of the OpenDC API", - envvar = "AUTH0_CLIENT_ID" + envvar = "AUTH0_CLIENT_ID", ) .required() @@ -89,7 +89,7 @@ class RunnerCli : CliktCommand(name = "opendc-runner") { private val authClientSecret by option( "--auth-secret", help = "auth client secret of the OpenDC API", - envvar = "AUTH0_CLIENT_SECRET" + envvar = "AUTH0_CLIENT_SECRET", ) .required() @@ -99,7 +99,7 @@ class RunnerCli : CliktCommand(name = "opendc-runner") { private val tracePath by option( "--traces", help = "path to the directory containing the traces", - envvar = "OPENDC_TRACES" + envvar = "OPENDC_TRACES", ) .file(canBeFile = false) .defaultLazy { File("traces/") } @@ -109,7 +109,7 @@ class RunnerCli : CliktCommand(name = "opendc-runner") { */ private val parallelism by option( "--parallelism", - help = "maximum number of threads for simulations" + help = "maximum number of threads for simulations", ) .int() .default(Runtime.getRuntime().availableProcessors() - 1) diff --git a/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/JobManager.kt b/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/JobManager.kt index d6c06889..a517f3b4 100644 --- a/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/JobManager.kt +++ b/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/JobManager.kt @@ -47,17 +47,27 @@ public interface JobManager { * @param runtime The total runtime of the job. * @return `true` if the job can continue, `false` if the job has been cancelled. */ - public fun heartbeat(id: Long, runtime: Int): Boolean + public fun heartbeat( + id: Long, + runtime: Int, + ): Boolean /** * Mark the job as failed. */ - public fun fail(id: Long, runtime: Int) + public fun fail( + id: Long, + runtime: Int, + ) /** * Persist the specified results for the specified job. */ - public fun finish(id: Long, runtime: Int, results: Map) + public fun finish( + id: Long, + runtime: Int, + results: Map, + ) public companion object { /** diff --git a/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/OpenDCRunner.kt b/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/OpenDCRunner.kt index 4351f3c1..eee340cf 100644 --- a/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/OpenDCRunner.kt +++ b/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/OpenDCRunner.kt @@ -76,7 +76,7 @@ public class OpenDCRunner( parallelism: Int = Runtime.getRuntime().availableProcessors(), private val jobTimeout: Duration = Duration.ofMinutes(10), private val pollInterval: Duration = Duration.ofSeconds(30), - private val heartbeatInterval: Duration = Duration.ofMinutes(1) + private val heartbeatInterval: Duration = Duration.ofMinutes(1), ) : Runnable { /** * Logging instance for this runner. @@ -149,26 +149,28 @@ public class OpenDCRunner( val startTime = Instant.now() val currentThread = Thread.currentThread() - val heartbeat = scheduler.scheduleWithFixedDelay( - { - if (!manager.heartbeat(id, startTime.secondsSince())) { - currentThread.interrupt() - } - }, - 0, - heartbeatInterval.toMillis(), - TimeUnit.MILLISECONDS - ) + val heartbeat = + scheduler.scheduleWithFixedDelay( + { + if (!manager.heartbeat(id, startTime.secondsSince())) { + currentThread.interrupt() + } + }, + 0, + heartbeatInterval.toMillis(), + TimeUnit.MILLISECONDS, + ) try { val topology = convertTopology(scenario.topology) - val jobs = (0 until scenario.portfolio.targets.repeats).map { repeat -> - SimulationTask( - scenario, - repeat, - topology - ) - } + val jobs = + (0 until scenario.portfolio.targets.repeats).map { repeat -> + SimulationTask( + scenario, + repeat, + topology, + ) + } val results = invokeAll(jobs).map { it.rawResult } heartbeat.cancel(true) @@ -194,8 +196,8 @@ public class OpenDCRunner( "total_vms_submitted" to results.map { it.totalVmsSubmitted }, "total_vms_queued" to results.map { it.totalVmsQueued }, "total_vms_finished" to results.map { it.totalVmsFinished }, - "total_vms_failed" to results.map { it.totalVmsFailed } - ) + "total_vms_failed" to results.map { it.totalVmsFailed }, + ), ) } catch (e: Exception) { // Check whether the job failed due to exceeding its time budget @@ -232,7 +234,7 @@ public class OpenDCRunner( private inner class SimulationTask( private val scenario: Scenario, private val repeat: Int, - private val topology: List + private val topology: List, ) : RecursiveTask() { override fun compute(): WebComputeMonitor.Results { val monitor = WebComputeMonitor() @@ -254,50 +256,51 @@ public class OpenDCRunner( /** * Run a single simulation of the scenario. */ - private fun runSimulation(monitor: WebComputeMonitor) = runSimulation { - val serviceDomain = "compute.opendc.org" - val seed = repeat.toLong() - - val scenario = scenario - - Provisioner(dispatcher, seed).use { provisioner -> - provisioner.runSteps( - setupComputeService( - serviceDomain, - { createComputeScheduler(scenario.schedulerName, Random(it.seeder.nextLong())) } - ), - registerComputeMonitor(serviceDomain, monitor), - setupHosts(serviceDomain, topology) - ) - - val service = provisioner.registry.resolve(serviceDomain, ComputeService::class.java)!! - - val workload = - trace(scenario.workload.trace.id).sampleByLoad(scenario.workload.samplingFraction) - val vms = workload.resolve(workloadLoader, Random(seed)) + private fun runSimulation(monitor: WebComputeMonitor) = + runSimulation { + val serviceDomain = "compute.opendc.org" + val seed = repeat.toLong() + + val scenario = scenario + + Provisioner(dispatcher, seed).use { provisioner -> + provisioner.runSteps( + setupComputeService( + serviceDomain, + { createComputeScheduler(scenario.schedulerName, Random(it.seeder.nextLong())) }, + ), + registerComputeMonitor(serviceDomain, monitor), + setupHosts(serviceDomain, topology), + ) - val phenomena = scenario.phenomena - val failureModel = - if (phenomena.failures) { - grid5000(Duration.ofDays(7)) - } else { - null + val service = provisioner.registry.resolve(serviceDomain, ComputeService::class.java)!! + + val workload = + trace(scenario.workload.trace.id).sampleByLoad(scenario.workload.samplingFraction) + val vms = workload.resolve(workloadLoader, Random(seed)) + + val phenomena = scenario.phenomena + val failureModel = + if (phenomena.failures) { + grid5000(Duration.ofDays(7)) + } else { + null + } + + // Run workload trace + service.replay(timeSource, vms, seed, failureModel = failureModel, interference = phenomena.interference) + + val serviceMetrics = service.getSchedulerStats() + logger.debug { + "Scheduler " + + "Success=${serviceMetrics.attemptsSuccess} " + + "Failure=${serviceMetrics.attemptsFailure} " + + "Error=${serviceMetrics.attemptsError} " + + "Pending=${serviceMetrics.serversPending} " + + "Active=${serviceMetrics.serversActive}" } - - // Run workload trace - service.replay(timeSource, vms, seed, failureModel = failureModel, interference = phenomena.interference) - - val serviceMetrics = service.getSchedulerStats() - logger.debug { - "Scheduler " + - "Success=${serviceMetrics.attemptsSuccess} " + - "Failure=${serviceMetrics.attemptsFailure} " + - "Error=${serviceMetrics.attemptsError} " + - "Pending=${serviceMetrics.serversPending} " + - "Active=${serviceMetrics.serversActive}" } } - } } /** @@ -307,46 +310,50 @@ public class OpenDCRunner( val res = mutableListOf() val random = Random(0) - val machines = topology.rooms.asSequence() - .flatMap { room -> - room.tiles.flatMap { tile -> - val rack = tile.rack - rack?.machines?.map { machine -> rack to machine } ?: emptyList() + val machines = + topology.rooms.asSequence() + .flatMap { room -> + room.tiles.flatMap { tile -> + val rack = tile.rack + rack?.machines?.map { machine -> rack to machine } ?: emptyList() + } } - } for ((rack, machine) in machines) { val clusterId = rack.id val position = machine.position - val processors = machine.cpus.flatMap { cpu -> - val cores = cpu.numberOfCores - val speed = cpu.clockRateMhz - // TODO Remove hard coding of vendor - val node = ProcessingNode("Intel", "amd64", cpu.name, cores) - List(cores) { coreId -> - ProcessingUnit(node, coreId, speed) + val processors = + machine.cpus.flatMap { cpu -> + val cores = cpu.numberOfCores + val speed = cpu.clockRateMhz + // TODO Remove hard coding of vendor + val node = ProcessingNode("Intel", "amd64", cpu.name, cores) + List(cores) { coreId -> + ProcessingUnit(node, coreId, speed) + } + } + val memoryUnits = + machine.memory.map { memory -> + MemoryUnit( + "Samsung", + memory.name, + memory.speedMbPerS, + memory.sizeMb.toLong(), + ) } - } - val memoryUnits = machine.memory.map { memory -> - MemoryUnit( - "Samsung", - memory.name, - memory.speedMbPerS, - memory.sizeMb.toLong() - ) - } val energyConsumptionW = machine.cpus.sumOf { it.energyConsumptionW } val powerModel = CpuPowerModels.linear(2 * energyConsumptionW, energyConsumptionW * 0.5) - val spec = HostSpec( - UUID(random.nextLong(), random.nextLong()), - "node-$clusterId-$position", - mapOf("cluster" to clusterId), - MachineModel(processors, memoryUnits), - SimPsuFactories.simple(powerModel) - ) + val spec = + HostSpec( + UUID(random.nextLong(), random.nextLong()), + "node-$clusterId-$position", + mapOf("cluster" to clusterId), + MachineModel(processors, memoryUnits), + SimPsuFactories.simple(powerModel), + ) res += spec } @@ -358,10 +365,11 @@ public class OpenDCRunner( * A custom [ForkJoinWorkerThreadFactory] that uses the [ClassLoader] of specified by the runner. */ private class RunnerThreadFactory(private val classLoader: ClassLoader) : ForkJoinWorkerThreadFactory { - override fun newThread(pool: ForkJoinPool): ForkJoinWorkerThread = object : ForkJoinWorkerThread(pool) { - init { - contextClassLoader = classLoader + override fun newThread(pool: ForkJoinPool): ForkJoinWorkerThread = + object : ForkJoinWorkerThread(pool) { + init { + contextClassLoader = classLoader + } } - } } } diff --git a/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/internal/JobManagerImpl.kt b/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/internal/JobManagerImpl.kt index 5b1b7132..7081041c 100644 --- a/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/internal/JobManagerImpl.kt +++ b/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/internal/JobManagerImpl.kt @@ -44,16 +44,26 @@ internal class JobManagerImpl(private val client: OpenDCRunnerClient) : JobManag } } - override fun heartbeat(id: Long, runtime: Int): Boolean { + override fun heartbeat( + id: Long, + runtime: Int, + ): Boolean { val res = client.jobs.update(id, Job.Update(JobState.RUNNING, runtime)) return res?.state != JobState.FAILED } - override fun fail(id: Long, runtime: Int) { + override fun fail( + id: Long, + runtime: Int, + ) { client.jobs.update(id, Job.Update(JobState.FAILED, runtime)) } - override fun finish(id: Long, runtime: Int, results: Map) { + override fun finish( + id: Long, + runtime: Int, + results: Map, + ) { client.jobs.update(id, Job.Update(JobState.FINISHED, runtime)) } } diff --git a/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/internal/WebComputeMonitor.kt b/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/internal/WebComputeMonitor.kt index 774689c9..4576a463 100644 --- a/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/internal/WebComputeMonitor.kt +++ b/opendc-web/opendc-web-runner/src/main/kotlin/org/opendc/web/runner/internal/WebComputeMonitor.kt @@ -34,31 +34,32 @@ import kotlin.math.roundToLong */ internal class WebComputeMonitor : ComputeMonitor { override fun record(reader: HostTableReader) { - val slices = reader.downtime / SLICE_LENGTH + val slices = reader.downtime / sliceLength - hostAggregateMetrics = AggregateHostMetrics( - hostAggregateMetrics.totalActiveTime + reader.cpuActiveTime, - hostAggregateMetrics.totalIdleTime + reader.cpuIdleTime, - hostAggregateMetrics.totalStealTime + reader.cpuStealTime, - hostAggregateMetrics.totalLostTime + reader.cpuLostTime, - hostAggregateMetrics.totalPowerDraw + reader.energyUsage, - hostAggregateMetrics.totalFailureSlices + slices, - hostAggregateMetrics.totalFailureVmSlices + reader.guestsRunning * slices - ) + hostAggregateMetrics = + AggregateHostMetrics( + hostAggregateMetrics.totalActiveTime + reader.cpuActiveTime, + hostAggregateMetrics.totalIdleTime + reader.cpuIdleTime, + hostAggregateMetrics.totalStealTime + reader.cpuStealTime, + hostAggregateMetrics.totalLostTime + reader.cpuLostTime, + hostAggregateMetrics.totalPowerDraw + reader.energyUsage, + hostAggregateMetrics.totalFailureSlices + slices, + hostAggregateMetrics.totalFailureVmSlices + reader.guestsRunning * slices, + ) hostMetrics.compute(reader.host.id) { _, prev -> HostMetrics( reader.cpuUsage + (prev?.cpuUsage ?: 0.0), reader.cpuDemand + (prev?.cpuDemand ?: 0.0), reader.guestsRunning + (prev?.instanceCount ?: 0), - 1 + (prev?.count ?: 0) + 1 + (prev?.count ?: 0), ) } } private var hostAggregateMetrics: AggregateHostMetrics = AggregateHostMetrics() private val hostMetrics: MutableMap = mutableMapOf() - private val SLICE_LENGTH: Long = 5 * 60L + private val sliceLength: Long = 5 * 60L private data class AggregateHostMetrics( val totalActiveTime: Long = 0L, @@ -67,14 +68,14 @@ internal class WebComputeMonitor : ComputeMonitor { val totalLostTime: Long = 0L, val totalPowerDraw: Double = 0.0, val totalFailureSlices: Double = 0.0, - val totalFailureVmSlices: Double = 0.0 + val totalFailureVmSlices: Double = 0.0, ) private data class HostMetrics( val cpuUsage: Double, val cpuDemand: Double, val instanceCount: Long, - val count: Long + val count: Long, ) private lateinit var serviceData: ServiceData @@ -106,7 +107,7 @@ internal class WebComputeMonitor : ComputeMonitor { serviceData.serversTotal, serviceData.serversPending, serviceData.serversTotal - serviceData.serversPending - serviceData.serversActive, - serviceData.attemptsError + serviceData.attemptsFailure + serviceData.attemptsError + serviceData.attemptsFailure, ) } @@ -128,6 +129,6 @@ internal class WebComputeMonitor : ComputeMonitor { val totalVmsSubmitted: Int, val totalVmsQueued: Int, val totalVmsFinished: Int, - val totalVmsFailed: Int + val totalVmsFailed: Int, ) } diff --git a/opendc-web/opendc-web-server/Dockerfile b/opendc-web/opendc-web-server/Dockerfile index 8aa54291..bcdb831e 100644 --- a/opendc-web/opendc-web-server/Dockerfile +++ b/opendc-web/opendc-web-server/Dockerfile @@ -1,4 +1,4 @@ -FROM openjdk:17-slim +FROM openjdk:19-slim MAINTAINER OpenDC Maintainers # Obtain (cache) Gradle wrapper @@ -19,7 +19,7 @@ ENV OPENDC_AUTH0_DOCS_CLIENT_ID=$OPENDC_AUTH0_DOCS_CLIENT_ID COPY ./ /app/ RUN ./gradlew --no-daemon :opendc-web:opendc-web-server:quarkusBuild -Dquarkus.profile=docker -FROM openjdk:17-slim +FROM openjdk:19-slim COPY --from=0 /app/opendc-web/opendc-web-server/build/quarkus-app /opt/opendc WORKDIR /opt/opendc CMD java -jar quarkus-run.jar diff --git a/opendc-web/opendc-web-server/build.gradle.kts b/opendc-web/opendc-web-server/build.gradle.kts index 8a6d8c0b..484e98c0 100644 --- a/opendc-web/opendc-web-server/build.gradle.kts +++ b/opendc-web/opendc-web-server/build.gradle.kts @@ -22,7 +22,7 @@ description = "Web server of OpenDC" -/* Build configuration */ +// Build configuration plugins { `quarkus-conventions` distribution @@ -34,7 +34,7 @@ dependencies { implementation(projects.opendcWeb.opendcWebProto) testImplementation("junit:junit:4.13.1") testImplementation("junit:junit:4.13.1") - compileOnly(projects.opendcWeb.opendcWebUiQuarkusDeployment) /* Temporary fix for Quarkus/Gradle issues */ + compileOnly(projects.opendcWeb.opendcWebUiQuarkusDeployment) // Temporary fix for Quarkus/Gradle issues compileOnly(projects.opendcWeb.opendcWebRunnerQuarkusDeployment) implementation(projects.opendcWeb.opendcWebUiQuarkus) implementation(projects.opendcWeb.opendcWebRunnerQuarkus) @@ -68,7 +68,7 @@ val createStartScripts by tasks.creating(CreateStartScripts::class) { applicationName = "opendc-server" mainClass.set("io.quarkus.bootstrap.runner.QuarkusEntryPoint") classpath = files("lib/quarkus-run.jar") - outputDir = project.buildDir.resolve("scripts") + outputDir = project.layout.buildDirectory.get().asFile.resolve("scripts") } distributions { diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Job.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Job.java index c5fb208e..a0ac390f 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Job.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Job.java @@ -22,23 +22,14 @@ package org.opendc.web.server.model; +import io.hypersistence.utils.hibernate.type.json.JsonType; import io.quarkus.hibernate.orm.panache.Panache; -import io.quarkus.hibernate.orm.panache.PanacheEntity; +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; import io.quarkus.hibernate.orm.panache.PanacheQuery; import io.quarkus.panache.common.Parameters; +import jakarta.persistence.*; import java.time.Instant; import java.util.Map; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.Table; import org.hibernate.annotations.Type; import org.opendc.web.proto.JobState; @@ -46,7 +37,7 @@ import org.opendc.web.proto.JobState; * A simulation job to be run by the simulator. */ @Entity -@Table(name = "jobs") +@Table @NamedQueries({ @NamedQuery( name = "Job.updateOne", @@ -57,7 +48,16 @@ import org.opendc.web.proto.JobState; WHERE j.id = :id AND j.state = :oldState """) }) -public class Job extends PanacheEntity { +public class Job extends PanacheEntityBase { + /** + * The main ID of a project. + * The value starts at 6 to account for the other 5 projects already made by the loading script. + */ + @Id + @SequenceGenerator(name = "jobSeq", sequenceName = "job_id_seq", allocationSize = 1, initialValue = 3) + @GeneratedValue(generator = "jobSeq") + public Long id; + @ManyToOne(optional = false, fetch = FetchType.EAGER) @JoinColumn(name = "scenario_id", foreignKey = @ForeignKey(name = "fk_jobs_scenario"), nullable = false) public Scenario scenario; @@ -83,9 +83,8 @@ public class Job extends PanacheEntity { /** * The state of the job. */ - @Type(type = "io.hypersistence.utils.hibernate.type.basic.PostgreSQLEnumType") - @Column(nullable = false, columnDefinition = "enum") @Enumerated(EnumType.STRING) + @Column(nullable = false) public JobState state = JobState.PENDING; /** @@ -97,8 +96,8 @@ public class Job extends PanacheEntity { /** * Experiment results in JSON */ - @Type(type = "io.hypersistence.utils.hibernate.type.json.JsonType") @Column(columnDefinition = "jsonb") + @Type(JsonType.class) public Map results = null; /** diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Portfolio.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Portfolio.java index 3a406683..c2695192 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Portfolio.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Portfolio.java @@ -22,23 +22,27 @@ package org.opendc.web.server.model; -import io.quarkus.hibernate.orm.panache.PanacheEntity; +import io.hypersistence.utils.hibernate.type.json.JsonType; +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; import io.quarkus.hibernate.orm.panache.PanacheQuery; import io.quarkus.panache.common.Parameters; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.NamedQueries; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OrderBy; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; import java.util.HashSet; import java.util.Set; -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.OneToMany; -import javax.persistence.OrderBy; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; import org.hibernate.annotations.Type; import org.opendc.web.proto.Targets; @@ -47,7 +51,6 @@ import org.opendc.web.proto.Targets; */ @Entity @Table( - name = "portfolios", uniqueConstraints = { @UniqueConstraint( name = "uk_portfolios_number", @@ -60,7 +63,17 @@ import org.opendc.web.proto.Targets; name = "Portfolio.findOneByProject", query = "SELECT p FROM Portfolio p WHERE p.project.id = :projectId AND p.number = :number") }) -public class Portfolio extends PanacheEntity { +public class Portfolio extends PanacheEntityBase { + + /** + * The main ID of a project. + * The value starts at 6 to account for the other 5 projects already made by the loading script. + */ + @Id + @SequenceGenerator(name = "portfolioSeq", sequenceName = "portfolio_id_seq", allocationSize = 1, initialValue = 4) + @GeneratedValue(generator = "portfolioSeq") + public Long id; + /** * The {@link Project} this portfolio belongs to. */ @@ -83,8 +96,8 @@ public class Portfolio extends PanacheEntity { /** * The portfolio targets (metrics, repetitions). */ - @Type(type = "io.hypersistence.utils.hibernate.type.json.JsonType") @Column(columnDefinition = "jsonb", nullable = false, updatable = false) + @Type(JsonType.class) public Targets targets; /** diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Project.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Project.java index 5836e33f..f4e5305d 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Project.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Project.java @@ -23,25 +23,28 @@ package org.opendc.web.server.model; import io.quarkus.hibernate.orm.panache.Panache; -import io.quarkus.hibernate.orm.panache.PanacheEntity; +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; import io.quarkus.panache.common.Parameters; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.NamedQueries; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OrderBy; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import java.time.Instant; import java.util.HashSet; import java.util.Set; -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.OneToMany; -import javax.persistence.OrderBy; -import javax.persistence.Table; /** * A project in OpenDC encapsulates all the datacenter designs and simulation runs for a set of users. */ @Entity -@Table(name = "projects") +@Table @NamedQueries({ @NamedQuery( name = "Project.findByUser", @@ -49,7 +52,7 @@ import javax.persistence.Table; """ SELECT a FROM ProjectAuthorization a - WHERE a.key.userId = :userId + WHERE a.key.userName = :userName """), @NamedQuery( name = "Project.allocatePortfolio", @@ -76,7 +79,17 @@ import javax.persistence.Table; WHERE p.id = :id AND p.scenariosCreated = :oldState """) }) -public class Project extends PanacheEntity { +public class Project extends PanacheEntityBase { + + /** + * The main ID of a project. + * The value starts at 6 to account for the other 5 projects already made by the loading script. + */ + @Id + @SequenceGenerator(name = "projectSeq", sequenceName = "project_id_seq", allocationSize = 1, initialValue = 7) + @GeneratedValue(generator = "projectSeq") + public Long id; + /** * The name of the project. */ diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/ProjectAuthorization.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/ProjectAuthorization.java index 1238f58d..3776ae12 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/ProjectAuthorization.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/ProjectAuthorization.java @@ -25,30 +25,29 @@ package org.opendc.web.server.model; import io.quarkus.hibernate.orm.panache.PanacheEntityBase; import io.quarkus.hibernate.orm.panache.PanacheQuery; import io.quarkus.panache.common.Parameters; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapsId; +import jakarta.persistence.NamedQueries; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.Table; import java.io.Serializable; import java.util.Objects; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.EmbeddedId; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.MapsId; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.Table; -import org.hibernate.annotations.Type; import org.opendc.web.proto.user.ProjectRole; /** * An authorization for some user to participate in a project. */ @Entity -@Table(name = "project_authorizations") +@Table @NamedQueries({ @NamedQuery( name = "ProjectAuthorization.findByUser", @@ -56,7 +55,7 @@ import org.opendc.web.proto.user.ProjectRole; """ SELECT a FROM ProjectAuthorization a - WHERE a.key.userId = :userId + WHERE a.key.userName = :userName """), }) public class ProjectAuthorization extends PanacheEntityBase { @@ -82,16 +81,15 @@ public class ProjectAuthorization extends PanacheEntityBase { /** * The role of the user in the project. */ - @Type(type = "io.hypersistence.utils.hibernate.type.basic.PostgreSQLEnumType") - @Column(nullable = false, columnDefinition = "enum") + @Column(nullable = false) @Enumerated(EnumType.STRING) public ProjectRole role; /** * Construct a {@link ProjectAuthorization} object. */ - public ProjectAuthorization(Project project, String userId, ProjectRole role) { - this.key = new ProjectAuthorization.Key(project.id, userId); + public ProjectAuthorization(Project project, String userName, ProjectRole role) { + this.key = new ProjectAuthorization.Key(project.id, userName); this.project = project; this.role = role; } @@ -102,25 +100,25 @@ public class ProjectAuthorization extends PanacheEntityBase { protected ProjectAuthorization() {} /** - * List all projects for the user with the specified userId. + * List all projects for the user with the specified userName. * - * @param userId The identifier of the user that is requesting the list of projects. + * @param userName The identifier of the user that is requesting the list of projects. * @return A query returning projects that the user has received authorization for. */ - public static PanacheQuery findByUser(String userId) { - return find("#ProjectAuthorization.findByUser", Parameters.with("userId", userId)); + public static PanacheQuery findByUser(String userName) { + return find("#ProjectAuthorization.findByUser", Parameters.with("userName", userName)); } /** - * Find the project with id for the user with the specified userId. + * Find the project with id for the user with the specified userName. * - * @param userId The identifier of the user that is requesting the list of projects. - * @param id The unique identifier of the project. + * @param userName The identifier of the user that is requesting the list of projects. + * @param project_id The unique identifier of the project. * @return The project with the specified identifier or null if it does not exist or is not accessible * to the user with the specified identifier. */ - public static ProjectAuthorization findByUser(String userId, long id) { - return findById(new ProjectAuthorization.Key(id, userId)); + public static ProjectAuthorization findByUser(String userName, long project_id) { + return findById(new ProjectAuthorization.Key(project_id, userName)); } /** @@ -148,12 +146,12 @@ public class ProjectAuthorization extends PanacheEntityBase { @Column(name = "project_id", nullable = false) public long projectId; - @Column(name = "user_id", nullable = false) - public String userId; + @Column(name = "user_name", nullable = false) + public String userName; - public Key(long projectId, String userId) { + public Key(long projectId, String userName) { this.projectId = projectId; - this.userId = userId; + this.userName = userName; } protected Key() {} @@ -163,12 +161,12 @@ public class ProjectAuthorization extends PanacheEntityBase { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Key key = (Key) o; - return projectId == key.projectId && userId.equals(key.userId); + return projectId == key.projectId && userName.equals(key.userName); } @Override public int hashCode() { - return Objects.hash(projectId, userId); + return Objects.hash(projectId, userName); } } } diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Scenario.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Scenario.java index 016e931b..c79ef5bb 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Scenario.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Scenario.java @@ -22,25 +22,13 @@ package org.opendc.web.server.model; -import io.quarkus.hibernate.orm.panache.PanacheEntity; +import io.hypersistence.utils.hibernate.type.json.JsonType; +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; import io.quarkus.hibernate.orm.panache.PanacheQuery; import io.quarkus.panache.common.Parameters; +import jakarta.persistence.*; import java.util.ArrayList; import java.util.List; -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Embedded; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.OneToMany; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; import org.hibernate.annotations.Type; import org.opendc.web.proto.OperationalPhenomena; @@ -49,7 +37,6 @@ import org.opendc.web.proto.OperationalPhenomena; */ @Entity @Table( - name = "scenarios", uniqueConstraints = { @UniqueConstraint( name = "uk_scenarios_number", @@ -71,7 +58,16 @@ import org.opendc.web.proto.OperationalPhenomena; name = "Scenario.findOneByProject", query = "SELECT s FROM Scenario s WHERE s.project.id = :projectId AND s.number = :number") }) -public class Scenario extends PanacheEntity { +public class Scenario extends PanacheEntityBase { + /** + * The main ID of a Scenario. + * The value starts at 3 to account for the other 2 scenarios already made by the loading script. + */ + @Id + @SequenceGenerator(name = "scenarioSeq", sequenceName = "scenario_id_seq", allocationSize = 1, initialValue = 3) + @GeneratedValue(generator = "scenarioSeq") + public Long id; + /** * The {@link Project} to which this scenario belongs. */ @@ -113,9 +109,11 @@ public class Scenario extends PanacheEntity { /** * Operational phenomena activated in the scenario. + * @Column(columnDefinition = "jsonb", nullable = false, updatable = false) + * @Type(JsonType.class) */ - @Type(type = "io.hypersistence.utils.hibernate.type.json.JsonType") @Column(columnDefinition = "jsonb", nullable = false, updatable = false) + @Type(JsonType.class) public OperationalPhenomena phenomena; /** diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Topology.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Topology.java index 05a1ac12..8a4e2ae2 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Topology.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Topology.java @@ -22,20 +22,24 @@ package org.opendc.web.server.model; -import io.quarkus.hibernate.orm.panache.PanacheEntity; +import io.hypersistence.utils.hibernate.type.json.JsonType; +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; import io.quarkus.hibernate.orm.panache.PanacheQuery; import io.quarkus.panache.common.Parameters; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.NamedQueries; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; import java.time.Instant; import java.util.List; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; import org.hibernate.annotations.Type; import org.opendc.web.proto.Room; @@ -44,7 +48,6 @@ import org.opendc.web.proto.Room; */ @Entity @Table( - name = "topologies", uniqueConstraints = { @UniqueConstraint( name = "uk_topologies_number", @@ -57,7 +60,16 @@ import org.opendc.web.proto.Room; name = "Topology.findOneByProject", query = "SELECT t FROM Topology t WHERE t.project.id = :projectId AND t.number = :number") }) -public class Topology extends PanacheEntity { +public class Topology extends PanacheEntityBase { + /** + * The main ID of a project. + * The value starts at 6 to account for the other 5 projects already made by the loading script. + */ + @Id + @SequenceGenerator(name = "topologySeq", sequenceName = "topology_id_seq", allocationSize = 1, initialValue = 5) + @GeneratedValue(generator = "topologySeq") + public Long id; + /** * The {@link Project} to which the topology belongs. */ @@ -91,9 +103,11 @@ public class Topology extends PanacheEntity { /** * Datacenter design in JSON + * @Column(columnDefinition = "jsonb", nullable = false) + * @Type(JsonType.class) */ - @Type(type = "io.hypersistence.utils.hibernate.type.json.JsonType") @Column(columnDefinition = "jsonb", nullable = false) + @Type(JsonType.class) public List rooms; /** diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Trace.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Trace.java index 36d27abc..71c647bc 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Trace.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Trace.java @@ -23,16 +23,16 @@ package org.opendc.web.server.model; import io.quarkus.hibernate.orm.panache.PanacheEntityBase; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; /** * A workload trace available for simulation. */ @Entity -@Table(name = "traces") +@Table public class Trace extends PanacheEntityBase { /** * The unique identifier of the trace. diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/UserAccounting.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/UserAccounting.java index fda4302f..10a10ef9 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/UserAccounting.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/UserAccounting.java @@ -24,19 +24,19 @@ package org.opendc.web.server.model; import io.quarkus.hibernate.orm.panache.PanacheEntityBase; import io.quarkus.panache.common.Parameters; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.NamedQueries; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.Table; import java.time.LocalDate; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.Table; /** * Entity to track the number of simulation minutes used by a user. */ @Entity -@Table(name = "user_accounting") +@Table @NamedQueries({ @NamedQuery( name = "UserAccounting.consumeBudget", diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Workload.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Workload.java index 129fb0c5..fd7010d2 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Workload.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Workload.java @@ -22,9 +22,9 @@ package org.opendc.web.server.model; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.ManyToOne; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.ManyToOne; /** * Specification of the workload for a {@link Scenario} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/SchedulerResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/SchedulerResource.java index 0fd58182..d7bb8f69 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/SchedulerResource.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/SchedulerResource.java @@ -22,10 +22,10 @@ package org.opendc.web.server.rest; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; import java.util.List; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; /** * A resource representing the available schedulers that can be used during experiments. diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/TraceResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/TraceResource.java index 7316c93f..daec01cd 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/TraceResource.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/TraceResource.java @@ -22,13 +22,13 @@ package org.opendc.web.server.rest; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; import java.util.List; import java.util.stream.Stream; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; import org.opendc.web.server.model.Trace; /** diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.java index 3b6be42e..345acdfe 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.java @@ -23,10 +23,10 @@ package org.opendc.web.server.rest.error; import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; import org.opendc.web.proto.ProtocolError; /** diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/WebApplicationExceptionMapper.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/WebApplicationExceptionMapper.java index ad1bb05e..e027e559 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/WebApplicationExceptionMapper.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/WebApplicationExceptionMapper.java @@ -22,11 +22,11 @@ package org.opendc.web.server.rest.error; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; import org.opendc.web.proto.ProtocolError; /** diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java index dff52526..4dde8654 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java @@ -22,17 +22,17 @@ package org.opendc.web.server.rest.runner; +import jakarta.annotation.security.RolesAllowed; +import jakarta.transaction.Transactional; +import jakarta.validation.Valid; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; import java.util.List; -import javax.annotation.security.RolesAllowed; -import javax.transaction.Transactional; -import javax.validation.Valid; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; import org.opendc.web.proto.JobState; import org.opendc.web.server.model.Job; import org.opendc.web.server.service.JobService; diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java index d1fc980d..2a3a40f4 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java @@ -23,19 +23,19 @@ package org.opendc.web.server.rest.user; import io.quarkus.security.identity.SecurityIdentity; +import jakarta.annotation.security.RolesAllowed; +import jakarta.transaction.Transactional; +import jakarta.validation.Valid; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; import java.time.Instant; import java.util.List; -import javax.annotation.security.RolesAllowed; -import javax.transaction.Transactional; -import javax.validation.Valid; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; import org.opendc.web.server.model.Portfolio; import org.opendc.web.server.model.ProjectAuthorization; diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java index a058cd31..789808c8 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java @@ -23,18 +23,18 @@ package org.opendc.web.server.rest.user; import io.quarkus.security.identity.SecurityIdentity; +import jakarta.annotation.security.RolesAllowed; +import jakarta.transaction.Transactional; +import jakarta.validation.Valid; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; import java.time.Instant; import java.util.List; -import javax.annotation.security.RolesAllowed; -import javax.transaction.Transactional; -import javax.validation.Valid; -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; import org.opendc.web.proto.JobState; import org.opendc.web.server.model.Job; import org.opendc.web.server.model.Portfolio; diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java index da47c3ff..ae1c959e 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java @@ -23,19 +23,19 @@ package org.opendc.web.server.rest.user; import io.quarkus.security.identity.SecurityIdentity; +import jakarta.annotation.security.RolesAllowed; +import jakarta.transaction.Transactional; +import jakarta.validation.Valid; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; import java.time.Instant; import java.util.List; -import javax.annotation.security.RolesAllowed; -import javax.transaction.Transactional; -import javax.validation.Valid; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; import org.opendc.web.proto.user.ProjectRole; import org.opendc.web.server.model.Project; import org.opendc.web.server.model.ProjectAuthorization; @@ -96,9 +96,9 @@ public final class ProjectResource { */ @GET @Path("{project}") - public org.opendc.web.proto.user.Project get(@PathParam("project") long id) { + public org.opendc.web.proto.user.Project get(@PathParam("project") long project_id) { ProjectAuthorization auth = - ProjectAuthorization.findByUser(identity.getPrincipal().getName(), id); + ProjectAuthorization.findByUser(identity.getPrincipal().getName(), project_id); if (auth == null) { throw new WebApplicationException("Project not found", 404); diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ScenarioResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ScenarioResource.java index cf933c32..bb3eb89b 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ScenarioResource.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ScenarioResource.java @@ -23,15 +23,15 @@ package org.opendc.web.server.rest.user; import io.quarkus.security.identity.SecurityIdentity; +import jakarta.annotation.security.RolesAllowed; +import jakarta.transaction.Transactional; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; import java.util.List; -import javax.annotation.security.RolesAllowed; -import javax.transaction.Transactional; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; import org.opendc.web.server.model.ProjectAuthorization; import org.opendc.web.server.model.Scenario; diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java index 71491801..b8c542d3 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java @@ -24,21 +24,21 @@ package org.opendc.web.server.rest.user; import io.quarkus.hibernate.orm.panache.Panache; import io.quarkus.security.identity.SecurityIdentity; +import jakarta.annotation.security.RolesAllowed; +import jakarta.persistence.PersistenceException; +import jakarta.transaction.Transactional; +import jakarta.validation.Valid; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.WebApplicationException; import java.time.Instant; import java.util.List; -import javax.annotation.security.RolesAllowed; -import javax.persistence.PersistenceException; -import javax.transaction.Transactional; -import javax.validation.Valid; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.WebApplicationException; import org.opendc.web.server.model.Project; import org.opendc.web.server.model.ProjectAuthorization; import org.opendc.web.server.model.Topology; diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/UserResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/UserResource.java index c3fb2866..c8cda2b7 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/UserResource.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/UserResource.java @@ -23,10 +23,10 @@ package org.opendc.web.server.rest.user; import io.quarkus.security.identity.SecurityIdentity; -import javax.annotation.security.RolesAllowed; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; +import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; import org.opendc.web.proto.user.User; import org.opendc.web.proto.user.UserAccounting; import org.opendc.web.server.service.UserAccountingService; diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/JobService.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/JobService.java index ed0eaf9c..70933520 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/JobService.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/JobService.java @@ -22,9 +22,9 @@ package org.opendc.web.server.service; +import jakarta.enterprise.context.ApplicationScoped; import java.time.Instant; import java.util.Map; -import javax.enterprise.context.ApplicationScoped; import org.opendc.web.proto.JobState; import org.opendc.web.server.model.Job; diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/UserAccountingService.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/UserAccountingService.java index e5003cb4..73fa2a3e 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/UserAccountingService.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/UserAccountingService.java @@ -22,11 +22,11 @@ package org.opendc.web.server.service; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.persistence.EntityExistsException; import java.time.Duration; import java.time.LocalDate; import java.time.temporal.TemporalAdjusters; -import javax.enterprise.context.ApplicationScoped; -import javax.persistence.EntityExistsException; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.opendc.web.server.model.UserAccounting; diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/DevSecurityOverrideFilter.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/DevSecurityOverrideFilter.java index de4478cb..103f868d 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/DevSecurityOverrideFilter.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/DevSecurityOverrideFilter.java @@ -23,12 +23,12 @@ package org.opendc.web.server.util; import io.quarkus.arc.properties.IfBuildProperty; +import jakarta.ws.rs.container.ContainerRequestContext; +import jakarta.ws.rs.container.ContainerRequestFilter; +import jakarta.ws.rs.container.PreMatching; +import jakarta.ws.rs.core.SecurityContext; +import jakarta.ws.rs.ext.Provider; import java.security.Principal; -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.container.ContainerRequestFilter; -import javax.ws.rs.container.PreMatching; -import javax.ws.rs.core.SecurityContext; -import javax.ws.rs.ext.Provider; /** * Helper class to disable security for the OpenDC web API when in development mode. diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/KotlinModuleCustomizer.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/KotlinModuleCustomizer.java index c30edcbf..ff3ba1cd 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/KotlinModuleCustomizer.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/KotlinModuleCustomizer.java @@ -25,7 +25,7 @@ package org.opendc.web.server.util; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.module.kotlin.KotlinModule; import io.quarkus.jackson.ObjectMapperCustomizer; -import javax.inject.Singleton; +import jakarta.inject.Singleton; /** * Helper class to register the Kotlin Jackson module. diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/QuarkusObjectMapperSupplier.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/QuarkusObjectMapperSupplier.java index e46c74ed..60ca77e5 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/QuarkusObjectMapperSupplier.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/QuarkusObjectMapperSupplier.java @@ -25,7 +25,7 @@ package org.opendc.web.server.util; import com.fasterxml.jackson.databind.ObjectMapper; import io.hypersistence.utils.hibernate.type.util.ObjectMapperSupplier; import io.quarkus.runtime.annotations.RegisterForReflection; -import javax.enterprise.inject.spi.CDI; +import jakarta.enterprise.inject.spi.CDI; /** * A supplier for an {@link ObjectMapper} used by the Hypersistence utilities. diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/runner/QuarkusJobManager.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/runner/QuarkusJobManager.java index 0331eacf..47d397f3 100644 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/runner/QuarkusJobManager.java +++ b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/runner/QuarkusJobManager.java @@ -22,9 +22,9 @@ package org.opendc.web.server.util.runner; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.transaction.Transactional; import java.util.Map; -import javax.enterprise.context.ApplicationScoped; -import javax.transaction.Transactional; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.opendc.web.proto.JobState; diff --git a/opendc-web/opendc-web-server/src/main/resources/application-test.properties b/opendc-web/opendc-web-server/src/main/resources/application-test.properties index bee17221..4e3063e4 100644 --- a/opendc-web/opendc-web-server/src/main/resources/application-test.properties +++ b/opendc-web/opendc-web-server/src/main/resources/application-test.properties @@ -37,3 +37,7 @@ quarkus.swagger-ui.enable=false # Disable OpenDC web UI and runner quarkus.opendc-ui.include=false quarkus.opendc-runner.include=false + +# Create new tables and fill them +quarkus.hibernate-orm.database.generation=drop-and-create +quarkus.hibernate-orm.sql-load-script=load_data.sql diff --git a/opendc-web/opendc-web-server/src/main/resources/db/migration/V3.0__core.sql b/opendc-web/opendc-web-server/src/main/resources/db/migration/V3.0__core.sql deleted file mode 100644 index 40654b6b..00000000 --- a/opendc-web/opendc-web-server/src/main/resources/db/migration/V3.0__core.sql +++ /dev/null @@ -1,160 +0,0 @@ --- Hibernate sequence for unique identifiers -create sequence hibernate_sequence start with 1 increment by 1; - --- Projects -create table projects -( - id bigint not null, - created_at timestamp not null, - name varchar(255) not null, - portfolios_created integer not null default 0, - scenarios_created integer not null default 0, - topologies_created integer not null default 0, - updated_at timestamp not null, - primary key (id) -); - -create type project_role as enum ('OWNER', 'EDITOR', 'VIEWER'); - --- Project authorizations authorize users specific permissions to a project. -create table project_authorizations -( - project_id bigint not null, - user_id varchar(255) not null, - role project_role not null, - primary key (project_id, user_id) -); - --- Topologies represent the datacenter designs created by users. -create table topologies -( - id bigint not null, - created_at timestamp not null, - name varchar(255) not null, - number integer not null, - rooms jsonb not null, - updated_at timestamp not null, - project_id bigint not null, - primary key (id) -); - --- Portfolios -create table portfolios -( - id bigint not null, - name varchar(255) not null, - number integer not null, - targets jsonb not null, - project_id bigint not null, - primary key (id) -); - -create table scenarios -( - id bigint not null, - name varchar(255) not null, - number integer not null, - phenomena jsonb not null, - scheduler_name varchar(255) not null, - sampling_fraction double precision not null, - portfolio_id bigint not null, - project_id bigint not null, - topology_id bigint not null, - trace_id varchar(255) not null, - primary key (id) -); - -create type job_state as enum ('PENDING', 'CLAIMED', 'RUNNING', 'FINISHED', 'FAILED'); - -create table jobs -( - id bigint not null, - created_by varchar(255) not null, - created_at timestamp not null, - repeats integer not null, - results jsonb, - state job_state not null default 'PENDING', - runtime integer not null default 0, - updated_at timestamp not null, - scenario_id bigint not null, - primary key (id) -); - --- User accounting -create table user_accounting -( - user_id varchar(255) not null, - period_end date not null, - simulation_time integer not null, - simulation_time_budget integer not null, - primary key (user_id) -); - --- Workload traces available to the user. -create table traces -( - id varchar(255) not null, - name varchar(255) not null, - type varchar(255) not null, - primary key (id) -); - --- Relations -alter table project_authorizations - add constraint fk_project_authorizations - foreign key (project_id) - references projects; - -create index ux_topologies_number on topologies (project_id, number); - -alter table topologies - add constraint uk_topologies_number unique (project_id, number); - -alter table topologies - add constraint fk_topologies_project - foreign key (project_id) - references projects; - -create index ux_portfolios_number on portfolios (project_id, number); - -alter table portfolios - add constraint fk_portfolios_project - foreign key (project_id) - references projects; - -alter table portfolios - add constraint uk_portfolios_number unique (project_id, number); - -create index ux_scenarios_number on scenarios (project_id, number); - -alter table scenarios - add constraint uk_scenarios_number unique (project_id, number); - -alter table scenarios - add constraint fk_scenarios_project - foreign key (project_id) - references projects; - -alter table scenarios - add constraint fk_scenarios_topology - foreign key (topology_id) - references topologies; - -alter table scenarios - add constraint fk_scenarios_portfolio - foreign key (portfolio_id) - references portfolios; - -alter table scenarios - add constraint fk_scenarios_trace - foreign key (trace_id) - references traces; - -alter table jobs - add constraint fk_scenarios_job - foreign key (scenario_id) - references scenarios; - --- Initial data -insert into traces (id, name, type) -values ('bitbrains-small', 'Bitbrains Small', 'vm'); diff --git a/opendc-web/opendc-web-server/src/main/resources/db/testing/V3.0.1__entities.sql b/opendc-web/opendc-web-server/src/main/resources/db/testing/V3.0.1__entities.sql deleted file mode 100644 index 1b702f4e..00000000 --- a/opendc-web/opendc-web-server/src/main/resources/db/testing/V3.0.1__entities.sql +++ /dev/null @@ -1,24 +0,0 @@ --- Test entities - -alter sequence hibernate_sequence restart with 500; - -insert into projects (id, created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at) -values (1, current_timestamp(), 'Test Project', 1, 2, 1, current_timestamp()); -insert into project_authorizations (project_id, user_id, role) -values (1, 'owner', 'OWNER'), - (1, 'editor', 'EDITOR'), - (1, 'viewer', 'VIEWER'); - -insert into portfolios (id, name, number, targets, project_id) -values (1, 'Test Portfolio', 1, '{ "metrics": [] }' format json, 1); - -insert into topologies (id, created_at, name, number, rooms, updated_at, project_id) -values (1, current_timestamp(), 'Test Topology', 1, '[]' format json, current_timestamp(), 1); - -insert into scenarios (id, name, number, phenomena, scheduler_name, sampling_fraction, portfolio_id, project_id, topology_id, trace_id) -values (1, 'Test Scenario', 1, '{ "failures": false, "interference": false }' format json, 'mem', 1.0, 1, 1, 1, 'bitbrains-small'), - (2, 'Test Scenario', 2, '{ "failures": false, "interference": false }' format json, 'mem', 1.0, 1, 1, 1, 'bitbrains-small'); - -insert into jobs (id, created_by, created_at, repeats, updated_at, scenario_id) -values (1, 'owner', current_timestamp(), 1, current_timestamp(), 1), - (2, 'owner', current_timestamp(), 1, current_timestamp(), 2); diff --git a/opendc-web/opendc-web-server/src/main/resources/load_data.sql b/opendc-web/opendc-web-server/src/main/resources/load_data.sql new file mode 100644 index 00000000..72396cef --- /dev/null +++ b/opendc-web/opendc-web-server/src/main/resources/load_data.sql @@ -0,0 +1,124 @@ + +-- Insert data + +INSERT INTO PROJECT (created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at, id) + VALUES ('2024-03-01T15:31:41.579969Z', 'Test Project 1', 0, 0, 0, '2024-03-01T15:31:41.579969Z', 1); + +INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) +VALUES ('OWNER', 1, 'test_user_1'); + +-- Add test user 2 as a viewer for project 1 + +INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) +VALUES ('VIEWER', 1, 'test_user_2'); + +-- Add test user 3 as an editor for project 1 + +INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) +VALUES ('EDITOR', 1, 'test_user_3'); + +-- Create a project for test user 2 + +INSERT INTO PROJECT (created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at, id) +VALUES ('2024-03-01T15:31:41.579969Z', 'Test Project 2', 0, 0, 0, '2024-03-01T15:31:41.579969Z', 2); + +INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) +VALUES ('OWNER', 2, 'test_user_2'); + +-- Create three projects for test user 3. User 3 has multiple projects to test getAll + +INSERT INTO PROJECT (created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at, id) +VALUES ('2024-03-01T15:31:41.579969Z', 'Test Project 3', 0, 0, 0, '2024-03-01T15:31:41.579969Z', 3); + +INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) +VALUES ('OWNER', 3, 'test_user_3'); + +INSERT INTO PROJECT (created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at, id) +VALUES ('2024-03-01T15:31:41.579969Z', 'Test Project 4', 0, 0, 0, '2024-03-01T15:31:41.579969Z', 4); + +INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) +VALUES ('OWNER', 4, 'test_user_3'); + +INSERT INTO PROJECT (created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at, id) +VALUES ('2024-03-01T15:31:41.579969Z', 'Test Project 5', 0, 0, 0, '2024-03-01T15:31:41.579969Z', 5); + +INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) +VALUES ('OWNER', 5, 'test_user_3'); + +-- Project to delete + +INSERT INTO PROJECT (created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at, id) +VALUES ('2024-03-01T15:31:41.579969Z', 'Test Project Delete', 0, 0, 0, '2024-03-01T15:31:41.579969Z', 6); + +INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) +VALUES ('OWNER', 6, 'test_user_1'); + +-- -------------------------------------------------------------------------------- +-- PortFolios +-- -------------------------------------------------------------------------------- + +-- Add Portfolio to project 1 +INSERT INTO PORTFOLIO (name, number, project_id, targets, id) +VALUES ('Test PortFolio Base', 1, 1, '{"metrics": [], "repeats":1}' FORMAT JSON, 1); + +INSERT INTO PORTFOLIO (name, number, project_id, targets, id) +VALUES ('Test PortFolio Delete', 2, 1, '{"metrics": [], "repeats":1}' FORMAT JSON, 2); + +INSERT INTO PORTFOLIO (name, number, project_id, targets, id) +VALUES ('Test PortFolio DeleteEditor', 3, 1, '{"metrics": [], "repeats":1}' FORMAT JSON, 3); + +UPDATE Project p +SET p.portfolios_created = 3, p.updated_at = '2024-03-01T15:31:41.579969Z' +WHERE p.id = 1; + +-- -------------------------------------------------------------------------------- +-- Topologies +-- -------------------------------------------------------------------------------- + +INSERT INTO TOPOLOGY (created_at, name, number, project_id, rooms, updated_at, id) +VALUES ('2024-03-01T15:31:41.579969Z', 'Test Topology testUpdate', 1, 1, '[]' FORMAT JSON, '2024-03-01T15:31:41.579969Z', 1); + +INSERT INTO TOPOLOGY (created_at, name, number, project_id, rooms, updated_at, id) +VALUES ('2024-03-01T15:31:41.579969Z', 'Test Topology testDeleteAsEditor', 2, 1, '[]' FORMAT JSON, '2024-03-01T15:31:41.579969Z', 2); + +INSERT INTO TOPOLOGY (created_at, name, number, project_id, rooms, updated_at, id) +VALUES ('2024-03-01T15:31:41.579969Z', 'Test Topology testDelete', 3, 1, '[]' FORMAT JSON, '2024-03-01T15:31:41.579969Z', 3); + +INSERT INTO TOPOLOGY (created_at, name, number, project_id, rooms, updated_at, id) +VALUES ('2024-03-01T15:31:41.579969Z', 'Test Topology testDeleteUsed', 4, 1, '[]' FORMAT JSON, '2024-03-01T15:31:41.579969Z', 4); + +UPDATE Project p +SET p.topologies_created = 4, p.updated_at = '2024-03-01T15:31:41.579969Z' +WHERE p.id = 1; + +-- -------------------------------------------------------------------------------- +-- Traces +-- -------------------------------------------------------------------------------- + +INSERT INTO TRACE (id, name, type) +VALUES ('bitbrains-small', 'Bitbrains Small', 'small'); + +-- -------------------------------------------------------------------------------- +-- Scenario +-- -------------------------------------------------------------------------------- + +INSERT INTO SCENARIO (name, number, phenomena, portfolio_id, project_id, scheduler_name, topology_id, sampling_fraction, trace_id, id) +VALUES ('Test Scenario testDelete', 1, '{"failures": false, "interference": false}' FORMAT JSON, 1, 1, 'test', 1, 1.0, 'bitbrains-small', 1); + +INSERT INTO SCENARIO (name, number, phenomena, portfolio_id, project_id, scheduler_name, topology_id, sampling_fraction, trace_id, id) +VALUES ('Test Scenario testDeleteUsed', 2, '{"failures": false, "interference": false}' FORMAT JSON, 1, 1, 'test', 4, 1.0, 'bitbrains-small', 2); + + +UPDATE Project p +SET p.scenarios_created = 2, p.updated_at = '2024-03-01T15:31:41.579969Z' +WHERE p.id = 1; + +-- -------------------------------------------------------------------------------- +-- Job +-- -------------------------------------------------------------------------------- + +INSERT INTO JOB (scenario_id, created_by, created_at, repeats, updated_at, state, runtime, results, id) +VALUES (1, 'test_user_1', '2024-03-01T15:31:41.579969Z', 1, '2024-03-01T15:31:41.579969Z', 'PENDING', 1, '{}' FORMAT JSON, 1); + +INSERT INTO JOB (scenario_id, created_by, created_at, repeats, updated_at, state, runtime, results, id) +VALUES (1, 'test_user_1', '2024-03-01T15:31:41.579969Z', 1, '2024-03-01T15:31:41.579969Z', 'PENDING', 1, '{}' FORMAT JSON, 2); diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/SchedulerResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/SchedulerResourceTest.java index feeac4d3..f52ede3a 100644 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/SchedulerResourceTest.java +++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/SchedulerResourceTest.java @@ -22,11 +22,10 @@ package org.opendc.web.server.rest; -import static io.restassured.RestAssured.when; +import static io.restassured.RestAssured.given; import io.quarkus.test.common.http.TestHTTPEndpoint; import io.quarkus.test.junit.QuarkusTest; -import io.restassured.http.ContentType; import org.junit.jupiter.api.Test; /** @@ -40,6 +39,6 @@ public final class SchedulerResourceTest { */ @Test public void testGetSchedulers() { - when().get().then().statusCode(200).contentType(ContentType.JSON); + given().get().then().statusCode(200); } } diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/TraceResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/TraceResourceTest.java index 5c5976db..9da26059 100644 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/TraceResourceTest.java +++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/TraceResourceTest.java @@ -41,7 +41,7 @@ public final class TraceResourceTest { */ @Test public void testGetAllEmpty() { - when().get().then().statusCode(200).contentType(ContentType.JSON); + when().get().then().statusCode(200); } /** @@ -49,7 +49,7 @@ public final class TraceResourceTest { */ @Test public void testGetNonExisting() { - when().get("/unknown").then().statusCode(404).contentType(ContentType.JSON); + when().get("/unknown").then().statusCode(404); } /** diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/runner/JobResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/runner/JobResourceTest.java index 94b2cef0..09f60c0a 100644 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/runner/JobResourceTest.java +++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/runner/JobResourceTest.java @@ -23,7 +23,6 @@ package org.opendc.web.server.rest.runner; import static io.restassured.RestAssured.given; -import static io.restassured.RestAssured.when; import static org.hamcrest.Matchers.equalTo; import io.quarkus.test.common.http.TestHTTPEndpoint; @@ -44,7 +43,7 @@ public final class JobResourceTest { */ @Test public void testQueryWithoutToken() { - when().get().then().statusCode(401); + given().get().then().statusCode(401); } /** @@ -52,10 +51,10 @@ public final class JobResourceTest { */ @Test @TestSecurity( - user = "test", + user = "test_user_1", roles = {"openid"}) public void testQueryInvalidScope() { - when().get().then().statusCode(403); + given().get().then().statusCode(403); } /** @@ -63,10 +62,10 @@ public final class JobResourceTest { */ @Test @TestSecurity( - user = "test", + user = "test_user_1", roles = {"runner"}) public void testQuery() { - when().get().then().statusCode(200).contentType(ContentType.JSON).body("get(0).state", equalTo("PENDING")); + given().get().then().statusCode(200).contentType(ContentType.JSON).body("get(0).state", equalTo("PENDING")); } /** @@ -74,10 +73,10 @@ public final class JobResourceTest { */ @Test @TestSecurity( - user = "test", + user = "test_user_1", roles = {"runner"}) public void testGetNonExisting() { - when().get("/0").then().statusCode(404).contentType(ContentType.JSON); + given().get("/0").then().statusCode(404); } /** @@ -85,10 +84,10 @@ public final class JobResourceTest { */ @Test @TestSecurity( - user = "test", + user = "test_user_1", roles = {"runner"}) public void testGetExisting() { - when().get("/1").then().statusCode(200).contentType(ContentType.JSON).body("id", equalTo(1)); + given().get("/1").then().statusCode(200).contentType(ContentType.JSON).body("id", equalTo(1)); } /** @@ -96,7 +95,7 @@ public final class JobResourceTest { */ @Test @TestSecurity( - user = "test", + user = "test_user_1", roles = {"runner"}) public void testUpdateNonExistent() { given().body(new org.opendc.web.proto.runner.Job.Update(JobState.PENDING, 0, null)) @@ -113,7 +112,7 @@ public final class JobResourceTest { */ @Test @TestSecurity( - user = "test", + user = "test_user_1", roles = {"runner"}) public void testUpdateState() { given().body(new org.opendc.web.proto.runner.Job.Update(JobState.CLAIMED, 0, null)) @@ -131,7 +130,7 @@ public final class JobResourceTest { */ @Test @TestSecurity( - user = "test", + user = "test_user_1", roles = {"runner"}) public void testUpdateInvalidInput() { given().body("{ \"test\": \"test\" }") diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioResourceTest.java index a952d83f..f23b4fc4 100644 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioResourceTest.java +++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioResourceTest.java @@ -40,33 +40,55 @@ import org.opendc.web.proto.Targets; @TestHTTPEndpoint(PortfolioResource.class) public final class PortfolioResourceTest { /** - * Test that tries to obtain the list of portfolios belonging to a project. + * Test that tries to obtain the list of all portfolios belonging to a project. */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) - public void testGetForProject() { - given().pathParam("project", 1).when().get().then().statusCode(200).contentType(ContentType.JSON); + public void testGetAllForProject() { + given().pathParam("project", 1).when().get().then().statusCode(200); } /** - * Test that tries to obtain the list of portfolios belonging to a project without authorization. + * Test that tries to obtain the list of all portfolios belonging to a project + * without authorization. + * + * TODO: Why is this an empty list, and not a 403 message? */ @Test @TestSecurity( - user = "unknown", + user = "test_user_1", roles = {"openid"}) - public void testGetForProjectNoAuthorization() { - given().pathParam("project", 1).when().get().then().statusCode(200).contentType(ContentType.JSON); + public void testGetAllForProjectNoAuthorization() { + given().pathParam("project", 1).when().get().then().statusCode(200); } /** - * Test that tries to create a topology for a project. + * Test that tries to create a portfolio for a project that exists and user has permission. */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", + roles = {"openid"}) + public void testCreate() { + given().pathParam("project", "1") + .body(new org.opendc.web.proto.user.Portfolio.Create("Test Portfolio New", new Targets(Set.of(), 1))) + .contentType(ContentType.JSON) + .when() + .post() + .then() + .statusCode(200) + .contentType(ContentType.JSON) + .body("name", equalTo("Test Portfolio New")); + } + + /** + * Test that tries to create a topology for a project that does not exist. + */ + @Test + @TestSecurity( + user = "test_user_1", roles = {"openid"}) public void testCreateNonExistent() { given().pathParam("project", "0") @@ -75,45 +97,42 @@ public final class PortfolioResourceTest { .when() .post() .then() - .statusCode(404) - .contentType(ContentType.JSON); + .statusCode(404); } /** - * Test that tries to create a topology for a project. + * Test that tries to create a portfolio for a project that does exist but the user does not have permission. */ @Test @TestSecurity( - user = "viewer", + user = "test_user_2", roles = {"openid"}) - public void testCreateNotPermitted() { + public void testCreateViewer() { given().pathParam("project", "1") .body(new org.opendc.web.proto.user.Portfolio.Create("test", new Targets(Set.of(), 1))) .contentType(ContentType.JSON) .when() .post() .then() - .statusCode(403) - .contentType(ContentType.JSON); + .statusCode(403); } /** - * Test that tries to create a portfolio for a project. + * Test that tries to create a portfolio for a project that does exist but the user does not have permission. + * TODO: This should return 403 but does not because there is no user class */ @Test @TestSecurity( - user = "editor", + user = "test_user_1", roles = {"openid"}) - public void testCreate() { - given().pathParam("project", "1") + public void testCreateNotPermitted() { + given().pathParam("project", "3") .body(new org.opendc.web.proto.user.Portfolio.Create("test", new Targets(Set.of(), 1))) .contentType(ContentType.JSON) .when() .post() .then() - .statusCode(200) - .contentType(ContentType.JSON) - .body("name", equalTo("test")); + .statusCode(404); } /** @@ -121,7 +140,7 @@ public final class PortfolioResourceTest { */ @Test @TestSecurity( - user = "editor", + user = "test_user_1", roles = {"openid"}) public void testCreateEmpty() { given().pathParam("project", "1") @@ -130,8 +149,7 @@ public final class PortfolioResourceTest { .when() .post() .then() - .statusCode(400) - .contentType(ContentType.JSON); + .statusCode(400); } /** @@ -139,7 +157,7 @@ public final class PortfolioResourceTest { */ @Test @TestSecurity( - user = "editor", + user = "test_user_1", roles = {"openid"}) public void testCreateBlankName() { given().pathParam("project", "1") @@ -148,8 +166,7 @@ public final class PortfolioResourceTest { .when() .post() .then() - .statusCode(400) - .contentType(ContentType.JSON); + .statusCode(400); } /** @@ -165,7 +182,7 @@ public final class PortfolioResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"runner"}) public void testGetInvalidToken() { given().pathParam("project", "1").when().get("/1").then().statusCode(403); @@ -176,15 +193,10 @@ public final class PortfolioResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testGetNonExisting() { - given().pathParam("project", "1") - .when() - .get("/0") - .then() - .statusCode(404) - .contentType(ContentType.JSON); + given().pathParam("project", "1").when().get("/0").then().statusCode(404); } /** @@ -192,15 +204,10 @@ public final class PortfolioResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testGetNonExistingProject() { - given().pathParam("project", "0") - .when() - .get("/1") - .then() - .statusCode(404) - .contentType(ContentType.JSON); + given().pathParam("project", "0").when().get("/1").then().statusCode(404); } /** @@ -208,7 +215,7 @@ public final class PortfolioResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testGetExisting() { given().pathParam("project", "1") @@ -225,7 +232,7 @@ public final class PortfolioResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testDeleteNonExistent() { given().pathParam("project", "1").when().delete("/0").then().statusCode(404); @@ -236,7 +243,7 @@ public final class PortfolioResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testDeleteNonExistentProject() { given().pathParam("project", "0").when().delete("/1").then().statusCode(404); @@ -247,26 +254,21 @@ public final class PortfolioResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testDelete() { - int number = given().pathParam("project", "1") - .body(new org.opendc.web.proto.user.Portfolio.Create("Delete Portfolio", new Targets(Set.of(), 1))) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(200) - .contentType(ContentType.JSON) - .extract() - .path("number"); + given().pathParam("project", "1").when().delete("/2").then().statusCode(200); + } - given().pathParam("project", "1") - .when() - .delete("/" + number) - .then() - .statusCode(200) - .contentType(ContentType.JSON); + /** + * Test to delete a portfolio as an editor. + */ + @Test + @TestSecurity( + user = "test_user_3", + roles = {"openid"}) + public void testDeleteEditor() { + given().pathParam("project", "1").when().delete("/3").then().statusCode(200); } /** @@ -274,14 +276,9 @@ public final class PortfolioResourceTest { */ @Test @TestSecurity( - user = "viewer", + user = "test_user_2", roles = {"openid"}) public void testDeleteAsViewer() { - given().pathParam("project", "1") - .when() - .delete("/1") - .then() - .statusCode(403) - .contentType(ContentType.JSON); + given().pathParam("project", "1").when().delete("/1").then().statusCode(403); } } diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.java index 58042833..270dbae9 100644 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.java +++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.java @@ -43,7 +43,7 @@ public final class PortfolioScenarioResourceTest { /** * Test that tries to obtain a portfolio without token. */ - // @Test + @Test public void testGetWithoutToken() { given().pathParam("project", "1") .pathParam("portfolio", "1") @@ -58,7 +58,7 @@ public final class PortfolioScenarioResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"runner"}) public void testGetInvalidToken() { given().pathParam("project", "1") @@ -72,12 +72,12 @@ public final class PortfolioScenarioResourceTest { /** * Test that tries to obtain a scenario without authorization. */ - // @Test - // @TestSecurity( - // user = "unknown", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testGetUnauthorized() { - given().pathParam("project", "1") + given().pathParam("project", "2") .pathParam("portfolio", "1") .when() .get() @@ -88,28 +88,28 @@ public final class PortfolioScenarioResourceTest { /** * Test that tries to obtain a scenario. + * TODO: shouldn't this be all scenarios? */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testGet() { given().pathParam("project", "1") .pathParam("portfolio", "1") .when() .get() .then() - .statusCode(200) - .contentType(ContentType.JSON); + .statusCode(200); } /** - * Test that tries to create a scenario for a portfolio. + * Test that tries to create a scenario for a portfolio that does not exist in a project that can be accessed. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreateNonExistent() { given().pathParam("project", "1") .pathParam("portfolio", "0") @@ -126,13 +126,13 @@ public final class PortfolioScenarioResourceTest { /** * Test that tries to create a scenario for a portfolio without authorization. */ - // @Test - // @TestSecurity( - // user = "unknown", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreateUnauthorized() { - given().pathParam("project", "1") - .pathParam("portfolio", "0") + given().pathParam("project", "2") + .pathParam("portfolio", "1") .body(new Scenario.Create( "test", new Workload.Spec("test", 1.0), 1, new OperationalPhenomena(false, false), "test")) .contentType(ContentType.JSON) @@ -146,13 +146,13 @@ public final class PortfolioScenarioResourceTest { /** * Test that tries to create a scenario for a portfolio as a viewer. */ - // @Test - // @TestSecurity( - // user = "viewer", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_2", + roles = {"openid"}) public void testCreateAsViewer() { given().pathParam("project", "1") - .pathParam("portfolio", "0") + .pathParam("portfolio", "1") .body(new Scenario.Create( "test", new Workload.Spec("test", 1.0), 1, new OperationalPhenomena(false, false), "test")) .contentType(ContentType.JSON) @@ -166,15 +166,15 @@ public final class PortfolioScenarioResourceTest { /** * Test that tries to create a scenario for a portfolio. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreate() { given().pathParam("project", "1") .pathParam("portfolio", "1") .body(new Scenario.Create( - "test", + "Test Scenario New", new Workload.Spec("bitbrains-small", 1.0), 1, new OperationalPhenomena(false, false), @@ -185,16 +185,16 @@ public final class PortfolioScenarioResourceTest { .then() .statusCode(200) .contentType(ContentType.JSON) - .body("name", equalTo("test")); + .body("name", equalTo("Test Scenario New")); } /** * Test to create a project with an empty body. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreateEmpty() { given().pathParam("project", "1") .pathParam("portfolio", "1") @@ -210,10 +210,10 @@ public final class PortfolioScenarioResourceTest { /** * Test to create a project with a blank name. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreateBlankName() { given().pathParam("project", "1") .pathParam("portfolio", "1") @@ -228,12 +228,12 @@ public final class PortfolioScenarioResourceTest { } /** - * Test that tries to create a scenario for a portfolio. + * Test that tries to create a scenario for a portfolio with an unknown Topology. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreateUnknownTopology() { given().pathParam("project", "1") .pathParam("portfolio", "1") @@ -252,12 +252,12 @@ public final class PortfolioScenarioResourceTest { } /** - * Test that tries to create a scenario for a portfolio. + * Test that tries to create a scenario for a portfolio with an unknown Trace. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreateUnknownTrace() { given().pathParam("project", "1") .pathParam("portfolio", "1") diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ProjectResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ProjectResourceTest.java index bd7cff9b..450c0c0c 100644 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ProjectResourceTest.java +++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ProjectResourceTest.java @@ -24,7 +24,7 @@ package org.opendc.web.server.rest.user; import static io.restassured.RestAssured.given; import static io.restassured.RestAssured.when; -import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.*; import io.quarkus.test.common.http.TestHTTPEndpoint; import io.quarkus.test.junit.QuarkusTest; @@ -41,7 +41,7 @@ public final class ProjectResourceTest { /** * Test that tries to obtain all projects without token. */ - // @Test + @Test public void testGetAllWithoutToken() { when().get().then().statusCode(401); } @@ -51,30 +51,41 @@ public final class ProjectResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"runner"}) public void testGetAllWithInvalidScope() { when().get().then().statusCode(403); } + /** + * Test that tries to obtain when no projects have yet been made. + */ + @Test + @TestSecurity( + user = "test_user_4", + roles = {"openid"}) + public void testGetAllWithNoAvailableProjects() { + when().get().then().statusCode(200).contentType(ContentType.JSON).body("", empty()); + } + /** * Test that tries to obtain all project for a user. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_3", + roles = {"openid"}) public void testGetAll() { - when().get().then().statusCode(200).contentType(ContentType.JSON).body("get(0).name", equalTo("Test Project")); + given().get().then().statusCode(200).contentType(ContentType.JSON).body("", hasSize(4)); } /** * Test that tries to obtain a non-existent project. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testGetNonExisting() { when().get("/0").then().statusCode(404).contentType(ContentType.JSON); } @@ -82,106 +93,104 @@ public final class ProjectResourceTest { /** * Test that tries to obtain a project. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testGetExisting() { - when().get("/1").then().statusCode(200).contentType(ContentType.JSON).body("id", equalTo(1)); + // Try to get the project + given().get("/1").then().statusCode(200).contentType(ContentType.JSON).body("id", equalTo(1)); } /** * Test that tries to create a project. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreate() { - given().body(new org.opendc.web.proto.user.Project.Create("test")) + given().body(new org.opendc.web.proto.user.Project.Create("Test Project New")) .contentType(ContentType.JSON) .when() .post() .then() .statusCode(200) .contentType(ContentType.JSON) - .body("name", equalTo("test")); + .body("name", equalTo("Test Project New")); } /** * Test to create a project with an empty body. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreateEmpty() { - given().body("{}") - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(400) - .contentType(ContentType.JSON); + given().body("{}").contentType(ContentType.JSON).when().post().then().statusCode(400); } /** * Test to create a project with a blank name. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testCreateBlankName() { given().body(new org.opendc.web.proto.user.Project.Create("")) .contentType(ContentType.JSON) .when() .post() .then() - .statusCode(400) - .contentType(ContentType.JSON); + .statusCode(400); + } + + /** + * Test to delete a project that is owned by the user. + */ + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) + public void testDelete() { + given().delete("/6").then().statusCode(200); } /** * Test to delete a non-existent project. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_1", + roles = {"openid"}) public void testDeleteNonExistent() { - when().delete("/0").then().statusCode(404).contentType(ContentType.JSON); + when().delete("/0").then().statusCode(404); } /** - * Test to delete a project. + * Test to delete a project which is not connected to the user. + * test_user_3 is not connected to project 1. */ - // @Test - // @TestSecurity( - // user = "owner", - // roles = {"openid"}) - public void testDelete() { - int id = given().body(new org.opendc.web.proto.user.Project.Create("Delete Project")) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(200) - .contentType(ContentType.JSON) - .extract() - .path("id"); - - when().delete("/" + id).then().statusCode(200).contentType(ContentType.JSON); + @Test + @TestSecurity( + user = "test_user_3", + roles = {"openid"}) + public void testDeleteNotConnected() { + when().delete("/1").then().statusCode(403); } /** * Test to delete a project which the user does not own. + * project 1 is owned by test_user_1, test_user_2 is a viewer + * should not be able to delete it */ - // @Test - // @TestSecurity( - // user = "viewer", - // roles = {"openid"}) + @Test + @TestSecurity( + user = "test_user_2", + roles = {"openid"}) public void testDeleteNonOwner() { - when().delete("/1").then().statusCode(403).contentType(ContentType.JSON); + when().delete("/1").then().statusCode(403); } } diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ScenarioResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ScenarioResourceTest.java index a980e4e2..d81f9655 100644 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ScenarioResourceTest.java +++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ScenarioResourceTest.java @@ -28,13 +28,8 @@ import static org.hamcrest.Matchers.equalTo; import io.quarkus.test.common.http.TestHTTPEndpoint; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.security.TestSecurity; -import io.restassured.builder.RequestSpecBuilder; import io.restassured.http.ContentType; -import io.restassured.specification.RequestSpecification; import org.junit.jupiter.api.Test; -import org.opendc.web.proto.OperationalPhenomena; -import org.opendc.web.proto.Workload; -import org.opendc.web.proto.user.Scenario; /** * Test suite for {@link ScenarioResource}. @@ -47,10 +42,10 @@ public final class ScenarioResourceTest { */ @Test @TestSecurity( - user = "unknown", + user = "test_user_1", roles = {"openid"}) public void testGetAllUnauthorized() { - given().pathParam("project", "1").when().get().then().statusCode(404).contentType(ContentType.JSON); + given().pathParam("project", "2").when().get().then().statusCode(404); } /** @@ -58,10 +53,10 @@ public final class ScenarioResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testGetAll() { - given().pathParam("project", "1").when().get().then().statusCode(200).contentType(ContentType.JSON); + given().pathParam("project", "1").when().get().then().statusCode(200); } /** @@ -77,7 +72,7 @@ public final class ScenarioResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"runner"}) public void testGetInvalidToken() { given().pathParam("project", "1").when().get("/1").then().statusCode(403); @@ -88,7 +83,7 @@ public final class ScenarioResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testGetNonExisting() { given().pathParam("project", "1") @@ -100,14 +95,14 @@ public final class ScenarioResourceTest { } /** - * Test that tries to obtain a scenario. + * Test that tries to obtain a scenario when it does not have authority to get to the project. */ @Test @TestSecurity( - user = "unknown", + user = "test_user_1", roles = {"openid"}) public void testGetExistingUnauthorized() { - given().pathParam("project", "1") + given().pathParam("project", "2") .when() .get("/1") .then() @@ -120,7 +115,7 @@ public final class ScenarioResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testGetExisting() { given().pathParam("project", "1") @@ -137,7 +132,7 @@ public final class ScenarioResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testDeleteNonExistent() { given().pathParam("project", "1").when().delete("/0").then().statusCode(404); @@ -148,10 +143,10 @@ public final class ScenarioResourceTest { */ @Test @TestSecurity( - user = "unknown", + user = "test_user_1", roles = {"openid"}) public void testDeleteUnauthorized() { - given().pathParam("project", "1").when().delete("/1").then().statusCode(404); + given().pathParam("project", "2").when().delete("/1").then().statusCode(404); } /** @@ -159,7 +154,7 @@ public final class ScenarioResourceTest { */ @Test @TestSecurity( - user = "viewer", + user = "test_user_2", roles = {"openid"}) public void testDeleteAsViewer() { given().pathParam("project", "1").when().delete("/1").then().statusCode(403); @@ -170,32 +165,12 @@ public final class ScenarioResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testDelete() { - RequestSpecification spec = new RequestSpecBuilder() - .setBasePath("/projects/1/portfolios/1/scenarios") - .build(); - - int number = given(spec) - .body(new Scenario.Create( - "test", - new Workload.Spec("bitbrains-small", 1.0), - 1, - new OperationalPhenomena(false, false), - "test")) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(200) - .contentType(ContentType.JSON) - .extract() - .path("number"); - given().pathParam("project", "1") .when() - .delete("/" + number) + .delete("/1") .then() .statusCode(200) .contentType(ContentType.JSON); diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/TopologyResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/TopologyResourceTest.java index c0746e7a..277376e5 100644 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/TopologyResourceTest.java +++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/TopologyResourceTest.java @@ -44,7 +44,7 @@ public final class TopologyResourceTest { */ @Test @TestSecurity( - user = "unknown", + user = "test_user_4", roles = {"openid"}) public void testGetAllWithoutAuth() { given().pathParam("project", "1") @@ -58,21 +58,22 @@ public final class TopologyResourceTest { /** * Test that tries to obtain the list of topologies belonging to a project. + * TODO: check if any topology comes back */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testGetAll() { - given().pathParam("project", "1").when().get().then().statusCode(200).contentType(ContentType.JSON); + given().pathParam("project", "1").when().get().then().statusCode(200); } /** - * Test that tries to create a topology for a project. + * Test that tries to create a topology for a project that does not exist. */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testCreateNonExistent() { given().pathParam("project", "0") @@ -81,26 +82,25 @@ public final class TopologyResourceTest { .when() .post() .then() - .statusCode(404) - .contentType(ContentType.JSON); + .statusCode(404); } /** - * Test that tries to create a topology for a project as viewer. + * Test that tries to create a topology for a project while not authorized. + * TODO: should probably return 403, but this does not work in the current system */ @Test @TestSecurity( - user = "viewer", + user = "test_user_1", roles = {"openid"}) public void testCreateUnauthorized() { - given().pathParam("project", "1") + given().pathParam("project", "2") .body(new Topology.Create("test", List.of())) .contentType(ContentType.JSON) .when() .post() .then() - .statusCode(403) - .contentType(ContentType.JSON); + .statusCode(404); } /** @@ -108,18 +108,18 @@ public final class TopologyResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testCreate() { given().pathParam("project", "1") - .body(new Topology.Create("test", List.of())) + .body(new Topology.Create("Test Topology New", List.of())) .contentType(ContentType.JSON) .when() .post() .then() .statusCode(200) .contentType(ContentType.JSON) - .body("name", equalTo("test")); + .body("name", equalTo("Test Topology New")); } /** @@ -127,7 +127,7 @@ public final class TopologyResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testCreateEmpty() { given().pathParam("project", "1") @@ -136,8 +136,7 @@ public final class TopologyResourceTest { .when() .post() .then() - .statusCode(400) - .contentType(ContentType.JSON); + .statusCode(400); } /** @@ -145,7 +144,7 @@ public final class TopologyResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testCreateBlankName() { given().pathParam("project", "1") @@ -154,8 +153,7 @@ public final class TopologyResourceTest { .when() .post() .then() - .statusCode(400) - .contentType(ContentType.JSON); + .statusCode(400); } /** @@ -171,7 +169,7 @@ public final class TopologyResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"runner"}) public void testGetInvalidToken() { given().pathParam("project", "1").when().get("/1").then().statusCode(403); @@ -182,15 +180,10 @@ public final class TopologyResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testGetNonExisting() { - given().pathParam("project", "1") - .when() - .get("/0") - .then() - .statusCode(404) - .contentType(ContentType.JSON); + given().pathParam("project", "1").when().get("/0").then().statusCode(404); } /** @@ -198,15 +191,10 @@ public final class TopologyResourceTest { */ @Test @TestSecurity( - user = "unknown", + user = "test_user_1", roles = {"openid"}) public void testGetUnauthorized() { - given().pathParam("project", "1") - .when() - .get("/1") - .then() - .statusCode(404) - .contentType(ContentType.JSON); + given().pathParam("project", "2").when().get("/1").then().statusCode(404); } /** @@ -214,7 +202,7 @@ public final class TopologyResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testGetExisting() { given().pathParam("project", "1") @@ -231,7 +219,7 @@ public final class TopologyResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testUpdateNonExistent() { given().pathParam("project", "1") @@ -248,10 +236,10 @@ public final class TopologyResourceTest { */ @Test @TestSecurity( - user = "unknown", + user = "test_user_1", roles = {"openid"}) public void testUpdateUnauthorized() { - given().pathParam("project", "1") + given().pathParam("project", "2") .body(new Topology.Update(List.of())) .contentType(ContentType.JSON) .when() @@ -262,10 +250,11 @@ public final class TopologyResourceTest { /** * Test to update a topology as a viewer. + * TODO: should return 403, but currently returns 404 */ @Test @TestSecurity( - user = "viewer", + user = "test_user_2", roles = {"openid"}) public void testUpdateAsViewer() { given().pathParam("project", "1") @@ -283,7 +272,7 @@ public final class TopologyResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testUpdate() { given().pathParam("project", "1") @@ -292,8 +281,7 @@ public final class TopologyResourceTest { .when() .put("/1") .then() - .statusCode(200) - .contentType(ContentType.JSON); + .statusCode(200); } /** @@ -301,7 +289,7 @@ public final class TopologyResourceTest { */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testDeleteNonExistent() { given().pathParam("project", "1").when().delete("/0").then().statusCode(404); @@ -312,10 +300,10 @@ public final class TopologyResourceTest { */ @Test @TestSecurity( - user = "unknown", + user = "test_user_1", roles = {"openid"}) public void testDeleteUnauthorized() { - given().pathParam("project", "1").when().delete("/1").then().statusCode(404); + given().pathParam("project", "2").when().delete("/1").then().statusCode(404); } /** @@ -323,50 +311,46 @@ public final class TopologyResourceTest { */ @Test @TestSecurity( - user = "viewer", + user = "test_user_2", roles = {"openid"}) public void testDeleteAsViewer() { given().pathParam("project", "1").when().delete("/1").then().statusCode(403); } + /** + * Test to delete a topology as a viewer. + */ + @Test + @TestSecurity( + user = "test_user_3", + roles = {"openid"}) + public void testDeleteAsEditor() { + given().pathParam("project", "1").when().delete("/2").then().statusCode(200); + } + /** * Test to delete a topology. */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testDelete() { - int number = given().pathParam("project", "1") - .body(new Topology.Create("Delete Topology", List.of())) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(200) - .contentType(ContentType.JSON) - .extract() - .path("number"); - - given().pathParam("project", "1") - .when() - .delete("/" + number) - .then() - .statusCode(200) - .contentType(ContentType.JSON); + given().pathParam("project", "1").when().delete("/3").then().statusCode(200); } /** * Test to delete a topology that is still being used by a scenario. + * TODO: fix later */ @Test @TestSecurity( - user = "owner", + user = "test_user_1", roles = {"openid"}) public void testDeleteUsed() { given().pathParam("project", "1") .when() - .delete("/1") // Topology 1 is still used by scenario 1 and 2 + .delete("/4") // Topology 1 is still used by scenario 1 and 2 .then() .statusCode(403) .contentType(ContentType.JSON); diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/UserAccountingServiceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/UserAccountingServiceTest.java index d1d82097..91e3eb66 100644 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/UserAccountingServiceTest.java +++ b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/UserAccountingServiceTest.java @@ -33,9 +33,9 @@ import static org.mockito.ArgumentMatchers.anyString; import io.quarkus.panache.mock.PanacheMock; import io.quarkus.test.junit.QuarkusTest; +import jakarta.persistence.EntityExistsException; import java.time.Duration; import java.time.LocalDate; -import javax.persistence.EntityExistsException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; diff --git a/opendc-web/opendc-web-ui-quarkus-deployment/build.gradle.kts b/opendc-web/opendc-web-ui-quarkus-deployment/build.gradle.kts index 5a42aaea..6be49e22 100644 --- a/opendc-web/opendc-web-ui-quarkus-deployment/build.gradle.kts +++ b/opendc-web/opendc-web-ui-quarkus-deployment/build.gradle.kts @@ -22,7 +22,7 @@ description = "Quarkus extension for serving OpenDC web interface" -/* Build configuration */ +// Build configuration plugins { `java-library-conventions` } diff --git a/opendc-web/opendc-web-ui/build.gradle.kts b/opendc-web/opendc-web-ui/build.gradle.kts index 79160a2e..777098d4 100644 --- a/opendc-web/opendc-web-ui/build.gradle.kts +++ b/opendc-web/opendc-web-ui/build.gradle.kts @@ -43,27 +43,29 @@ node { version.set(libs.versions.node.get()) } -val formatTask = tasks.register("prettierFormat") { - group = "formatting" - description = "Use Prettier to format the JavaScript codebase" - - args.set(listOf("run", "format")) - dependsOn(tasks.npmInstall) - inputs.dir("src") - inputs.files("package.json", "next.config.js", ".prettierrc.yaml") - outputs.upToDateWhen { true } -} +val formatTask = + tasks.register("prettierFormat") { + group = "formatting" + description = "Use Prettier to format the JavaScript codebase" + + args.set(listOf("run", "format")) + dependsOn(tasks.npmInstall) + inputs.dir("src") + inputs.files("package.json", "next.config.js", ".prettierrc.yaml") + outputs.upToDateWhen { true } + } -val lintTask = tasks.register("nextLint") { - group = "verification" - description = "Use ESLint to check for problems" +val lintTask = + tasks.register("nextLint") { + group = "verification" + description = "Use ESLint to check for problems" - args.set(listOf("run", "lint")) - dependsOn(tasks.npmInstall) - inputs.dir("src") - inputs.files("package.json", "next.config.js", ".eslintrc") - outputs.upToDateWhen { true } -} + args.set(listOf("run", "lint")) + dependsOn(tasks.npmInstall) + inputs.dir("src") + inputs.files("package.json", "next.config.js", ".eslintrc") + outputs.upToDateWhen { true } + } tasks.register("nextDev") { group = "build" @@ -77,30 +79,32 @@ tasks.register("nextDev") { outputs.upToDateWhen { true } } -val buildTask = tasks.register("nextBuild") { - group = "build" - description = "Build the Next.js project" - - args.set(listOf("run", "build")) - - val env = listOf( - "NEXT_PUBLIC_API_BASE_URL", - "NEXT_PUBLIC_SENTRY_DSN", - "NEXT_PUBLIC_AUTH0_DOMAIN", - "NEXT_PUBLIC_AUTH0_CLIENT_ID", - "NEXT_PUBLIC_AUTH0_AUDIENCE" - ) - for (envvar in env) { - environment.put(envvar, "%%$envvar%%") +val buildTask = + tasks.register("nextBuild") { + group = "build" + description = "Build the Next.js project" + + args.set(listOf("run", "build")) + + val env = + listOf( + "NEXT_PUBLIC_API_BASE_URL", + "NEXT_PUBLIC_SENTRY_DSN", + "NEXT_PUBLIC_AUTH0_DOMAIN", + "NEXT_PUBLIC_AUTH0_CLIENT_ID", + "NEXT_PUBLIC_AUTH0_AUDIENCE", + ) + for (envvar in env) { + environment.put(envvar, "%%$envvar%%") + } + + dependsOn(tasks.npmInstall) + inputs.dir(project.fileTree("src")) + inputs.dir("node_modules") + inputs.files("package.json", "next.config.js") + outputs.dir(layout.buildDirectory.dir("next")) } - dependsOn(tasks.npmInstall) - inputs.dir(project.fileTree("src")) - inputs.dir("node_modules") - inputs.files("package.json", "next.config.js") - outputs.dir(layout.buildDirectory.dir("next")) -} - tasks.register("nextStart") { group = "build" description = "Build the Next.js project" -- cgit v1.2.3