diff --git a/README.md b/README.md index cbbe3e5f6..10d853924 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,9 @@ images. * Augment container images i.e. dynamically add one or more container layers to existing images; * Build container images on-demand for a given container file (aka Dockerfile); * Build container images on-demand based on one or more Conda packages; -* Build container images on-demand based on one or more Spack packages, Spack support will be removed in future releases; * Build container images for a specified target platform (currently linux/amd64 and linux/arm64); * Push and cache built containers to a user-provided container repository; -* Build Singularity native containers both using a Singularity spec file, Conda package(s) and Spack package(s); +* Build Singularity native containers both using a Singularity spec file, Conda package(s); * Push Singularity native container images to OCI-compliant registries; diff --git a/VERSION b/VERSION index 1cac385c6..6b89d58f8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.11.0 +1.12.2 diff --git a/changelog.txt b/changelog.txt index 2042d0315..5c865f7eb 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,53 @@ # Wave changelog +1.12.2 - 18 Sep 2024 +- Fix Remove entries permanently from stream once consumed [adfad9d6] +- Refactor container build service [1a858c12] +- Remove unused code [268c76ea] + +1.12.1 - 17 Sep 2024 +- Fix stream check for new messages [16a7e256] + +1.12.0 - 17 Sep 2024 +- Add Job manager (#605) [00daf919] + - Add support for build and scan operation via Job manager (#620) [0e5e5ca4] + - Do not retry on build failure (#632) [e6568d1e] + - Fix Blob cache failure duration (#643) [ebf65adc] + - Fix K8s job status detection (#630) [d5b45d8d] [7a9046ed] [e26811dd] + - Fix Retry policy delay multipler (#629) [80037565] + - Improve blob cache info (#644) [8b96173a] + - Improve blob cache logging [e4c75671] + - Improve blob cache reliability (#596) [dfb64bad] + - Improve build & scan logging [b086f3d8] + - Improve job dispatcher (#645) [fee3db9d] + - Remove unneeded timeout logic (#633) [5eabf285] + - Deferred resources cleanup (#636) [c6b3e9b8] + - Change k8s Job deletion to foreground pods propagation (#595) [b5baea03] + - Run Docker process in background (#647) [1fcb4c94] +- Add build in progress status in build page (#607) [3d940e88] +- Add container image name to container scan view (#635) [7858b95f] +- Add entropy to cron services (#640) [a6d1d884] +- Add link to build Id in container request view [57129960] +- Add rate limiter to container request [a05c1094] +- Add trusted builds timeout (#600) [63b58088] +- Add /v1alpha2/container/{containerId} endpoint (#609) [6c05498c] +- Add /v1alpha2/container/{token} in typespec (#618) [5cbd67a8] +- Fix failing type checks [bd704bea] +- Fix too many requests error code (#610) [ec43fa0d] +- Increase blob cache timeout to 10m and decrese status to 1h [cf4b7588] +- Improve container view page (#615) [d9b8cab8] +- Improve registry auth error handling (#628) [c9185730] +- Increase cache-tower-client to 1min (#641) [df32b305] +- Message queue name refactoring [861d0580] +- Simplify tests (#627) [cf53cba2] +- Update dev default logs [6b588f4c] +- Update nextflow.mdx (#612) [fe9b4273] +- Update scan model (#637) [94d37637] +- Use public repo for s5cmd (#639) [c16c0959] +- Bump Trivy 0.55 (#638) [b69d34c4] + +1.11.1 - 5 Sep 2024 +- Add rate limiter to container request [a3c63525] + 1.11.0 - 23 Aug 2024 - added /v1alpha2/container/{containerId} (#609) [5221b5a0] - Improve contaiener view page (#615) [9e15b455] diff --git a/configuration.md b/configuration.md index b7523b17c..d162a98b5 100644 --- a/configuration.md +++ b/configuration.md @@ -107,20 +107,6 @@ Below are the standard format for known registries, but you can change registry - **`wave.build.force-compression`**: determines whether to force the compression for each cache layers produced by the build process. The default is `false`, enabling compression for more efficient storage. *Optional*. -### Spack configuration for wave build process - -**Note**: Spack support will be removed in future releases. - -Spack configuration consists of the path of its secret file, the mount path for the secret file in the spack container, and the optional S3 bucket name for the spack binary cache. - -**Note**: these configuration are mandatory to support Spack in a wave installation. - -- **`wave.build.spack.secretKeyFile`**: the path to the file containing the PGP private key used to [sign Spack packages built by Wave](https://spack.readthedocs.io/en/latest/binary_caches.html#build-cache-signing). For example, `/efs/wave/spack/key`. *Mandatory*. - -- **`wave.build.spack.secretMountPath`**: sets the mount path inside the Spack Docker image for the PGP private key specified by `wave.build.spack.secretKeyFile`. For instance `/var/seqera/spack/key`. Indicating where the PGP private key should be mounted inside the Spack Docker image. *Mandatory*. - -- **`wave.build.spack.cacheBucket`**: specifies the S3 bucket for the Spack binary cache, for example, `s3://spack-binarycache`. *Optional*. - ### Build process logs configuration This configuration specifies attributes for the persistence of the logs fetched from containers or k8s pods used for building requested images, which can be accessed later and also attached to the build completion email. diff --git a/docs/api.mdx b/docs/api.mdx index 0d646c0b3..ca52843f3 100644 --- a/docs/api.mdx +++ b/docs/api.mdx @@ -52,7 +52,6 @@ This API endpoint is deprecated in current versions of Wave. ] }, condaFile: string, - spackFile: string, containerPlatform: string, buildRepository: string, cacheRepository: string, @@ -81,7 +80,6 @@ This API endpoint is deprecated in current versions of Wave. | `containerConfig.layers.gzipSize` | The size in bytes of the the provided layer tar gzip file. | | `containerFile` | Dockerfile used for building a new container encoded in base64 (optional). When provided, the attribute `containerImage` must be omitted. | | `condaFile` | Conda environment file encoded as base64 string. | -| `spackFile` | `Deprecated` Spack recipe file encoded as base64 string. Spack support will be removed in future releases. | | `containerPlatform` | Target container architecture of the built container, e.g., `linux/amd64` (optional). Currently only supporting amd64 and arm64. | | `buildRepository` | Container repository where container builds should be pushed, e.g., `docker.io/user/my-image` (optional). | | `cacheRepository` | Container repository used to cache build layers `docker.io/user/my-cache` (optional). | @@ -136,7 +134,6 @@ The endpoint returns the name of the container request made available by Wave. ] }, condaFile: string, - spackFile: string, containerPlatform: string, buildRepository: string, cacheRepository: string, @@ -157,10 +154,6 @@ The endpoint returns the name of the container request made available by Wave. commands: string[], basePackages: string } - spackOpts:{ - commands: string[], - basePackages: string - } }, nameStrategy: string @@ -182,7 +175,6 @@ The endpoint returns the name of the container request made available by Wave. | `containerConfig.layers.gzipSize` | The size in bytes of the the provided layer tar gzip file. | | `containerFile` | Dockerfile used for building a new container encoded in base64 (optional). When provided, the attribute `containerImage` must be omitted. | | `condaFile` | Conda environment file encoded as base64 string. | -| `spackFile` | `Deprecated` Spack recipe file encoded as base64 string. Spack support will be removed in future releases. | | `containerPlatform` | Target container architecture of the built container, e.g., `linux/amd64` (optional). Currently only supporting amd64 and arm64. | | `buildRepository` | Container repository where container builds should be pushed, e.g., `docker.io/user/my-image` (optional). | | `cacheRepository` | Container repository used to cache build layers `docker.io/user/my-cache` (optional). | diff --git a/src/main/groovy/io/seqera/wave/configuration/SpackConfig.groovy b/src/main/groovy/io/seqera/wave/configuration/SpackConfig.groovy deleted file mode 100644 index 0b81d6299..000000000 --- a/src/main/groovy/io/seqera/wave/configuration/SpackConfig.groovy +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Wave, containers provisioning service - * Copyright (c) 2023-2024, Seqera Labs - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package io.seqera.wave.configuration - -import java.nio.file.Path - -import groovy.transform.CompileStatic -import groovy.transform.EqualsAndHashCode -import groovy.transform.ToString -import io.micronaut.context.annotation.Value -import io.micronaut.core.annotation.Nullable -import jakarta.inject.Singleton -/** - * Model Spack configuration - * - * @author Paolo Di Tommaso - */ -@ToString -@EqualsAndHashCode -@Singleton -@CompileStatic -@Deprecated -class SpackConfig { - - /** - * The s3 bucket where Spack cached binaries are stored - */ - @Nullable - @Value('${wave.build.spack.cacheBucket}') - private String cacheBucket - - /** - * The host path where the GPG key required by the Spack "buildcache" is located - */ - @Nullable - @Value('${wave.build.spack.secretKeyFile}') - private String secretKeyFile - - /** - * The container path where the GPG key required by the Spack "buildcache" is located - */ - @Nullable - @Value('${wave.build.spack.secretMountPath}') - private String secretMountPath - - /** - * The container image used for Spack builds - */ - @Value('${wave.build.spack.builderImage:`spack/ubuntu-jammy:v0.20.0`}') - private String builderImage - - /** - * The container image used for Spack container - */ - @Value('${wave.build.spack.runnerImage:`ubuntu:22.04`}') - private String runnerImage - - String getCacheBucket() { - if( !cacheBucket ) - throw new IllegalStateException("Missing Spack 'cacheBucket' configuration setting") - return cacheBucket - } - - Path getSecretKeyFile() { - if( !secretKeyFile ) - throw new IllegalStateException("Missing Spack 'secretKeyFile' configuration setting") - return Path.of(secretKeyFile).toAbsolutePath().normalize() - } - - String getSecretMountPath() { - if( !secretMountPath ) - throw new IllegalStateException("Missing Spack 'secretMountPath' configuration setting") - return secretMountPath - } - - String getBuilderImage() { - return builderImage - } - - String getRunnerImage() { - return runnerImage - } -} diff --git a/src/main/groovy/io/seqera/wave/controller/ContainerController.groovy b/src/main/groovy/io/seqera/wave/controller/ContainerController.groovy index 2499bf925..6fcd84dd8 100644 --- a/src/main/groovy/io/seqera/wave/controller/ContainerController.groovy +++ b/src/main/groovy/io/seqera/wave/controller/ContainerController.groovy @@ -86,8 +86,6 @@ import static io.seqera.wave.util.ContainerHelper.makeResponseV1 import static io.seqera.wave.util.ContainerHelper.makeResponseV2 import static io.seqera.wave.util.ContainerHelper.makeTargetImage import static io.seqera.wave.util.ContainerHelper.patchPlatformEndpoint -import static io.seqera.wave.util.ContainerHelper.spackFileFromRequest -import static io.seqera.wave.util.SpackHelper.prependBuilderTemplate import static java.util.concurrent.CompletableFuture.completedFuture /** * Implement a controller to receive container token requests @@ -237,6 +235,10 @@ class ContainerController { req = req.copyWith(containerFile: generated.bytes.encodeBase64().toString()) } + if( req.spackFile ) { + throw new BadRequestException("Spack packages are not supported any more") + } + final ip = addressResolver.resolve(httpRequest) // check the rate limit before continuing if( rateLimiterService ) @@ -317,7 +319,6 @@ class ContainerController { final containerSpec = decodeBase64OrFail(req.containerFile, 'containerFile') final condaContent = condaFileFromRequest(req) - final spackContent = spackFileFromRequest(req) final format = req.formatSingularity() ? SINGULARITY : DOCKER final platform = ContainerPlatform.of(req.containerPlatform) final buildRepository = targetRepo( req.buildRepository ?: (req.freeze && buildConfig.defaultPublicRepository @@ -328,7 +329,6 @@ class ContainerController { final containerConfig = req.freeze ? req.containerConfig : null final offset = DataTimeUtils.offsetId(req.timestamp) final scanId = scanEnabled && format==DOCKER ? LongRndKey.rndHex() : null - final containerFile = spackContent ? prependBuilderTemplate(containerSpec,format) : containerSpec // use 'imageSuffix' strategy by default for public repo images final nameStrategy = req.nameStrategy==null && buildRepository @@ -338,14 +338,13 @@ class ContainerController { checkContainerSpec(containerSpec) // create a unique digest to identify the build request - final containerId = makeContainerId(containerFile, condaContent, spackContent, platform, buildRepository, req.buildContext) - final targetImage = makeTargetImage(format, buildRepository, containerId, condaContent, spackContent, nameStrategy) + final containerId = makeContainerId(containerSpec, condaContent, platform, buildRepository, req.buildContext) + final targetImage = makeTargetImage(format, buildRepository, containerId, condaContent, nameStrategy) final maxDuration = buildConfig.buildMaxDuration(req) return new BuildRequest( containerId, - containerFile, + containerSpec, condaContent, - spackContent, Path.of(buildConfig.buildWorkspace), targetImage, identity, diff --git a/src/main/groovy/io/seqera/wave/controller/ViewController.groovy b/src/main/groovy/io/seqera/wave/controller/ViewController.groovy index 6edad5dd6..0e5163566 100644 --- a/src/main/groovy/io/seqera/wave/controller/ViewController.groovy +++ b/src/main/groovy/io/seqera/wave/controller/ViewController.groovy @@ -18,6 +18,7 @@ package io.seqera.wave.controller +import groovy.json.JsonOutput import io.micronaut.core.annotation.Nullable import groovy.transform.CompileStatic @@ -25,15 +26,18 @@ import io.micronaut.context.annotation.Value import io.micronaut.http.HttpResponse import io.micronaut.http.annotation.Controller import io.micronaut.http.annotation.Get +import io.micronaut.http.annotation.QueryValue import io.micronaut.scheduling.TaskExecutors import io.micronaut.scheduling.annotation.ExecuteOn import io.micronaut.views.View import io.seqera.wave.exception.NotFoundException import io.seqera.wave.service.builder.ContainerBuildService +import io.seqera.wave.service.inspect.ContainerInspectService import io.seqera.wave.service.logs.BuildLogService import io.seqera.wave.service.persistence.PersistenceService import io.seqera.wave.service.persistence.WaveBuildRecord import io.seqera.wave.service.scan.ScanResult +import io.seqera.wave.util.JacksonHelper import jakarta.inject.Inject import static io.seqera.wave.util.DataTimeUtils.formatDuration import static io.seqera.wave.util.DataTimeUtils.formatTimestamp @@ -61,6 +65,9 @@ class ViewController { @Nullable private BuildLogService buildLogService + @Inject + private ContainerInspectService inspectService + @View("build-view") @Get('/builds/{buildId}') HttpResponse> viewBuild(String buildId) { @@ -86,7 +93,6 @@ class ViewController { binding.build_platform = result.platform binding.build_containerfile = result.dockerFile ?: '-' binding.build_condafile = result.condaFile - binding.build_spackfile = result.spackFile binding.build_digest = result.digest ?: '-' binding.put('server_url', serverUrl) binding.scan_url = result.scanId && result.succeeded() ? "$serverUrl/view/scans/${result.scanId}" : null @@ -164,6 +170,28 @@ class ViewController { return HttpResponse.>ok(binding) } + @View("inspect-view") + @Get('/inspect') + HttpResponse> viewInspect(@QueryValue String image) { + final binding = new HashMap(10) + try { + final spec = inspectService.containerSpec(image, null) + binding.imageName = spec.imageName + binding.reference = spec.reference + binding.digest = spec.digest + binding.registry = spec.registry + binding.hostName = spec.hostName + binding.config = JacksonHelper.toJson(spec.config) + binding.manifest = JacksonHelper.toJson(spec.manifest) + }catch (Exception e){ + binding.error_message = e.getMessage() + } + + // return the response + binding.put('server_url', serverUrl) + return HttpResponse.>ok(binding) + } + Map makeScanViewBinding(ScanResult result, Map binding=new HashMap(10)) { binding.should_refresh = !result.isCompleted() binding.scan_id = result.id @@ -182,4 +210,5 @@ class ViewController { return binding } + } diff --git a/src/main/groovy/io/seqera/wave/service/builder/BuildCacheStore.groovy b/src/main/groovy/io/seqera/wave/service/builder/BuildCacheStore.groovy index cdd914598..dbc4085c7 100644 --- a/src/main/groovy/io/seqera/wave/service/builder/BuildCacheStore.groovy +++ b/src/main/groovy/io/seqera/wave/service/builder/BuildCacheStore.groovy @@ -60,7 +60,7 @@ class BuildCacheStore extends AbstractCacheStore implements Bui protected Duration getDuration() { return buildConfig.statusDuration } - + @Override BuildStoreEntry getBuild(String imageName) { return get(imageName) diff --git a/src/main/groovy/io/seqera/wave/service/builder/BuildRequest.groovy b/src/main/groovy/io/seqera/wave/service/builder/BuildRequest.groovy index 545a52ca7..2f77c69de 100644 --- a/src/main/groovy/io/seqera/wave/service/builder/BuildRequest.groovy +++ b/src/main/groovy/io/seqera/wave/service/builder/BuildRequest.groovy @@ -60,11 +60,6 @@ class BuildRequest { */ final String condaFile - /** - * The spock file recipe associated with this request - */ - final String spackFile - /** * The build context work directory */ @@ -116,11 +111,6 @@ class BuildRequest { final ContainerConfig containerConfig /** - * Whenever is a spack build - */ - final boolean isSpackBuild - - /** * The ID of the security scan triggered by this build */ final String scanId @@ -147,7 +137,6 @@ class BuildRequest { BuildRequest(String containerId, String containerFile, String condaFile, - String spackFile, Path workspace, String targetImage, PlatformId identity, @@ -166,7 +155,6 @@ class BuildRequest { this.containerId = containerId this.containerFile = containerFile this.condaFile = condaFile - this.spackFile = spackFile this.workspace = workspace this.targetImage = targetImage this.identity = identity @@ -177,7 +165,6 @@ class BuildRequest { this.configJson = configJson this.offsetId = offsetId ?: OffsetDateTime.now().offset.id this.containerConfig = containerConfig - this.isSpackBuild = spackFile this.scanId = scanId this.buildContext = buildContext this.format = format @@ -188,7 +175,6 @@ class BuildRequest { this.containerId = opts.containerId this.containerFile = opts.containerFile this.condaFile = opts.condaFile - this.spackFile = opts.spackFile this.workspace = opts.workspace as Path this.targetImage = opts.targetImage this.identity = opts.identity as PlatformId @@ -199,7 +185,6 @@ class BuildRequest { this.configJson = opts.configJson this.offsetId = opts.offesetId this.containerConfig = opts.containerConfig as ContainerConfig - this.isSpackBuild = opts.isSpackBuild this.scanId = opts.scanId this.buildContext = opts.buildContext as BuildContext this.format = opts.format as BuildFormat @@ -210,7 +195,7 @@ class BuildRequest { @Override String toString() { - return "BuildRequest[containerId=$containerId; targetImage=$targetImage; identity=$identity; dockerFile=${trunc(containerFile)}; condaFile=${trunc(condaFile)}; spackFile=${trunc(spackFile)}; buildId=$buildId, maxDuration=$maxDuration]" + return "BuildRequest[containerId=$containerId; targetImage=$targetImage; identity=$identity; dockerFile=${trunc(containerFile)}; condaFile=${trunc(condaFile)}; buildId=$buildId, maxDuration=$maxDuration]" } String getContainerId() { @@ -230,10 +215,6 @@ class BuildRequest { return condaFile } - String getSpackFile() { - return spackFile - } - Path getWorkDir() { return workDir } diff --git a/src/main/groovy/io/seqera/wave/service/builder/BuildStrategy.groovy b/src/main/groovy/io/seqera/wave/service/builder/BuildStrategy.groovy index fb5a25cb4..c061c30e5 100644 --- a/src/main/groovy/io/seqera/wave/service/builder/BuildStrategy.groovy +++ b/src/main/groovy/io/seqera/wave/service/builder/BuildStrategy.groovy @@ -84,19 +84,6 @@ abstract class BuildStrategy { result << "type=registry,ref=$req.cacheRepository:$req.containerId".toString() } - if(req.spackFile){ - result << '--opt' - result << 'build-arg:AWS_STS_REGIONAL_ENDPOINTS=$(AWS_STS_REGIONAL_ENDPOINTS)' - result << '--opt' - result << 'build-arg:AWS_REGION=$(AWS_REGION)' - result << '--opt' - result << 'build-arg:AWS_DEFAULT_REGION=$(AWS_DEFAULT_REGION)' - result << '--opt' - result << 'build-arg:AWS_ROLE_ARN=$(AWS_ROLE_ARN)' - result << '--opt' - result << 'build-arg:AWS_WEB_IDENTITY_TOKEN_FILE=$(AWS_WEB_IDENTITY_TOKEN_FILE)' - } - return result } diff --git a/src/main/groovy/io/seqera/wave/service/builder/ContainerBuildService.groovy b/src/main/groovy/io/seqera/wave/service/builder/ContainerBuildService.groovy index f63d3ec1d..8d90dd7ee 100644 --- a/src/main/groovy/io/seqera/wave/service/builder/ContainerBuildService.groovy +++ b/src/main/groovy/io/seqera/wave/service/builder/ContainerBuildService.groovy @@ -21,10 +21,8 @@ package io.seqera.wave.service.builder import java.util.concurrent.CompletableFuture import groovy.transform.CompileStatic -import io.micronaut.runtime.event.annotation.EventListener import io.seqera.wave.core.RoutePath import io.seqera.wave.service.persistence.WaveBuildRecord - /** * Declare container build service interface * @@ -79,56 +77,6 @@ interface ContainerBuildService { // ** build record operations // ************************************************************** - @EventListener - default void onBuildEvent(BuildEvent event) { - saveBuildRecord(event) - } - - /** - * Store a build record for the given {@link BuildRequest} object. - * - * This method is expected to store the build record associated with the request - * *only* in the short term store caching system, ie. without hitting the - * long-term SurrealDB storage - * - * @param request The build request that needs to be storage - */ - default void createBuildRecord(BuildRequest request) { - final record0 = WaveBuildRecord.fromEvent(new BuildEvent(request)) - createBuildRecord(record0.buildId, record0) - } - - /** - * Store the build record associated with the specified event both in the - * short-term cache (redis) and long-term persistence layer (surrealdb) - * - * @param event The {@link BuildEvent} object for which the build record needs to be stored - */ - default void saveBuildRecord(BuildEvent event) { - final record0 = WaveBuildRecord.fromEvent(event) - saveBuildRecord(record0.buildId, record0) - } - - /** - * Store a build record object. - * - * This method is expected to store the build record *only* in the short term store cache (redis), - * ie. without hitting the long-term storage (surrealdb) - * - * @param buildId The Id of the build record - * @param value The {@link WaveBuildRecord} to be stored - */ - void createBuildRecord(String buildId, WaveBuildRecord value) - - /** - * Store the specified build record both in the short-term cache (redis) - * and long-term persistence layer (surrealdb) - * - * @param buildId The Id of the build record - * @param value The {@link WaveBuildRecord} to be stored - */ - void saveBuildRecord(String buildId, WaveBuildRecord value) - /** * Retrieve the build record for the specified id. * diff --git a/src/main/groovy/io/seqera/wave/service/builder/ContainerBuildServiceImpl.groovy b/src/main/groovy/io/seqera/wave/service/builder/ContainerBuildServiceImpl.groovy index cc57d4e57..31dfb6eff 100644 --- a/src/main/groovy/io/seqera/wave/service/builder/ContainerBuildServiceImpl.groovy +++ b/src/main/groovy/io/seqera/wave/service/builder/ContainerBuildServiceImpl.groovy @@ -29,13 +29,13 @@ import groovy.transform.CompileStatic import groovy.util.logging.Slf4j import io.micronaut.context.event.ApplicationEventPublisher import io.micronaut.core.annotation.Nullable +import io.micronaut.runtime.event.annotation.EventListener import io.micronaut.scheduling.TaskExecutors import io.seqera.wave.api.BuildContext import io.seqera.wave.auth.RegistryCredentialsProvider import io.seqera.wave.auth.RegistryLookupService import io.seqera.wave.configuration.BuildConfig import io.seqera.wave.configuration.HttpClientConfig -import io.seqera.wave.configuration.SpackConfig import io.seqera.wave.core.RegistryProxyService import io.seqera.wave.exception.HttpServerRetryableErrorException import io.seqera.wave.ratelimit.AcquireRequest @@ -51,9 +51,7 @@ import io.seqera.wave.service.persistence.WaveBuildRecord import io.seqera.wave.service.stream.StreamService import io.seqera.wave.tower.PlatformId import io.seqera.wave.util.Retryable -import io.seqera.wave.util.SpackHelper import io.seqera.wave.util.TarUtils -import io.seqera.wave.util.TemplateRenderer import jakarta.inject.Inject import jakarta.inject.Named import jakarta.inject.Singleton @@ -99,9 +97,6 @@ class ContainerBuildServiceImpl implements ContainerBuildService, JobHandler buildCmd(String jobName, BuildRequest req, Path credsFile) { - final spack = req.isSpackBuild ? spackConfig : null final dockerCmd = req.formatDocker() - ? cmdForBuildkit(jobName, req.workDir, credsFile, spack, req.platform) - : cmdForSingularity(jobName, req.workDir, credsFile, spack, req.platform) + ? cmdForBuildkit(jobName, req.workDir, credsFile, req.platform) + : cmdForSingularity(jobName, req.workDir, credsFile, req.platform) return dockerCmd + launchCmd(req) } - protected List cmdForBuildkit(String name, Path workDir, Path credsFile, SpackConfig spackConfig, ContainerPlatform platform ) { + protected List cmdForBuildkit(String name, Path workDir, Path credsFile, ContainerPlatform platform ) { //checkout the documentation here to know more about these options https://github.com/moby/buildkit/blob/master/docs/rootless.md#docker final wrapper = ['docker', 'run', @@ -125,12 +120,6 @@ class DockerBuildStrategy extends BuildStrategy { wrapper.add("$credsFile:/home/user/.docker/config.json:ro".toString()) } - if( spackConfig ) { - // secret file - wrapper.add('-v') - wrapper.add("${spackConfig.secretKeyFile}:${spackConfig.secretMountPath}:ro".toString()) - } - if( platform ) { wrapper.add('--platform') wrapper.add(platform.toString()) @@ -142,7 +131,7 @@ class DockerBuildStrategy extends BuildStrategy { return wrapper } - protected List cmdForSingularity(String name, Path workDir, Path credsFile, SpackConfig spackConfig, ContainerPlatform platform) { + protected List cmdForSingularity(String name, Path workDir, Path credsFile, ContainerPlatform platform) { final wrapper = ['docker', 'run', '--detach', @@ -159,12 +148,6 @@ class DockerBuildStrategy extends BuildStrategy { wrapper.add("${credsFile.resolveSibling('singularity-remote.yaml')}:/root/.singularity/remote.yaml:ro".toString()) } - if( spackConfig ) { - // secret file - wrapper.add('-v') - wrapper.add("${spackConfig.secretKeyFile}:${spackConfig.secretMountPath}:ro".toString()) - } - if( platform ) { wrapper.add('--platform') wrapper.add(platform.toString()) diff --git a/src/main/groovy/io/seqera/wave/service/builder/KubeBuildStrategy.groovy b/src/main/groovy/io/seqera/wave/service/builder/KubeBuildStrategy.groovy index 4d4e9fa06..f6ba6e041 100644 --- a/src/main/groovy/io/seqera/wave/service/builder/KubeBuildStrategy.groovy +++ b/src/main/groovy/io/seqera/wave/service/builder/KubeBuildStrategy.groovy @@ -30,7 +30,6 @@ import io.micronaut.context.annotation.Property import io.micronaut.context.annotation.Requires import io.micronaut.core.annotation.Nullable import io.seqera.wave.configuration.BuildConfig -import io.seqera.wave.configuration.SpackConfig import io.seqera.wave.core.RegistryProxyService import io.seqera.wave.exception.BadRequestException import io.seqera.wave.service.k8s.K8sService @@ -65,9 +64,6 @@ class KubeBuildStrategy extends BuildStrategy { @Inject private BuildConfig buildConfig - @Inject - private SpackConfig spackConfig - @Inject private RegistryProxyService proxyService @@ -95,8 +91,7 @@ class KubeBuildStrategy extends BuildStrategy { final buildCmd = launchCmd(req) final timeout = req.maxDuration ?: buildConfig.defaultTimeout final selector= getSelectorLabel(req.platform, nodeSelectorMap) - final spackCfg0 = req.isSpackBuild ? spackConfig : null - k8sService.launchBuildJob(jobName, buildImage, buildCmd, req.workDir, configFile, timeout, spackCfg0, selector) + k8sService.launchBuildJob(jobName, buildImage, buildCmd, req.workDir, configFile, timeout, selector) } catch (ApiException e) { throw new BadRequestException("Unexpected build failure - ${e.responseBody}", e) diff --git a/src/main/groovy/io/seqera/wave/service/builder/store/BuildRecordCacheStore.groovy b/src/main/groovy/io/seqera/wave/service/builder/store/BuildRecordCacheStore.groovy index 0f9884cfd..f820f080e 100644 --- a/src/main/groovy/io/seqera/wave/service/builder/store/BuildRecordCacheStore.groovy +++ b/src/main/groovy/io/seqera/wave/service/builder/store/BuildRecordCacheStore.groovy @@ -23,6 +23,7 @@ import java.time.Duration import groovy.transform.CompileStatic import io.seqera.wave.configuration.BuildConfig import io.seqera.wave.encoder.MoshiEncodeStrategy +import io.seqera.wave.service.builder.BuildStoreEntry import io.seqera.wave.service.cache.AbstractCacheStore import io.seqera.wave.service.cache.impl.CacheProvider import io.seqera.wave.service.persistence.WaveBuildRecord diff --git a/src/main/groovy/io/seqera/wave/service/cache/AbstractCacheStore.groovy b/src/main/groovy/io/seqera/wave/service/cache/AbstractCacheStore.groovy index b78e4f535..e1b142766 100644 --- a/src/main/groovy/io/seqera/wave/service/cache/AbstractCacheStore.groovy +++ b/src/main/groovy/io/seqera/wave/service/cache/AbstractCacheStore.groovy @@ -28,7 +28,7 @@ import io.seqera.wave.service.cache.impl.CacheProvider * * @author Paolo Di Tommaso */ -abstract class AbstractCacheStore implements CacheStore, BiCacheStore { +abstract class AbstractCacheStore implements CacheStore { private EncodingStrategy encodingStrategy @@ -101,25 +101,4 @@ abstract class AbstractCacheStore implements CacheStore, BiCacheSto delegate.clear() } - @Override - void biPut(String key, V value, Duration ttl) { - delegate.biPut(key0(key), serialize(value), ttl) - } - - @Override - void biRemove(String key) { - delegate.biRemove(key0(key)) - } - - @Override - Set biKeysFor(V value) { - final keys = delegate.biKeysFor(serialize(value)) - return keys.collect( (it) -> it.replace(getPrefix(),'') ) - } - - @Override - String biKeyFind(V value, boolean sorted) { - final result = delegate.biKeyFind(serialize(value), sorted) - result ? result.replace(getPrefix(),'') : null - } } diff --git a/src/main/groovy/io/seqera/wave/service/cache/BiCacheStore.groovy b/src/main/groovy/io/seqera/wave/service/cache/BiCacheStore.groovy deleted file mode 100644 index 546c2166d..000000000 --- a/src/main/groovy/io/seqera/wave/service/cache/BiCacheStore.groovy +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Wave, containers provisioning service - * Copyright (c) 2023-2024, Seqera Labs - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package io.seqera.wave.service.cache - -import java.time.Duration - -/** - * A cache store implementing a bi-direction key-value access, - * it allows retrieving all keys for a given value in time O(2) - * - * @author Paolo Di Tommaso - */ -interface BiCacheStore { - - /** - * Add a bi-directional key-value - * - * @param key The entry key - * @param value The entry value - * @param ttl The entry time-to-live. - */ - void biPut(K key, V value, Duration ttl) - - /** - * Remove an entry for the given value. - * - * @param key The key of entry to be removed - */ - void biRemove(K key) - - /** - * Get all key for the given value. - * - * @param value The value for which find corresponding keys - * @return A set of keys associated with the specified value or an empty set otherwise. - */ - Set biKeysFor(V value) - - /** - * Find a key in the cache for the given value. - * - * @param value The value for which find corresponding key - * @param sorted When true, the list of keys is sorted before getting the first value - * @return A key associated with the specified value or null if not key is found - */ - K biKeyFind(V value, boolean sorted) - -} diff --git a/src/main/groovy/io/seqera/wave/service/cache/impl/CacheProvider.groovy b/src/main/groovy/io/seqera/wave/service/cache/impl/CacheProvider.groovy index 3b8dcb80a..0e458c75b 100644 --- a/src/main/groovy/io/seqera/wave/service/cache/impl/CacheProvider.groovy +++ b/src/main/groovy/io/seqera/wave/service/cache/impl/CacheProvider.groovy @@ -18,7 +18,6 @@ package io.seqera.wave.service.cache.impl -import io.seqera.wave.service.cache.BiCacheStore import io.seqera.wave.service.cache.CacheStore /** @@ -26,5 +25,5 @@ import io.seqera.wave.service.cache.CacheStore * * @author Paolo Di Tommaso */ -interface CacheProvider extends CacheStore, BiCacheStore { +interface CacheProvider extends CacheStore { } diff --git a/src/main/groovy/io/seqera/wave/service/cache/impl/LocalCacheProvider.groovy b/src/main/groovy/io/seqera/wave/service/cache/impl/LocalCacheProvider.groovy index 09f0c0cfa..0659f7335 100644 --- a/src/main/groovy/io/seqera/wave/service/cache/impl/LocalCacheProvider.groovy +++ b/src/main/groovy/io/seqera/wave/service/cache/impl/LocalCacheProvider.groovy @@ -99,57 +99,4 @@ class LocalCacheProvider implements CacheProvider { store.clear() } - // =============== bi-cache store implementation =============== - - private Map> index = new HashMap<>() - - @Override - void biPut(String key, String value, Duration ttl) { - synchronized (this) { - this.put(key, value, ttl) - final id = value.hashCode() - def set = index.get(id) - if( set==null ) { - set=new HashSet() - index.put(id, set) - } - set.add(key) - } - } - - @Override - void biRemove(String key) { - synchronized (this) { - final entry = store.remove(key) - if( !entry ) - return - final id = entry.value.hashCode() - final set = index.get(id) - if( set ) { - set.remove(key) - } - } - } - - @Override - Set biKeysFor(String value) { - final id = value.hashCode() - return index.get(id) ?: Set.of() - } - - String biKeyFind(String value, boolean sorted) { - final id = value.hashCode() - final list = biKeysFor(value).toList() - final keys = sorted ? list.toSorted() : list.shuffled() - final itr = keys.iterator() - while( itr.hasNext() ) { - final result = itr.next() - // verify the key still exists - if( get(result)!=null ) - return result - // if not exist, remove it from the set - index.get(id)?.remove(result) - } - return null - } } diff --git a/src/main/groovy/io/seqera/wave/service/cache/impl/RedisCacheProvider.groovy b/src/main/groovy/io/seqera/wave/service/cache/impl/RedisCacheProvider.groovy index e597a96f8..a551f5083 100644 --- a/src/main/groovy/io/seqera/wave/service/cache/impl/RedisCacheProvider.groovy +++ b/src/main/groovy/io/seqera/wave/service/cache/impl/RedisCacheProvider.groovy @@ -24,7 +24,6 @@ import groovy.transform.CompileStatic import io.micronaut.context.annotation.Requires import jakarta.inject.Inject import jakarta.inject.Singleton -import org.apache.commons.codec.digest.DigestUtils import redis.clients.jedis.Jedis import redis.clients.jedis.JedisPool import redis.clients.jedis.params.SetParams @@ -90,58 +89,4 @@ class RedisCacheProvider implements CacheProvider { } } - // =============== bi-cache store implementation =============== - - @Override - void biPut(String key, String value, Duration ttl) { - final id = DigestUtils.sha256Hex(value) - try( Jedis conn=pool.getResource() ) { - final params = new SetParams().nx().ex(ttl.toSeconds()) - final tx = conn.multi() - tx.set(key, value, params) - tx.sadd(id, key) - tx.exec() - } - } - - @Override - void biRemove(String key) { - try( Jedis conn=pool.getResource() ) { - final value = conn.get(key) - final tx = conn.multi() - tx.del(key) - if( value ) { - final id = DigestUtils.sha256Hex(value) - tx.srem(id, key) - } - tx.exec() - } - } - - @Override - Set biKeysFor(String value) { - final id = DigestUtils.sha256Hex(value) - try( Jedis conn=pool.getResource() ) { - return conn.smembers(id) - } - } - - @Override - String biKeyFind(String value, boolean sorted) { - final id = DigestUtils.sha256Hex(value) - final list = biKeysFor(value).toList() - final keys = sorted ? list.toSorted() : list.shuffled() - final itr = keys.iterator() - while( itr.hasNext() ) { - final key = itr.next() - // verify the key still exists - if( get(key)!=null ) - return key - // if the key is not found, remove it from the set - try( Jedis conn=pool.getResource() ) { - conn.srem(id, key) - } - } - return null - } } diff --git a/src/main/groovy/io/seqera/wave/service/data/stream/AbstractMessageStream.groovy b/src/main/groovy/io/seqera/wave/service/data/stream/AbstractMessageStream.groovy index 262ce3151..560a81306 100644 --- a/src/main/groovy/io/seqera/wave/service/data/stream/AbstractMessageStream.groovy +++ b/src/main/groovy/io/seqera/wave/service/data/stream/AbstractMessageStream.groovy @@ -104,7 +104,7 @@ abstract class AbstractMessageStream implements Closeable { // initialize the stream stream.init(streamId) // then add the consumer to the listeners - final value = listeners.put(streamId, consumer) + listeners.put(streamId, consumer) } } @@ -125,7 +125,7 @@ abstract class AbstractMessageStream implements Closeable { protected boolean processMessage(String msg, MessageConsumer consumer, AtomicInteger count) { count.incrementAndGet() final decoded = encoder.decode(msg) - log.trace "Message streaming - receiving message=$msg; decoded=$decoded" + log.trace "Message stream - receiving message=$msg; decoded=$decoded" return consumer.accept(decoded) } @@ -145,7 +145,8 @@ abstract class AbstractMessageStream implements Closeable { // reset the attempt count because no error has been thrown attempt.reset() // if no message was sent, sleep for a while before retrying - if( count==0 ) { + if( count.get()==0 ) { + log.trace "Message stream - await before checking for new messages" sleep(pollInterval().toMillis()) } } diff --git a/src/main/groovy/io/seqera/wave/service/data/stream/impl/RedisMessageStream.groovy b/src/main/groovy/io/seqera/wave/service/data/stream/impl/RedisMessageStream.groovy index a8150962c..609b71061 100644 --- a/src/main/groovy/io/seqera/wave/service/data/stream/impl/RedisMessageStream.groovy +++ b/src/main/groovy/io/seqera/wave/service/data/stream/impl/RedisMessageStream.groovy @@ -104,9 +104,12 @@ class RedisMessageStream implements MessageStream { try (Jedis jedis = pool.getResource()) { final entry = claimMessage(jedis,streamId) ?: readMessage(jedis, streamId) if( entry && consumer.accept(entry.getFields().get(DATA_FIELD)) ) { - // acknowledge the job after processing + final tx = jedis.multi() + // acknowledge the entry has been processed so that it cannot be claimed anymore + tx.xack(streamId, CONSUMER_GROUP_NAME, entry.getID()) // this remove permanently the entry from the stream - jedis.xack(streamId, CONSUMER_GROUP_NAME, entry.getID()) + tx.xdel(streamId, entry.getID()) + tx.exec() return true } else diff --git a/src/main/groovy/io/seqera/wave/service/job/JobConfig.groovy b/src/main/groovy/io/seqera/wave/service/job/JobConfig.groovy index a2c5d3db9..2676724c9 100644 --- a/src/main/groovy/io/seqera/wave/service/job/JobConfig.groovy +++ b/src/main/groovy/io/seqera/wave/service/job/JobConfig.groovy @@ -36,7 +36,7 @@ class JobConfig { @Value('${wave.job-manager.grace-interval:30s}') Duration graceInterval - @Value('${wave.job-manager.poll-interval:200ms}') + @Value('${wave.job-manager.poll-interval:400ms}') Duration pollInterval } diff --git a/src/main/groovy/io/seqera/wave/service/k8s/K8sService.groovy b/src/main/groovy/io/seqera/wave/service/k8s/K8sService.groovy index 6817caffd..bb042d86a 100644 --- a/src/main/groovy/io/seqera/wave/service/k8s/K8sService.groovy +++ b/src/main/groovy/io/seqera/wave/service/k8s/K8sService.groovy @@ -26,7 +26,6 @@ import io.kubernetes.client.openapi.models.V1Pod import io.kubernetes.client.openapi.models.V1PodList import io.seqera.wave.configuration.BlobCacheConfig import io.seqera.wave.configuration.ScanConfig -import io.seqera.wave.configuration.SpackConfig /** * Defines Kubernetes operations * @@ -43,7 +42,7 @@ interface K8sService { void deletePod(String name) @Deprecated - V1Pod buildContainer(String name, String containerImage, List args, Path workDir, Path creds, Duration timeout, SpackConfig spackConfig, Map nodeSelector) + V1Pod buildContainer(String name, String containerImage, List args, Path workDir, Path creds, Duration timeout, Map nodeSelector) @Deprecated V1Pod scanContainer(String name, String containerImage, List args, Path workDir, Path creds, ScanConfig scanConfig, Map nodeSelector) @@ -65,7 +64,7 @@ interface K8sService { V1Job launchTransferJob(String name, String containerImage, List args, BlobCacheConfig blobConfig) - V1Job launchBuildJob(String name, String containerImage, List args, Path workDir, Path creds, Duration timeout, SpackConfig spackConfig, Map nodeSelector) + V1Job launchBuildJob(String name, String containerImage, List args, Path workDir, Path creds, Duration timeout, Map nodeSelector) V1Job launchScanJob(String name, String containerImage, List args, Path workDir, Path creds, ScanConfig scanConfig, Map nodeSelector) diff --git a/src/main/groovy/io/seqera/wave/service/k8s/K8sServiceImpl.groovy b/src/main/groovy/io/seqera/wave/service/k8s/K8sServiceImpl.groovy index 77fe61b6b..cad5ec9ea 100644 --- a/src/main/groovy/io/seqera/wave/service/k8s/K8sServiceImpl.groovy +++ b/src/main/groovy/io/seqera/wave/service/k8s/K8sServiceImpl.groovy @@ -47,7 +47,6 @@ import io.micronaut.core.annotation.Nullable import io.seqera.wave.configuration.BlobCacheConfig import io.seqera.wave.configuration.BuildConfig import io.seqera.wave.configuration.ScanConfig -import io.seqera.wave.configuration.SpackConfig import io.seqera.wave.core.ContainerPlatform import io.seqera.wave.service.scan.Trivy import jakarta.inject.Inject @@ -98,9 +97,6 @@ class K8sServiceImpl implements K8sService { @Nullable private String requestsMemory - @Inject - private SpackConfig spackConfig - @Inject private K8sClient k8sClient @@ -300,27 +296,6 @@ class K8sServiceImpl implements K8sService { .readOnly(true) } - protected V1VolumeMount mountSpackCacheDir(Path spackCacheDir, String storageMountPath, String containerPath) { - final rel = Path.of(storageMountPath).relativize(spackCacheDir).toString() - if( !rel || rel.startsWith('../') ) - throw new IllegalArgumentException("Spack cacheDirectory '$spackCacheDir' must be a sub-directory of storage path '$storageMountPath'") - return new V1VolumeMount() - .name('build-data') - .mountPath(containerPath) - .subPath(rel) - } - - protected V1VolumeMount mountSpackSecretFile(Path secretFile, String storageMountPath, String containerPath) { - final rel = Path.of(storageMountPath).relativize(secretFile).toString() - if( !rel || rel.startsWith('../') ) - throw new IllegalArgumentException("Spack secretKeyFile '$secretFile' must be a sub-directory of storage path '$storageMountPath'") - return new V1VolumeMount() - .name('build-data') - .readOnly(true) - .mountPath(containerPath) - .subPath(rel) - } - protected V1VolumeMount mountScanCacheDir(Path scanCacheDir, String storageMountPath) { final rel = Path.of(storageMountPath).relativize(scanCacheDir).toString() if( !rel || rel.startsWith('../') ) @@ -349,14 +324,14 @@ class K8sServiceImpl implements K8sService { */ @Override @Deprecated - V1Pod buildContainer(String name, String containerImage, List args, Path workDir, Path creds, Duration timeout, SpackConfig spackConfig, Map nodeSelector) { - final spec = buildSpec(name, containerImage, args, workDir, creds, timeout, spackConfig, nodeSelector) + V1Pod buildContainer(String name, String containerImage, List args, Path workDir, Path creds, Duration timeout, Map nodeSelector) { + final spec = buildSpec(name, containerImage, args, workDir, creds, timeout, nodeSelector) return k8sClient .coreV1Api() .createNamespacedPod(namespace, spec, null, null, null,null) } - V1Pod buildSpec(String name, String containerImage, List args, Path workDir, Path credsFile, Duration timeout, SpackConfig spackConfig, Map nodeSelector) { + V1Pod buildSpec(String name, String containerImage, List args, Path workDir, Path credsFile, Duration timeout, Map nodeSelector) { // dirty dependency to avoid introducing another parameter final singularity = containerImage.contains('singularity') @@ -379,10 +354,6 @@ class K8sServiceImpl implements K8sService { } } - if( spackConfig ) { - mounts.add(mountSpackSecretFile(spackConfig.secretKeyFile, storageMountPath, spackConfig.secretMountPath)) - } - V1PodBuilder builder = new V1PodBuilder() //metadata section @@ -670,14 +641,14 @@ class K8sServiceImpl implements K8sService { * The {@link V1Pod} description the submitted pod */ @Override - V1Job launchBuildJob(String name, String containerImage, List args, Path workDir, Path creds, Duration timeout, SpackConfig spackConfig, Map nodeSelector) { - final spec = buildJobSpec(name, containerImage, args, workDir, creds, timeout, spackConfig, nodeSelector) + V1Job launchBuildJob(String name, String containerImage, List args, Path workDir, Path creds, Duration timeout, Map nodeSelector) { + final spec = buildJobSpec(name, containerImage, args, workDir, creds, timeout, nodeSelector) return k8sClient .batchV1Api() .createNamespacedJob(namespace, spec, null, null, null,null) } - V1Job buildJobSpec(String name, String containerImage, List args, Path workDir, Path credsFile, Duration timeout, SpackConfig spackConfig, Map nodeSelector) { + V1Job buildJobSpec(String name, String containerImage, List args, Path workDir, Path credsFile, Duration timeout, Map nodeSelector) { // dirty dependency to avoid introducing another parameter final singularity = containerImage.contains('singularity') @@ -693,10 +664,6 @@ class K8sServiceImpl implements K8sService { mounts.add(0, mountHostPath(credsFile, storageMountPath, '/home/user/.docker/config.json')) } - if( spackConfig ) { - mounts.add(mountSpackSecretFile(spackConfig.secretKeyFile, storageMountPath, spackConfig.secretMountPath)) - } - V1JobBuilder builder = new V1JobBuilder() //metadata section diff --git a/src/main/groovy/io/seqera/wave/service/mail/impl/MailServiceImpl.groovy b/src/main/groovy/io/seqera/wave/service/mail/impl/MailServiceImpl.groovy index 6e73804aa..259183d25 100644 --- a/src/main/groovy/io/seqera/wave/service/mail/impl/MailServiceImpl.groovy +++ b/src/main/groovy/io/seqera/wave/service/mail/impl/MailServiceImpl.groovy @@ -97,7 +97,6 @@ class MailServiceImpl implements MailService { binding.build_platform = req.platform binding.build_containerfile = req.containerFile ?: '-' binding.build_condafile = req.condaFile - binding.build_spackfile = req.spackFile binding.build_digest = result.digest ?: '-' binding.put('build_log_data', result.logs) binding.build_url = "$serverUrl/view/builds/${result.id}" diff --git a/src/main/groovy/io/seqera/wave/service/persistence/WaveBuildRecord.groovy b/src/main/groovy/io/seqera/wave/service/persistence/WaveBuildRecord.groovy index 10107f5d0..ba5225b34 100644 --- a/src/main/groovy/io/seqera/wave/service/persistence/WaveBuildRecord.groovy +++ b/src/main/groovy/io/seqera/wave/service/persistence/WaveBuildRecord.groovy @@ -42,7 +42,6 @@ class WaveBuildRecord { String buildId String dockerFile String condaFile - String spackFile String targetImage String userName String userEmail @@ -66,7 +65,6 @@ class WaveBuildRecord { buildId: event.request.buildId, dockerFile: event.request.containerFile, condaFile: event.request.condaFile, - spackFile: event.request.spackFile, targetImage: event.request.targetImage, userName: event.request.identity.user?.userName, userEmail: event.request.identity.user?.email, diff --git a/src/main/groovy/io/seqera/wave/util/ContainerHelper.groovy b/src/main/groovy/io/seqera/wave/util/ContainerHelper.groovy index 39dd5ae35..ae90a0578 100644 --- a/src/main/groovy/io/seqera/wave/util/ContainerHelper.groovy +++ b/src/main/groovy/io/seqera/wave/util/ContainerHelper.groovy @@ -27,7 +27,6 @@ import io.seqera.wave.api.PackagesSpec import io.seqera.wave.api.SubmitContainerTokenRequest import io.seqera.wave.api.SubmitContainerTokenResponse import io.seqera.wave.config.CondaOpts -import io.seqera.wave.config.SpackOpts import io.seqera.wave.core.ContainerPlatform import io.seqera.wave.exception.BadRequestException import io.seqera.wave.service.ContainerRequestData @@ -35,16 +34,12 @@ import io.seqera.wave.service.builder.BuildFormat import io.seqera.wave.service.token.TokenData import org.yaml.snakeyaml.Yaml import static io.seqera.wave.service.builder.BuildFormat.SINGULARITY -import static io.seqera.wave.util.DockerHelper.addPackagesToSpackYaml import static io.seqera.wave.util.DockerHelper.condaEnvironmentToCondaYaml import static io.seqera.wave.util.DockerHelper.condaFileToDockerFile import static io.seqera.wave.util.DockerHelper.condaFileToSingularityFile import static io.seqera.wave.util.DockerHelper.condaPackagesToCondaYaml import static io.seqera.wave.util.DockerHelper.condaPackagesToDockerFile import static io.seqera.wave.util.DockerHelper.condaPackagesToSingularityFile -import static io.seqera.wave.util.DockerHelper.spackFileToDockerFile -import static io.seqera.wave.util.DockerHelper.spackFileToSingularityFile -import static io.seqera.wave.util.DockerHelper.spackPackagesToSpackYaml /** * Container helper methods * @@ -82,16 +77,7 @@ class ContainerHelper { return result } - if( spec.type == PackagesSpec.Type.SPACK ) { - if( !spec.spackOpts ) - spec.spackOpts = new SpackOpts() - final result = formatSingularity - ? spackFileToSingularityFile(spec.spackOpts) - : spackFileToDockerFile(spec.spackOpts) - return result - } - - throw new IllegalArgumentException("Unexpected packages spec type: $spec.type") + throw new BadRequestException("Unexpected packages spec type: $spec.type") } static String condaFileFromRequest(SubmitContainerTokenRequest req) { @@ -129,26 +115,6 @@ class ContainerHelper { return result[0] } - static String spackFileFromRequest(SubmitContainerTokenRequest req) { - if( !req.packages ) - return decodeBase64OrFail(req.spackFile,'spackFile') - - if( req.packages.type != PackagesSpec.Type.SPACK ) - return null - - if( req.packages.environment ) { - final decoded = decodeBase64OrFail(req.packages.environment,'packages.envFile') - return addPackagesToSpackYaml(decoded, req.packages.spackOpts) - } - - if( req.packages.entries ) { - final String packages = req.packages.entries.join(' ') - return spackPackagesToSpackYaml(packages, req.packages.spackOpts) - } - - return null - } - static String decodeBase64OrFail(String value, String field) { if( !value ) return null @@ -268,56 +234,7 @@ class ContainerHelper { return new Tuple2(parts[0], parts[1]) } - static NameVersionPair guessSpackRecipeName(String spackFileContent, boolean split=false) { - if( !spackFileContent ) - return null - try { - final yaml = new Yaml().load(spackFileContent) as Map - final spack = yaml.spack as Map - - if( !spack ){ - throw new BadRequestException('Malformed Spack environment file - missing "spack:" section') - } - if( !spack.specs ){ - throw new BadRequestException('Malformed Spack environment file - missing "spack.specs:" section') - } - - if( spack.specs instanceof List ) { - final LinkedHashSet result = new LinkedHashSet() - final LinkedHashSet versions = new LinkedHashSet() - for( String it : spack.specs ) { - final p = it.indexOf(' ') - // remove everything after the first blank because they are supposed package directives - if( p!=-1 ) - it = it.substring(0,p) - if( split ) { - final pair = splitVersion(it, '@') - it = pair.v1 - versions.add(pair.v2) - } - else { - // replaces '@' version separator with `-` - it = it.replace('@','-') - } - if( it ) - result.add(it) - } - return split - ? new NameVersionPair(result, versions) - : new NameVersionPair(result) - } - return null - } - catch (BadRequestException e) { - throw e - } - catch (Throwable e) { - log.warn "Unable to infer spack recipe name - cause: ${e.message}", e - return null - } - } - - static String makeTargetImage(BuildFormat format, String repo, String id, @Nullable String condaFile, @Nullable String spackFile, @Nullable ImageNameStrategy nameStrategy) { + static String makeTargetImage(BuildFormat format, String repo, String id, @Nullable String condaFile, @Nullable ImageNameStrategy nameStrategy) { assert id, "Argument 'id' cannot be null or empty" assert repo, "Argument 'repo' cannot be null or empty" assert format, "Argument 'format' cannot be null" @@ -328,9 +245,6 @@ class ContainerHelper { if( condaFile && (tools=guessCondaRecipeName(condaFile,false)) ) { tag = "${normaliseTag(tools.qualifiedNames())}--${id}" } - else if( spackFile && (tools=guessSpackRecipeName(spackFile,false)) ) { - tag = "${normaliseTag(tools.qualifiedNames())}--${id}" - } } else if( nameStrategy==ImageNameStrategy.imageSuffix ) { if( condaFile && (tools=guessCondaRecipeName(condaFile,true)) ) { @@ -338,11 +252,6 @@ class ContainerHelper { if( tools.versions?.size()==1 && tools.versions[0] ) tag = "${normaliseTag(tools.versions[0])}--${id}" } - else if( spackFile && (tools=guessSpackRecipeName(spackFile, true)) ) { - repo = StringUtils.pathConcat(repo, normaliseName(tools.friendlyNames())) - if( tools.versions?.size()==1 && tools.versions[0] ) - tag = "${normaliseTag(tools.versions[0])}--${id}" - } } else if( nameStrategy!=ImageNameStrategy.none ) { throw new BadRequestException("Unsupported image naming strategy: '${nameStrategy}'") @@ -387,13 +296,12 @@ class ContainerHelper { value ? normalise0(value.toLowerCase(), maxLength, /[^a-z0-9_.\-\/]/) : null } - static String makeContainerId(String containerFile, String condaFile, String spackFile, ContainerPlatform platform, String repository, BuildContext buildContext) { + static String makeContainerId(String containerFile, String condaFile, ContainerPlatform platform, String repository, BuildContext buildContext) { final attrs = new LinkedHashMap(10) attrs.containerFile = containerFile attrs.condaFile = condaFile attrs.platform = platform?.toString() attrs.repository = repository - if( spackFile ) attrs.spackFile = spackFile if( buildContext ) attrs.buildContext = buildContext.tarDigest return RegHelper.sipHash(attrs) } diff --git a/src/main/groovy/io/seqera/wave/util/SpackHelper.groovy b/src/main/groovy/io/seqera/wave/util/SpackHelper.groovy deleted file mode 100644 index 82a324a98..000000000 --- a/src/main/groovy/io/seqera/wave/util/SpackHelper.groovy +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Wave, containers provisioning service - * Copyright (c) 2023-2024, Seqera Labs - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package io.seqera.wave.util - - -import groovy.transform.CompileStatic -import io.seqera.wave.core.ContainerPlatform -import io.seqera.wave.service.builder.BuildFormat - -/** - * Helper class for Spack package manager - * - * @author Paolo Di Tommaso - */ -@CompileStatic -@Deprecated -class SpackHelper { - - static String builderDockerTemplate() { - SpackHelper.class - .getResourceAsStream('/io/seqera/wave/spack/spack-builder-dockerfile.txt') - .getText() - } - - static String builderSingularityTemplate() { - SpackHelper.class - .getResourceAsStream('/io/seqera/wave/spack/spack-builder-singularityfile.txt') - .getText() - } - - static String prependBuilderTemplate(String dockerContent, BuildFormat buildFormat) { - if(buildFormat == BuildFormat.SINGULARITY){ - return builderSingularityTemplate() + dockerContent - } - else if( buildFormat == BuildFormat.DOCKER ) { - return builderDockerTemplate() + dockerContent - } - else - throw new IllegalStateException("Unexpected build format: $buildFormat") - } - - static String toSpackArch(ContainerPlatform platform) { - if( !platform ) - throw new IllegalArgumentException("Missing container platform argument") - final value = platform.toString() - if( value=='linux/amd64' ) - return 'x86_64' - if( value=='linux/arm64' ) - return 'aarch64' - throw new IllegalArgumentException("Unable to map container platform '${platform}' to Spack architecture") - } - -} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 297fe0279..f9dc8c18f 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -4,8 +4,6 @@ wave: enabled: true build: workspace: 'build-workspace' - spack: - cacheDirectory: 'spack-cache' metrics: enabled: true accounts: diff --git a/src/main/resources/io/seqera/wave/build-notification.html b/src/main/resources/io/seqera/wave/build-notification.html index 61d14fb04..0c3a96c3a 100644 --- a/src/main/resources/io/seqera/wave/build-notification.html +++ b/src/main/resources/io/seqera/wave/build-notification.html @@ -111,11 +111,6 @@

Conda file

${build_condafile}
<% } %> - <% if (build_spackfile) { %> -

Spack file

-
${build_spackfile}
- <% } %> -

Build logs

${build_log_data}
diff --git a/src/main/resources/io/seqera/wave/build-view.hbs b/src/main/resources/io/seqera/wave/build-view.hbs index acceacc55..821050224 100644 --- a/src/main/resources/io/seqera/wave/build-view.hbs +++ b/src/main/resources/io/seqera/wave/build-view.hbs @@ -127,11 +127,6 @@
{{build_condafile}}
{{/if}} - {{#if build_spackfile}} -

Spack file

-
{{build_spackfile}}
- {{/if}} - {{#if build_log_data}}

Build logs

{{build_log_data}}
diff --git a/src/main/resources/io/seqera/wave/inspect-view.hbs b/src/main/resources/io/seqera/wave/inspect-view.hbs new file mode 100644 index 000000000..3523dd2c4 --- /dev/null +++ b/src/main/resources/io/seqera/wave/inspect-view.hbs @@ -0,0 +1,234 @@ + + + + + + + Wave inspect: {{imageName}} + + + +
+ +
+ +

Wave container inspect

+
+
+ {{#if error_message}} +
+

+ {{error_message}} +

+
+ {{else}} + {{/if}} +

Container Specification

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Image Name{{imageName}}
Reference (tag){{reference}}
Digest{{digest}}
Registry{{registry}}
Host Name{{hostName}}
Config
Manifest
+
+ +
+ + + diff --git a/src/main/resources/io/seqera/wave/spack/spack-builder-dockerfile.txt b/src/main/resources/io/seqera/wave/spack/spack-builder-dockerfile.txt deleted file mode 100644 index 5637b1533..000000000 --- a/src/main/resources/io/seqera/wave/spack/spack-builder-dockerfile.txt +++ /dev/null @@ -1,53 +0,0 @@ -# Builder image -FROM {{spack_builder_image}} as builder -COPY spack.yaml /opt/spack-env/spack.yaml - -# Assume the values from the environment -ARG AWS_STS_REGIONAL_ENDPOINTS -ARG AWS_REGION -ARG AWS_DEFAULT_REGION -ARG AWS_ROLE_ARN -ARG AWS_WEB_IDENTITY_TOKEN_FILE - -ENV \ - AWS_STS_REGIONAL_ENDPOINTS=${AWS_STS_REGIONAL_ENDPOINTS} \ - AWS_REGION=${AWS_REGION} \ - AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION} \ - AWS_ROLE_ARN=${AWS_ROLE_ARN} \ - AWS_WEB_IDENTITY_TOKEN_FILE=${AWS_WEB_IDENTITY_TOKEN_FILE} - -RUN mkdir -p /opt/spack-env \ -&& sed -i -e 's;compilers:;compilers::;' \ - -e 's;^ *flags: *{}; flags:\n cflags: -O3\n cxxflags: -O3\n fflags: -O3;' \ - /root/.spack/linux/compilers.yaml \ -&& cd /opt/spack-env \ -&& spack config add config:install_tree:/opt/software \ -&& spack config add concretizer:unify:true \ -&& spack config add concretizer:reuse:true \ -&& spack config add packages:all:target:[{{spack_arch}}] \ -&& printf " view: /opt/view\n" >> /opt/spack-env/spack.yaml - -# Install packages, clean afterward, finally strip binaries -RUN cd /opt/spack-env \ -&& fingerprint="$(spack gpg trust {{spack_key_file}} 2>&1 | tee /dev/stderr | sed -nr "s/^gpg: key ([0-9A-F]{16}): secret key imported$/\1/p")" \ -&& spack mirror add seqera-spack {{spack_cache_bucket}} \ -&& spack mirror add binary_mirror https://binaries.spack.io/releases/v0.20 \ -&& spack buildcache keys --install --trust \ -&& spack -e . concretize -f \ -&& spack --env . install \ -&& spack -e . buildcache push --allow-root --key "$fingerprint" {{spack_cache_bucket}} \ -&& spack gc -y \ -&& ( find -L /opt/._view/* -type f -exec readlink -f '{}' \; | \ - xargs file -i | \ - grep 'charset=binary' | \ - grep 'x-executable\|x-archive\|x-sharedlib' | \ - awk -F: '{print $1}' | xargs strip -s ) || true - -RUN cd /opt/spack-env && \ - spack env activate --sh -d . >> /opt/spack-env/z10_spack_environment.sh && \ - original_view=$( cd /opt ; ls -1d ._view/* ) && \ - sed -i "s;/view/;/$original_view/;" /opt/spack-env/z10_spack_environment.sh && \ - echo "# Needed for Perl applications" >>/opt/spack-env/z10_spack_environment.sh && \ - echo "export PERL5LIB=$(eval ls -d /opt/._view/*/lib/5.*):$PERL5LIB" >>/opt/spack-env/z10_spack_environment.sh && \ - rm -rf /opt/view - diff --git a/src/main/resources/io/seqera/wave/spack/spack-builder-singularityfile.txt b/src/main/resources/io/seqera/wave/spack/spack-builder-singularityfile.txt deleted file mode 100644 index 5500cd652..000000000 --- a/src/main/resources/io/seqera/wave/spack/spack-builder-singularityfile.txt +++ /dev/null @@ -1,59 +0,0 @@ -Bootstrap: docker -From: {{spack_builder_image}} -Stage: build - -%setup -cat < ${SINGULARITY_ROOTFS}/aws-env.sh -export AWS_STS_REGIONAL_ENDPOINTS=${AWS_STS_REGIONAL_ENDPOINTS} -export AWS_REGION=${AWS_REGION} -export AWS_DEFAULT_REGION=${AWS_REGION} -export AWS_ROLE_ARN=${AWS_ROLE_ARN} -export AWS_WEB_IDENTITY_TOKEN_FILE=${AWS_WEB_IDENTITY_TOKEN_FILE} -EOF - -%files - {{wave_context_dir}}/spack.yaml /opt/spack-env/spack.yaml - {{spack_key_file}} {{spack_key_file}} - /var/run/secrets /var/run/secrets - -%post - # import AWS environment - . /aws-env.sh - - # Copy and modify spack.yaml - sed -i -e 's;compilers:;compilers::;' \ - -e 's;^ *flags: *{}; flags:\n cflags: -O3\n cxxflags: -O3\n fflags: -O3;' \ - /root/.spack/linux/compilers.yaml - - # Set up Spack environment - export PATH=/opt/spack/bin:$PATH - cd /opt/spack-env - spack config add config:install_tree:/opt/software - spack config add concretizer:unify:true - spack config add concretizer:reuse:true - spack config add packages:all:target:[{{spack_arch}}] - printf " view: /opt/view\n" >> /opt/spack-env/spack.yaml - - # Install packages, clean afterward, finally strip binaries - fingerprint="$(spack gpg trust {{spack_key_file}} 2>&1 | tee /dev/stderr | sed -nr "s/^gpg: key ([0-9A-F]{16}): secret key imported$/\1/p")" - spack mirror add seqera-spack {{spack_cache_bucket}} - spack mirror add binary_mirror https://binaries.spack.io/releases/v0.20 - spack buildcache keys --install --trust - spack -e . concretize -f - spack --env . install - spack -e . buildcache push --allow-root --key "$fingerprint" {{spack_cache_bucket}} - spack gc -y - ( find -L /opt/._view/* -type f -exec readlink -f '{}' \; | \ - xargs file -i | \ - grep 'charset=binary' | \ - grep 'x-executable\|x-archive\|x-sharedlib' | \ - awk -F: '{print $1}' | xargs strip -s ) || true - - # Set up environment variables - spack env activate --sh -d . >> /opt/spack-env/z10_spack_environment.sh - original_view=$( cd /opt ; ls -1d ._view/* ) - sed -i "s;/view/;/$original_view/;" /opt/spack-env/z10_spack_environment.sh - echo "# Needed for Perl applications" >>/opt/spack-env/z10_spack_environment.sh - echo "export PERL5LIB=$(set +f; ls -d /opt/._view/*/lib/5.*):\$PERL5LIB" >>/opt/spack-env/z10_spack_environment.sh - rm -rf /opt/view - diff --git a/src/test/groovy/io/seqera/wave/JavaClientTest.groovy b/src/test/groovy/io/seqera/wave/JavaClientTest.groovy deleted file mode 100644 index 5f830ca1d..000000000 --- a/src/test/groovy/io/seqera/wave/JavaClientTest.groovy +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Wave, containers provisioning service - * Copyright (c) 2023-2024, Seqera Labs - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package io.seqera.wave - -import spock.lang.Requires - -import groovy.json.JsonSlurper -import spock.lang.Specification - -import java.net.http.HttpClient -import java.net.http.HttpRequest -import java.net.http.HttpResponse -import java.time.Duration - -/** - * - * @author Paolo Di Tommaso - */ -class JavaClientTest extends Specification{ - - @Requires({System.getenv('DOCKER_USER') && System.getenv('DOCKER_PAT')}) - def 'should call target blob' () { - given: - def username = System.getenv('DOCKER_USER') - def IMAGE = 'library/hello-world' - def DIGEST = 'sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412' - def pat = System.getenv('DOCKER_PAT') - def basic = "$username:$pat".bytes.encodeBase64() - def auth = "Basic $basic" - and: - HttpClient httpClient = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_1_1) - .followRedirects(HttpClient.Redirect.NORMAL) - .connectTimeout(Duration.ofSeconds(10)) - .build() - - when: - HttpRequest req0 = HttpRequest.newBuilder() - .GET() - .uri(URI.create("https://auth.docker.io/token?service=registry.docker.io&scope=repository:${IMAGE}:pull")) - .setHeader("Authorization", auth.toString()) // add resp0 header - .build() - - HttpResponse resp0 = httpClient.send(req0, HttpResponse.BodyHandlers.ofString()); - and: - def json = (Map) new JsonSlurper().parseText(resp0.body()) - println json - then: - resp0.statusCode() == 200 - json.token != null - - when: - def req1 = HttpRequest.newBuilder() - .GET() - .uri(URI.create("https://registry-1.docker.io/v2/library/hello-world/blobs/sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412")) - .setHeader("Authorization", "Bearer ${json.token}") // add resp0 header - .build() - and: - HttpResponse resp1 = httpClient.send(req1, HttpResponse.BodyHandlers.ofString()); - then: - resp1.statusCode() == 200 - - } -} diff --git a/src/test/groovy/io/seqera/wave/MicronautClientTest.groovy b/src/test/groovy/io/seqera/wave/MicronautClientTest.groovy deleted file mode 100644 index 9342c17ee..000000000 --- a/src/test/groovy/io/seqera/wave/MicronautClientTest.groovy +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Wave, containers provisioning service - * Copyright (c) 2023-2024, Seqera Labs - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package io.seqera.wave - -import io.micronaut.http.HttpMethod -import io.micronaut.http.HttpRequest -import io.micronaut.http.HttpResponse -import io.micronaut.http.HttpStatus -import io.micronaut.http.client.HttpClient -import io.seqera.wave.proxy.LoginResponse -import spock.lang.IgnoreIf -import spock.lang.Specification -/** - * - * @author Paolo Di Tommaso - */ -class MicronautClientTest extends Specification{ - - @IgnoreIf({ System.getenv("DOCKER_USER") == null}) - def 'should call target blob' () { - given: - def username = System.getenv("DOCKER_USER") - def IMAGE = 'library/hello-world' - def DIGEST = 'sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412' - def pat = System.getenv("DOCKER_PAT") - def client = HttpClient.create(new URL('https://auth.docker.io')) - def login = "/token?service=registry.docker.io&scope=repository:${IMAGE}:pull" - def basic = "$username:$pat".bytes.encodeBase64() - def auth = "Basic $basic" - - when: - HttpRequest request = HttpRequest.create(HttpMethod.GET, login) - .header("Authorization", auth.toString()) - HttpResponse resp = client.toBlocking().exchange(request, LoginResponse); - println resp.body() - then: - resp.status() == HttpStatus.OK - resp.body().access_token - - when: - def token = resp.body().token - def registry = HttpClient.create(new URL(' https://registry-1.docker.io')) - def req1 = HttpRequest.create(HttpMethod.GET, "/v2/library/hello-world/blobs/sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412") - .header("Authorization", "Bearer ${token}") - .header('Accept-Encoding', 'identity') -// .accept("application/json") -// .accept("application/vnd.docker.distribution.manifest.v2+json") -// .accept("application/vnd.docker.distribution.manifest.list.v2+json") -// .accept("application/vnd.oci.image.index.v1+json") -// .accept("application/vnd.docker.distribution.manifest.v1+prettyjws") -// .accept("application/vnd.oci.image.manifest.v1+json") - def resp1 = registry.toBlocking().exchange(req1); - then: - resp1.status == HttpStatus.OK - } -} diff --git a/src/test/groovy/io/seqera/wave/controller/BuildConfigTest.groovy b/src/test/groovy/io/seqera/wave/controller/BuildConfigTest.groovy index aacb51586..74d2db23d 100644 --- a/src/test/groovy/io/seqera/wave/controller/BuildConfigTest.groovy +++ b/src/test/groovy/io/seqera/wave/controller/BuildConfigTest.groovy @@ -56,7 +56,6 @@ class BuildConfigTest extends Specification { config.singularityImage( ContainerPlatform.of('arm64') ) == 'bar' } - @Unroll def 'should validate build max duration' () { given: @@ -72,6 +71,5 @@ class BuildConfigTest extends Specification { 'xyz' | false | 5 | 10 | 5 'xtz' | true | 5 | 10 | 10 // <-- pick "trusted" because both "freeze" and "token" are provided 'xtz' | true | 20 | 10 | 20 // <-- pick "default" when it's greater than "trusted" - } } diff --git a/src/test/groovy/io/seqera/wave/controller/BuildControllerTest.groovy b/src/test/groovy/io/seqera/wave/controller/BuildControllerTest.groovy index fc69fc7b2..5809d680e 100644 --- a/src/test/groovy/io/seqera/wave/controller/BuildControllerTest.groovy +++ b/src/test/groovy/io/seqera/wave/controller/BuildControllerTest.groovy @@ -75,8 +75,8 @@ class BuildControllerTest extends Specification { final containerFile = 'FROM foo:latest' final format = BuildFormat.DOCKER final platform = ContainerPlatform.of('amd64') - final containerId = ContainerHelper.makeContainerId(containerFile, null, null, platform, 'buildrepo', null) - final targetImage = ContainerHelper.makeTargetImage(format, repo, containerId, null, null, null) + final containerId = ContainerHelper.makeContainerId(containerFile, null, platform, 'buildrepo', null) + final targetImage = ContainerHelper.makeTargetImage(format, repo, containerId, null, null) final build = new BuildRequest( containerId: containerId, containerFile: containerFile, diff --git a/src/test/groovy/io/seqera/wave/controller/ContainerControllerTest.groovy b/src/test/groovy/io/seqera/wave/controller/ContainerControllerTest.groovy index 0d707d001..aaf737ab9 100644 --- a/src/test/groovy/io/seqera/wave/controller/ContainerControllerTest.groovy +++ b/src/test/groovy/io/seqera/wave/controller/ContainerControllerTest.groovy @@ -37,7 +37,6 @@ import io.seqera.wave.api.PackagesSpec import io.seqera.wave.api.SubmitContainerTokenRequest import io.seqera.wave.api.SubmitContainerTokenResponse import io.seqera.wave.config.CondaOpts -import io.seqera.wave.config.SpackOpts import io.seqera.wave.configuration.BuildConfig import io.seqera.wave.core.ContainerPlatform import io.seqera.wave.core.RegistryProxyService @@ -280,17 +279,6 @@ class ContainerControllerTest extends Specification { build.targetImage == 'wave/build:c6dac2e544419f71' build.platform == ContainerPlatform.of('arm64') - when: - submit = new SubmitContainerTokenRequest(containerFile: encode('FROM foo'), spackFile: encode('some::spack-recipe'), containerPlatform: 'arm64') - build = controller.makeBuildRequest(submit, PlatformId.NULL, "") - then: - build.containerId =~ /b7d730d274d1e057/ - build.containerFile.endsWith('\nFROM foo') - build.containerFile.startsWith('# Builder image\n') - build.condaFile == null - build.spackFile == 'some::spack-recipe' - build.targetImage == 'wave/build:b7d730d274d1e057' - build.platform == ContainerPlatform.of('arm64') } def 'should return a bad request exception when field is not encoded' () { @@ -318,16 +306,6 @@ class ContainerControllerTest extends Specification { e = thrown(BadRequestException) e.message == "Invalid 'condaFile' attribute - make sure it encoded as a base64 string" - // validate spackFile - when: - controller.makeBuildRequest( - new SubmitContainerTokenRequest(containerFile: encode('FROM foo'), spackFile: 'spack@123'), - Mock(PlatformId), - null) - then: - e = thrown(BadRequestException) - e.message == "Invalid 'spackFile' attribute - make sure it encoded as a base64 string" - } def 'should add library prefix' () { @@ -459,42 +437,6 @@ class ContainerControllerTest extends Specification { } } - def 'should create response with spack packages' () { - given: - def dockerAuth = Mock(ContainerInspectServiceImpl) - def freeze = new FreezeServiceImpl( inspectService: dockerAuth) - def builder = Mock(ContainerBuildService) - def proxyRegistry = Mock(RegistryProxyService) - def addressResolver = Mock(HttpClientAddressResolver) - def tokenService = Mock(ContainerTokenService) - def persistence = Mock(PersistenceService) - def controller = new ContainerController(freezeService: freeze, buildService: builder, dockerAuthService: dockerAuth, - registryProxyService: proxyRegistry, buildConfig: buildConfig, inclusionService: Mock(ContainerInclusionService), - addressResolver: addressResolver, tokenService: tokenService, persistenceService: persistence, serverUrl: 'https://wave.seqera.io') - - when:'packages with spack' - def SPACK_OPTS = new SpackOpts([ - basePackages: 'foo bar', - commands: ['run','--this','--that'] - ]) - def packages = new PackagesSpec(type: PackagesSpec.Type.SPACK, spackOpts: SPACK_OPTS) - def req = new SubmitContainerTokenRequest(format: 'docker', packages: packages) - def response = controller.handleRequest(null, req, new PlatformId(new User(id: 100), 10), true) - - then: - 1 * builder.buildImage(_) >> new BuildTrack('build123', 'foo:1234', true) - and: - 1 * tokenService.computeToken(_) >> new TokenData('wavetoken123', Instant.now().plus(1, ChronoUnit.HOURS)) - and: - response.status.code == 200 - verifyAll(response.body.get() as SubmitContainerTokenResponse) { - targetImage == 'wave.seqera.io/wt/wavetoken123/library/foo:1234' - buildId == 'build123' - containerToken == 'wavetoken123' - cached == true - } - } - def 'should throw BadRequestException when more than one artifact (container image, container file or packages) is provided in the request' () { given: def controller = new ContainerController(inclusionService: Mock(ContainerInclusionService), allowAnonymous: false) diff --git a/src/test/groovy/io/seqera/wave/controller/ViewControllerTest.groovy b/src/test/groovy/io/seqera/wave/controller/ViewControllerTest.groovy index 0a3e2d040..efb1e48ae 100644 --- a/src/test/groovy/io/seqera/wave/controller/ViewControllerTest.groovy +++ b/src/test/groovy/io/seqera/wave/controller/ViewControllerTest.groovy @@ -24,6 +24,7 @@ import java.time.Duration import java.time.Instant import io.micronaut.http.HttpRequest +import io.micronaut.http.HttpStatus import io.micronaut.http.client.HttpClient import io.micronaut.http.client.annotation.Client import io.micronaut.test.annotation.MockBean @@ -31,7 +32,10 @@ import io.micronaut.test.extensions.spock.annotation.MicronautTest import io.seqera.wave.api.ContainerConfig import io.seqera.wave.api.SubmitContainerTokenRequest import io.seqera.wave.core.ContainerPlatform +import io.seqera.wave.core.spec.ContainerSpec +import io.seqera.wave.exception.DockerRegistryException import io.seqera.wave.service.ContainerRequestData +import io.seqera.wave.service.inspect.ContainerInspectService import io.seqera.wave.service.logs.BuildLogService import io.seqera.wave.service.logs.BuildLogServiceImpl import io.seqera.wave.service.persistence.PersistenceService @@ -67,6 +71,9 @@ class ViewControllerTest extends Specification { @Inject BuildLogService buildLogService + @Inject + private ContainerInspectService inspectService + def 'should render build page' () { given: def controller = new ViewController(serverUrl: 'http://foo.com', buildLogService: buildLogService) @@ -75,7 +82,6 @@ class ViewControllerTest extends Specification { buildId: '12345', dockerFile: 'FROM foo', condaFile: 'conda::foo', - spackFile: 'some-spack-recipe', targetImage: 'docker.io/some:image', userName: 'paolo', userEmail: 'paolo@seqera.io', @@ -101,7 +107,6 @@ class ViewControllerTest extends Specification { binding.build_platform == 'linux/amd64' binding.build_containerfile == 'FROM foo' binding.build_condafile == 'conda::foo' - binding.build_spackfile == 'some-spack-recipe' binding.build_format == 'Docker' binding.build_log_data == 'log content' binding.build_log_truncated == false @@ -137,7 +142,6 @@ class ViewControllerTest extends Specification { response.body().contains('FROM docker.io/test:foo') and: !response.body().contains('Conda file') - !response.body().contains('Spack file') } def 'should render a build page with conda file' () { @@ -167,39 +171,6 @@ class ViewControllerTest extends Specification { and: response.body().contains('Conda file') response.body().contains('conda::foo') - and: - !response.body().contains('Spack file') - } - - def 'should render a build page with spack file' () { - given: - def record1 = new WaveBuildRecord( - buildId: 'test', - spackFile: 'foo/conda/recipe', - targetImage: 'test', - userName: 'test', - userEmail: 'test', - userId: 1, - requestIp: '127.0.0.1', - startTime: Instant.now(), - duration: Duration.ofSeconds(1), - exitStatus: 0 ) - - when: - persistenceService.saveBuild(record1) - and: - def request = HttpRequest.GET("/view/builds/${record1.buildId}") - def response = client.toBlocking().exchange(request, String) - then: - response.body().contains(record1.buildId) - and: - response.body().contains('Container file') - response.body().contains('-') - and: - !response.body().contains('Conda file') - and: - response.body().contains('Spack file') - response.body().contains('foo/conda/recipe') } def 'should render container view page' () { @@ -236,6 +207,18 @@ class ViewControllerTest extends Specification { response.body().contains(token) } + def 'should render inspect view'() { + when: + def request = HttpRequest.GET('/view/inspect?image=ubuntu') + def response = client.toBlocking().exchange(request, String) + + then: + response.status == HttpStatus.OK + response.body().contains('ubuntu') + response.body().contains('latest') + response.body().contains('https://registry-1.docker.io') + } + def 'should render in progress build page' () { given: def controller = new ViewController(serverUrl: 'http://foo.com', buildLogService: buildLogService) @@ -244,7 +227,6 @@ class ViewControllerTest extends Specification { buildId: '12345', dockerFile: 'FROM foo', condaFile: 'conda::foo', - spackFile: 'some-spack-recipe', targetImage: 'docker.io/some:image', userName: 'paolo', userEmail: 'paolo@seqera.io', @@ -269,7 +251,6 @@ class ViewControllerTest extends Specification { binding.build_platform == 'linux/amd64' binding.build_containerfile == 'FROM foo' binding.build_condafile == 'conda::foo' - binding.build_spackfile == 'some-spack-recipe' binding.build_format == 'Docker' binding.build_log_data == 'log content' binding.build_log_truncated == false @@ -287,7 +268,6 @@ class ViewControllerTest extends Specification { buildId: '12345', dockerFile: 'FROM foo', condaFile: 'conda::foo', - spackFile: 'some-spack-recipe', targetImage: 'docker.io/some:image', userName: 'paolo', userEmail: 'paolo@seqera.io', @@ -313,7 +293,6 @@ class ViewControllerTest extends Specification { binding.build_platform == 'linux/amd64' binding.build_containerfile == 'FROM foo' binding.build_condafile == 'conda::foo' - binding.build_spackfile == 'some-spack-recipe' binding.build_format == 'Docker' binding.build_log_data == 'log content' binding.build_log_truncated == false @@ -347,4 +326,5 @@ class ViewControllerTest extends Specification { binding.vulnerabilities == [new ScanVulnerability(id:'cve-1', severity:'HIGH', title:'test vul', pkgName:'testpkg', installedVersion:'1.0.0', fixedVersion:'1.1.0', primaryUrl:'http://vul/cve-1')] binding.build_url == 'http://foo.com/view/builds/12345' } + } diff --git a/src/test/groovy/io/seqera/wave/encoder/MoshiEncodingStrategyTest.groovy b/src/test/groovy/io/seqera/wave/encoder/MoshiEncodingStrategyTest.groovy index 9cee2c36f..361fa3c79 100644 --- a/src/test/groovy/io/seqera/wave/encoder/MoshiEncodingStrategyTest.groovy +++ b/src/test/groovy/io/seqera/wave/encoder/MoshiEncodingStrategyTest.groovy @@ -329,7 +329,6 @@ class MoshiEncodingStrategyTest extends Specification { containerId: '12345', containerFile: 'from foo', condaFile: 'conda spec', - spackFile: 'spack spec', workspace: Path.of("/some/path"), targetImage: 'docker.io/some:image:12345', identity: PlatformId.NULL, @@ -356,6 +355,21 @@ class MoshiEncodingStrategyTest extends Specification { copy == record1 } + def 'should decode legacy record with spack config' () { + given: + def legacy = '{"buildId":"12345_1","condaFile":"conda spec","dockerFile":"from foo","duration":"3000000000","exitStatus":-1,"platform":"linux/amd64","requestIp":"1.2.3.4","scanId":"scan12345","spackFile":"spack spec","targetImage":"docker.io/some:image:12345"}\n' + and: + def encoder = new MoshiEncodeStrategy() { } + + // verify decoding is OK when the payload contains `spackFile` not existing anymore in the WaveBuildRecord + when: + def rec = encoder.decode(legacy) + then: + rec.buildId == '12345_1' + rec.condaFile == 'conda spec' + rec.dockerFile == 'from foo' + } + def 'should encode and decode registry info' () { given: def encoder = new MoshiEncodeStrategy() { } @@ -405,7 +419,6 @@ class MoshiEncodingStrategyTest extends Specification { containerId: '12345', containerFile: 'from foo', condaFile: 'conda spec', - spackFile: 'spack spec', workspace: Path.of("/some/path"), targetImage: 'docker.io/some:image:12345', identity: PlatformId.NULL, diff --git a/src/test/groovy/io/seqera/wave/ratelimit/BuildServiceRateLimitTest.groovy b/src/test/groovy/io/seqera/wave/ratelimit/BuildServiceRateLimitTest.groovy index 6bff0952a..e8e05f867 100644 --- a/src/test/groovy/io/seqera/wave/ratelimit/BuildServiceRateLimitTest.groovy +++ b/src/test/groovy/io/seqera/wave/ratelimit/BuildServiceRateLimitTest.groovy @@ -72,8 +72,8 @@ class BuildServiceRateLimitTest extends Specification { RUN echo hi > hello.txt """.stripIndent() and: - def CONTAINER_ID = ContainerHelper.makeContainerId(dockerfile, null, null, ContainerPlatform.of('amd64'), buildRepo, null) - def TARGET_IMAGE = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, CONTAINER_ID, null, null, null) + def CONTAINER_ID = ContainerHelper.makeContainerId(dockerfile, null, ContainerPlatform.of('amd64'), buildRepo, null) + def TARGET_IMAGE = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, CONTAINER_ID, null, null) def REQ = new BuildRequest( containerId: CONTAINER_ID, containerFile: dockerfile, @@ -106,8 +106,8 @@ class BuildServiceRateLimitTest extends Specification { RUN echo hi > hello.txt """.stripIndent() and: - def CONTAINER_ID = ContainerHelper.makeContainerId(dockerfile, null, null, ContainerPlatform.of('amd64'), buildRepo, null) - def TARGET_IMAGE = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, CONTAINER_ID, null, null, null) + def CONTAINER_ID = ContainerHelper.makeContainerId(dockerfile, null, ContainerPlatform.of('amd64'), buildRepo, null) + def TARGET_IMAGE = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, CONTAINER_ID, null, null) def REQ = new BuildRequest( containerId: CONTAINER_ID, containerFile: dockerfile, diff --git a/src/test/groovy/io/seqera/wave/service/builder/BuildRequestTest.groovy b/src/test/groovy/io/seqera/wave/service/builder/BuildRequestTest.groovy index e5eaa2172..9959f7873 100644 --- a/src/test/groovy/io/seqera/wave/service/builder/BuildRequestTest.groovy +++ b/src/test/groovy/io/seqera/wave/service/builder/BuildRequestTest.groovy @@ -52,15 +52,14 @@ class BuildRequestTest extends Specification { def CONTEXT = Mock(BuildContext) def PLATFORM = ContainerPlatform.of('amd64') def FORMAT = BuildFormat.DOCKER - def CONTAINER_ID = ContainerHelper.makeContainerId(CONTENT, null, null, PLATFORM, BUILD_REPO, CONTEXT) - def TARGET_IMAGE = ContainerHelper.makeTargetImage(FORMAT, BUILD_REPO, CONTAINER_ID, null, null, null) + def CONTAINER_ID = ContainerHelper.makeContainerId(CONTENT, null, PLATFORM, BUILD_REPO, CONTEXT) + def TARGET_IMAGE = ContainerHelper.makeTargetImage(FORMAT, BUILD_REPO, CONTAINER_ID, null, null) when: def req = new BuildRequest( CONTAINER_ID, CONTENT, null, - null, PATH, TARGET_IMAGE, USER, @@ -85,7 +84,6 @@ class BuildRequestTest extends Specification { req.cacheRepository == CACHE_REPO req.format == BuildFormat.DOCKER req.condaFile == null - req.spackFile == null req.platform == ContainerPlatform.of('amd64') req.configJson == '{"config":"json"}' req.scanId == SCAN_ID @@ -93,8 +91,6 @@ class BuildRequestTest extends Specification { req.offsetId == OFFSET req.containerConfig == CONFIG req.buildContext == CONTEXT - and: - !req.isSpackBuild // ==== provide a Conda recipe ==== when: @@ -103,13 +99,12 @@ class BuildRequestTest extends Specification { - samtools=1.0 ''' and: - CONTAINER_ID = ContainerHelper.makeContainerId(CONTENT, CONDA_RECIPE, null, PLATFORM, BUILD_REPO, CONTEXT) - TARGET_IMAGE = ContainerHelper.makeTargetImage(FORMAT, BUILD_REPO, CONTAINER_ID, CONDA_RECIPE, null, null) + CONTAINER_ID = ContainerHelper.makeContainerId(CONTENT, CONDA_RECIPE, PLATFORM, BUILD_REPO, CONTEXT) + TARGET_IMAGE = ContainerHelper.makeTargetImage(FORMAT, BUILD_REPO, CONTAINER_ID, CONDA_RECIPE, null) req = new BuildRequest( CONTAINER_ID, CONTENT, CONDA_RECIPE, - null, PATH, TARGET_IMAGE, USER, @@ -128,24 +123,14 @@ class BuildRequestTest extends Specification { req.containerId == '8026e3a63b5c863f' req.targetImage == 'docker.io/wave:samtools-1.0--8026e3a63b5c863f' req.condaFile == CONDA_RECIPE - req.spackFile == null - and: - !req.isSpackBuild - - // ===== spack content ==== - def SPACK_RECIPE = '''\ - spack: - specs: [bwa@0.7.15] - ''' when: - CONTAINER_ID = ContainerHelper.makeContainerId(CONTENT, null, SPACK_RECIPE, PLATFORM, BUILD_REPO, CONTEXT) - TARGET_IMAGE = ContainerHelper.makeTargetImage(FORMAT, BUILD_REPO, CONTAINER_ID, null, SPACK_RECIPE, null) + CONTAINER_ID = ContainerHelper.makeContainerId(CONTENT, null, PLATFORM, BUILD_REPO, CONTEXT) + TARGET_IMAGE = ContainerHelper.makeTargetImage(FORMAT, BUILD_REPO, CONTAINER_ID, null, null) req = new BuildRequest( CONTAINER_ID, CONTENT, null, - SPACK_RECIPE, PATH, TARGET_IMAGE, USER, @@ -161,12 +146,9 @@ class BuildRequestTest extends Specification { TIMEOUT ) then: - req.containerId == '8726782b1d9bb8fb' - req.targetImage == 'docker.io/wave:bwa-0.7.15--8726782b1d9bb8fb' - req.spackFile == SPACK_RECIPE + req.containerId == '181ec22b26ae6d04' + req.targetImage == 'docker.io/wave:181ec22b26ae6d04' req.condaFile == null - and: - req.isSpackBuild } def 'should create singularity build request'() { @@ -183,15 +165,14 @@ class BuildRequestTest extends Specification { def CONTEXT = Mock(BuildContext) def PLATFORM = ContainerPlatform.of('amd64') def FORMAT = BuildFormat.SINGULARITY - def CONTAINER_ID = ContainerHelper.makeContainerId(CONTENT, null, null, PLATFORM, BUILD_REPO, CONTEXT) - def TARGET_IMAGE = ContainerHelper.makeTargetImage(FORMAT, BUILD_REPO, CONTAINER_ID, null, null, null) + def CONTAINER_ID = ContainerHelper.makeContainerId(CONTENT, null, PLATFORM, BUILD_REPO, CONTEXT) + def TARGET_IMAGE = ContainerHelper.makeTargetImage(FORMAT, BUILD_REPO, CONTAINER_ID, null, null) when: def req = new BuildRequest( CONTAINER_ID, CONTENT, null, - null, PATH, TARGET_IMAGE, USER, @@ -220,8 +201,6 @@ class BuildRequestTest extends Specification { req.offsetId == OFFSET req.containerConfig == CONFIG req.buildContext == CONTEXT - and: - !req.isSpackBuild } @@ -238,28 +217,28 @@ class BuildRequestTest extends Specification { def FOO_CONTENT = 'from foo' def BAR_CONTENT = 'from bar' and: - def CONTAINER_ID1 = ContainerHelper.makeContainerId(FOO_CONTENT, null, null, PLATFORM, BUILD_REPO, null) - def TARGET_IMAGE1 = ContainerHelper.makeTargetImage(FORMAT, BUILD_REPO, CONTAINER_ID1, null, null, null) - def req1 = new BuildRequest(CONTAINER_ID1, FOO_CONTENT, null, null, PATH, TARGET_IMAGE1, USER, PLATFORM, CACHE_REPO, "10.20.30.40", '{"config":"json"}', null, null, null, null, FORMAT, TIMEOUT) + def CONTAINER_ID1 = ContainerHelper.makeContainerId(FOO_CONTENT, null, PLATFORM, BUILD_REPO, null) + def TARGET_IMAGE1 = ContainerHelper.makeTargetImage(FORMAT, BUILD_REPO, CONTAINER_ID1, null, null) + def req1 = new BuildRequest(CONTAINER_ID1, FOO_CONTENT, null, PATH, TARGET_IMAGE1, USER, PLATFORM, CACHE_REPO, "10.20.30.40", '{"config":"json"}', null, null, null, null, FORMAT, TIMEOUT) and: - def req2 = new BuildRequest(CONTAINER_ID1, FOO_CONTENT, null, null, PATH, TARGET_IMAGE1, USER, PLATFORM, CACHE_REPO, "10.20.30.40", '{"config":"json"}', null, null, null, null, FORMAT, TIMEOUT) + def req2 = new BuildRequest(CONTAINER_ID1, FOO_CONTENT, null, PATH, TARGET_IMAGE1, USER, PLATFORM, CACHE_REPO, "10.20.30.40", '{"config":"json"}', null, null, null, null, FORMAT, TIMEOUT) and: - def CONTAINER_ID3 = ContainerHelper.makeContainerId(BAR_CONTENT, null, null, PLATFORM, BUILD_REPO, null) - def TARGET_IMAGE3 = ContainerHelper.makeTargetImage(FORMAT, BUILD_REPO, CONTAINER_ID3, null, null, null) - def req3 = new BuildRequest(CONTAINER_ID3, BAR_CONTENT, null, null, PATH, TARGET_IMAGE3, USER, PLATFORM, CACHE_REPO, "10.20.30.40", '{"config":"json"}', null, null, null, null, FORMAT, TIMEOUT) + def CONTAINER_ID3 = ContainerHelper.makeContainerId(BAR_CONTENT, null, PLATFORM, BUILD_REPO, null) + def TARGET_IMAGE3 = ContainerHelper.makeTargetImage(FORMAT, BUILD_REPO, CONTAINER_ID3, null, null) + def req3 = new BuildRequest(CONTAINER_ID3, BAR_CONTENT, null, PATH, TARGET_IMAGE3, USER, PLATFORM, CACHE_REPO, "10.20.30.40", '{"config":"json"}', null, null, null, null, FORMAT, TIMEOUT) and: - def CONTAINER_ID4 = ContainerHelper.makeContainerId(BAR_CONTENT, CONDA_CONTENT, null, PLATFORM, BUILD_REPO, null) - def TARGET_IMAGE4 = ContainerHelper.makeTargetImage(FORMAT, BUILD_REPO, CONTAINER_ID4, CONDA_CONTENT, null, null) - def req4 = new BuildRequest(CONTAINER_ID4, BAR_CONTENT, CONDA_CONTENT, null, PATH, TARGET_IMAGE4, USER, PLATFORM, CACHE_REPO, "10.20.30.40", '{"config":"json"}', null, null, null, null, FORMAT, TIMEOUT) + def CONTAINER_ID4 = ContainerHelper.makeContainerId(BAR_CONTENT, CONDA_CONTENT, PLATFORM, BUILD_REPO, null) + def TARGET_IMAGE4 = ContainerHelper.makeTargetImage(FORMAT, BUILD_REPO, CONTAINER_ID4, CONDA_CONTENT, null) + def req4 = new BuildRequest(CONTAINER_ID4, BAR_CONTENT, CONDA_CONTENT, PATH, TARGET_IMAGE4, USER, PLATFORM, CACHE_REPO, "10.20.30.40", '{"config":"json"}', null, null, null, null, FORMAT, TIMEOUT) and: - def req5 = new BuildRequest(CONTAINER_ID4, BAR_CONTENT, CONDA_CONTENT, null, PATH, TARGET_IMAGE4, USER, PLATFORM, CACHE_REPO, "10.20.30.40", '{"config":"json"}', null, null, null, null, FORMAT, TIMEOUT) + def req5 = new BuildRequest(CONTAINER_ID4, BAR_CONTENT, CONDA_CONTENT, PATH, TARGET_IMAGE4, USER, PLATFORM, CACHE_REPO, "10.20.30.40", '{"config":"json"}', null, null, null, null, FORMAT, TIMEOUT) and: CONDA_CONTENT = 'salmon=1.2.5' - def CONTAINER_ID6 = ContainerHelper.makeContainerId(BAR_CONTENT, CONDA_CONTENT, null, PLATFORM, BUILD_REPO, null) - def TARGET_IMAGE6 = ContainerHelper.makeTargetImage(FORMAT, BUILD_REPO, CONTAINER_ID6, CONDA_CONTENT, null, null) - def req6 = new BuildRequest(CONTAINER_ID4, BAR_CONTENT, CONDA_CONTENT, null, PATH, TARGET_IMAGE6, USER, PLATFORM, CACHE_REPO, "10.20.30.40", '{"config":"json"}', null, null, null, null, FORMAT, TIMEOUT) + def CONTAINER_ID6 = ContainerHelper.makeContainerId(BAR_CONTENT, CONDA_CONTENT, PLATFORM, BUILD_REPO, null) + def TARGET_IMAGE6 = ContainerHelper.makeTargetImage(FORMAT, BUILD_REPO, CONTAINER_ID6, CONDA_CONTENT, null) + def req6 = new BuildRequest(CONTAINER_ID4, BAR_CONTENT, CONDA_CONTENT, PATH, TARGET_IMAGE6, USER, PLATFORM, CACHE_REPO, "10.20.30.40", '{"config":"json"}', null, null, null, null, FORMAT, TIMEOUT) and: - def req7 = new BuildRequest(CONTAINER_ID4, BAR_CONTENT, CONDA_CONTENT, null, PATH, TARGET_IMAGE6, USER, PLATFORM, CACHE_REPO, "10.20.30.40", '{"config":"json"}', "UTC+2", null, null, null, FORMAT, TIMEOUT) + def req7 = new BuildRequest(CONTAINER_ID4, BAR_CONTENT, CONDA_CONTENT, PATH, TARGET_IMAGE6, USER, PLATFORM, CACHE_REPO, "10.20.30.40", '{"config":"json"}', "UTC+2", null, null, null, FORMAT, TIMEOUT) expect: req1 == req2 diff --git a/src/test/groovy/io/seqera/wave/service/builder/BuildStrategyTest.groovy b/src/test/groovy/io/seqera/wave/service/builder/BuildStrategyTest.groovy index 8151c836f..fbab4a86c 100644 --- a/src/test/groovy/io/seqera/wave/service/builder/BuildStrategyTest.groovy +++ b/src/test/groovy/io/seqera/wave/service/builder/BuildStrategyTest.groovy @@ -128,13 +128,12 @@ class BuildStrategyTest extends Specification { def content = 'FROM foo:latest' def workspace = Path.of("some/path") def buildrepo = 'foo.com/repo' - def containerId = ContainerHelper.makeContainerId(content, null, null, ContainerPlatform.of('amd64'), buildrepo, null) - def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildrepo, containerId, null, null, null) + def containerId = ContainerHelper.makeContainerId(content, null, ContainerPlatform.of('amd64'), buildrepo, null) + def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildrepo, containerId, null, null) def build = new BuildRequest( containerId, content, null, - null, workspace, targetImage, PlatformId.NULL, diff --git a/src/test/groovy/io/seqera/wave/service/builder/ContainerBuildServiceLiveTest.groovy b/src/test/groovy/io/seqera/wave/service/builder/ContainerBuildServiceLiveTest.groovy index eae4f8c42..359e93043 100644 --- a/src/test/groovy/io/seqera/wave/service/builder/ContainerBuildServiceLiveTest.groovy +++ b/src/test/groovy/io/seqera/wave/service/builder/ContainerBuildServiceLiveTest.groovy @@ -76,8 +76,8 @@ class ContainerBuildServiceLiveTest extends Specification { '''.stripIndent() and: def cfg = dockerAuthService.credentialsConfigJson(dockerFile, buildRepo, cacheRepo, Mock(PlatformId)) - def containerId = ContainerHelper.makeContainerId(dockerFile, null, null, ContainerPlatform.of('amd64'), buildRepo, null) - def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, null, null, null) + def containerId = ContainerHelper.makeContainerId(dockerFile, null, ContainerPlatform.of('amd64'), buildRepo, null) + def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, null, null) def req = new BuildRequest( containerId: containerId, @@ -122,8 +122,8 @@ class ContainerBuildServiceLiveTest extends Specification { '''.stripIndent() and: def cfg = dockerAuthService.credentialsConfigJson(dockerFile, buildRepo, null, Mock(PlatformId)) - def containerId = ContainerHelper.makeContainerId(dockerFile, null, null, ContainerPlatform.of('amd64'), buildRepo, null) - def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, null, null, null) + def containerId = ContainerHelper.makeContainerId(dockerFile, null, ContainerPlatform.of('amd64'), buildRepo, null) + def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, null, null) def req = new BuildRequest( containerId: containerId, @@ -168,8 +168,8 @@ class ContainerBuildServiceLiveTest extends Specification { and: def buildRepo = "quay.io/pditommaso/wave-tests" def cfg = dockerAuthService.credentialsConfigJson(dockerFile, buildRepo, null, Mock(PlatformId)) - def containerId = ContainerHelper.makeContainerId(dockerFile, null, null, ContainerPlatform.of('linux/arm64'), buildRepo, null) - def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, null, null, null) + def containerId = ContainerHelper.makeContainerId(dockerFile, null, ContainerPlatform.of('linux/arm64'), buildRepo, null) + def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, null, null) def req = new BuildRequest( containerId: containerId, @@ -214,8 +214,8 @@ class ContainerBuildServiceLiveTest extends Specification { and: def duration = Duration.ofMinutes(1) def cfg = dockerAuthService.credentialsConfigJson(dockerFile, buildRepo, null, Mock(PlatformId)) - def containerId = ContainerHelper.makeContainerId(dockerFile, null, null, ContainerPlatform.of('amd64'), buildRepo, null) - def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, null, null, null) + def containerId = ContainerHelper.makeContainerId(dockerFile, null, ContainerPlatform.of('amd64'), buildRepo, null) + def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, null, null) def req = new BuildRequest( containerId: containerId, @@ -266,8 +266,8 @@ class ContainerBuildServiceLiveTest extends Specification { and: def duration = Duration.ofMinutes(1) def cfg = dockerAuthService.credentialsConfigJson(dockerFile, buildRepo, null, Mock(PlatformId)) - def containerId = ContainerHelper.makeContainerId(dockerFile, null, null, ContainerPlatform.of('amd64'), buildRepo, null) - def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, null, null, null) + def containerId = ContainerHelper.makeContainerId(dockerFile, null, ContainerPlatform.of('amd64'), buildRepo, null) + def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, null, null) def req = new BuildRequest( containerId: containerId, diff --git a/src/test/groovy/io/seqera/wave/service/builder/ContainerBuildServiceTest.groovy b/src/test/groovy/io/seqera/wave/service/builder/ContainerBuildServiceTest.groovy index 7e0e6d647..21c05e66d 100644 --- a/src/test/groovy/io/seqera/wave/service/builder/ContainerBuildServiceTest.groovy +++ b/src/test/groovy/io/seqera/wave/service/builder/ContainerBuildServiceTest.groovy @@ -40,7 +40,6 @@ import io.seqera.wave.auth.RegistryCredentialsProvider import io.seqera.wave.auth.RegistryLookupService import io.seqera.wave.configuration.BuildConfig import io.seqera.wave.configuration.HttpClientConfig -import io.seqera.wave.configuration.SpackConfig import io.seqera.wave.core.ContainerPlatform import io.seqera.wave.core.RegistryProxyService import io.seqera.wave.service.builder.store.BuildRecordStore @@ -56,7 +55,6 @@ import io.seqera.wave.test.TestHelper import io.seqera.wave.tower.PlatformId import io.seqera.wave.util.ContainerHelper import io.seqera.wave.util.Packer -import io.seqera.wave.util.SpackHelper import io.seqera.wave.util.TemplateRenderer import jakarta.inject.Inject import jakarta.inject.Singleton @@ -110,11 +108,9 @@ class ContainerBuildServiceTest extends Specification { def buildRepo = buildConfig.defaultBuildRepository def cacheRepo = buildConfig.defaultCacheRepository and: - def cfg = 'some credentials' def dockerFile = ''' FROM busybox RUN echo Hello > hello.txt - RUN {{spack_cache_bucket}} {{spack_key_file}} '''.stripIndent() and: def condaFile = ''' @@ -122,22 +118,13 @@ class ContainerBuildServiceTest extends Specification { - salmon=1.6.0 ''' and: - def spackFile = ''' - spack: - specs: [bwa@0.7.15, salmon@1.1.1] - concretizer: {unify: true, reuse: true} - ''' - and: - def spackConfig = new SpackConfig(cacheBucket: 's3://bucket/cache', secretMountPath: '/mnt/secret') - def containerId = ContainerHelper.makeContainerId(dockerFile, condaFile, spackFile, ContainerPlatform.of('amd64'), buildRepo, null) - def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, condaFile, spackFile, null) + def containerId = ContainerHelper.makeContainerId(dockerFile, condaFile, ContainerPlatform.of('amd64'), buildRepo, null) + def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, condaFile, null) def req = new BuildRequest( containerId: containerId, containerFile: dockerFile, condaFile: condaFile, - spackFile: spackFile, - isSpackBuild: true, workspace: folder, targetImage: targetImage, identity: Mock(PlatformId), @@ -151,17 +138,17 @@ class ContainerBuildServiceTest extends Specification { and: def store = Mock(BuildStore) def jobService = Mock(JobService) - def builder = new ContainerBuildServiceImpl(buildStore: store, buildConfig: buildConfig, spackConfig:spackConfig, jobService: jobService) + def builder = new ContainerBuildServiceImpl(buildStore: store, buildConfig: buildConfig, jobService: jobService) def RESPONSE = Mock(JobSpec) - + when: builder.launch(req) + then: 1 * jobService.launchBuild(req) >> RESPONSE and: - req.workDir.resolve('Containerfile').text == new TemplateRenderer().render(dockerFile, [spack_cache_bucket:'s3://bucket/cache', spack_key_file:'/mnt/secret']) + req.workDir.resolve('Containerfile').text == new TemplateRenderer().render(dockerFile, [:]) req.workDir.resolve('context/conda.yml').text == condaFile - req.workDir.resolve('context/spack.yaml').text == spackFile cleanup: folder?.deleteDir() @@ -172,11 +159,10 @@ class ContainerBuildServiceTest extends Specification { def folder = Files.createTempDirectory('test') def builder = new ContainerBuildServiceImpl() def buildRepo = buildConfig.defaultBuildRepository - def cacheRepo = buildConfig.defaultCacheRepository and: def dockerFile = 'FROM something; {{foo}}' - def containerId = ContainerHelper.makeContainerId(dockerFile, null, null, ContainerPlatform.of('amd64'), buildRepo, null) - def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, null, null, null) + def containerId = ContainerHelper.makeContainerId(dockerFile, null, ContainerPlatform.of('amd64'), buildRepo, null) + def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, null, null) def req = new BuildRequest( containerId: containerId, @@ -189,112 +175,16 @@ class ContainerBuildServiceTest extends Specification { startTime: Instant.now() ) .withBuildId('1') - and: - def spack = Mock(SpackConfig) when: - def result = builder.containerFile0(req, null, spack) + def result = builder.containerFile0(req, null) then: - 0* spack.getCacheMountPath() >> null - 0* spack.getSecretMountPath() >> null - 0* spack.getBuilderImage() >> null - and: result == 'FROM something; {{foo}}' cleanup: folder?.deleteDir() } - def 'should resolve docker file with spack config' () { - given: - def folder = Files.createTempDirectory('test') - def builder = new ContainerBuildServiceImpl() - and: - def dockerFile = SpackHelper.builderDockerTemplate() - def spackFile = 'some spack packages' - def containerId = ContainerHelper.makeContainerId(dockerFile, null, spackFile, ContainerPlatform.of('amd64'), 'buildRepo', null) - def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, 'foo.com/repo', containerId, null, spackFile, null) - def req = new BuildRequest( - containerId: containerId, - containerFile: dockerFile, - spackFile: spackFile, - isSpackBuild: true, - workspace: folder, - targetImage: targetImage, - identity:Mock(PlatformId), - platform: ContainerPlatform.of('amd64'), - format: BuildFormat.DOCKER, - startTime: Instant.now() - ) - .withBuildId('1') - and: - def spack = Mock(SpackConfig) - - when: - def result = builder.containerFile0(req, null, spack) - then: - 1* spack.getCacheBucket() >> 's3://bucket/cache' - 1* spack.getSecretMountPath() >> '/mnt/key' - 1* spack.getBuilderImage() >> 'spack-builder:2.0' - 1* spack.getRunnerImage() >> 'ubuntu:22.04' - and: - result.contains('FROM spack-builder:2.0 as builder') - result.contains('spack config add packages:all:target:[x86_64]') - result.contains('spack mirror add seqera-spack s3://bucket/cache') - result.contains('fingerprint="$(spack gpg trust /mnt/key 2>&1 | tee /dev/stderr | sed -nr "s/^gpg: key ([0-9A-F]{16}): secret key imported$/\\1/p")"') - - cleanup: - folder?.deleteDir() - } - - def 'should resolve singularity file with spack config' () { - given: - def folder = Files.createTempDirectory('test') - def builder = new ContainerBuildServiceImpl() - and: - def context = Path.of('/some/context/dir') - def dockerFile = SpackHelper.builderSingularityTemplate() - def spackFile = 'some spack packages' - def containerId = ContainerHelper.makeContainerId(dockerFile, null, spackFile, ContainerPlatform.of('amd64'), 'buildRepo', null) - def targetImage = ContainerHelper.makeTargetImage(BuildFormat.SINGULARITY, 'foo.com/repo', containerId, null, spackFile, null) - def req = - new BuildRequest( - containerId: containerId, - containerFile: dockerFile, - spackFile: spackFile, - isSpackBuild: true, - workspace: folder, - targetImage: targetImage, - identity: Mock(PlatformId), - platform: ContainerPlatform.of('amd64'), - format: BuildFormat.SINGULARITY, - startTime: Instant.now() - ) - - .withBuildId('1') - and: - def spack = Mock(SpackConfig) - - when: - def result = builder.containerFile0(req, context, spack) - then: - 1* spack.getCacheBucket() >> 's3://bucket/cache' - 1* spack.getSecretMountPath() >> '/mnt/key' - 1* spack.getBuilderImage() >> 'spack-builder:2.0' - 1* spack.getRunnerImage() >> 'ubuntu:22.04' - and: - result.contains('Bootstrap: docker\n' + - 'From: spack-builder:2.0\n' + - 'Stage: build') - result.contains('spack config add packages:all:target:[x86_64]') - result.contains('spack mirror add seqera-spack s3://bucket/cache') - result.contains('fingerprint="$(spack gpg trust /mnt/key 2>&1 | tee /dev/stderr | sed -nr "s/^gpg: key ([0-9A-F]{16}): secret key imported$/\\1/p")"') - result.contains('/some/context/dir/spack.yaml /opt/spack-env/spack.yaml') - - cleanup: - folder?.deleteDir() - } - def 'should replace context path' () { given: def folder = Path.of('/some/work/dir') @@ -306,8 +196,8 @@ class ContainerBuildServiceTest extends Specification { '''.stripIndent() and: def builder = new ContainerBuildServiceImpl() - def containerId = ContainerHelper.makeContainerId(containerFile, null, null, ContainerPlatform.of('amd64'), 'buildRepo', null) - def targetImage = ContainerHelper.makeTargetImage(BuildFormat.SINGULARITY, 'foo.com/repo', containerId, null, null, null) + def containerId = ContainerHelper.makeContainerId(containerFile, null, ContainerPlatform.of('amd64'), 'buildRepo', null) + def targetImage = ContainerHelper.makeTargetImage(BuildFormat.SINGULARITY, 'foo.com/repo', containerId, null, null) def req = new BuildRequest( containerId: containerId, @@ -322,7 +212,7 @@ class ContainerBuildServiceTest extends Specification { .withBuildId('1') when: - def result = builder.containerFile0(req, Path.of('/some/context/'), null) + def result = builder.containerFile0(req, Path.of('/some/context/')) then: result == '''\ BootStrap: docker @@ -386,8 +276,8 @@ class ContainerBuildServiceTest extends Specification { and: def dockerFile = 'from foo' def buildRepo = 'quay.io/org/name' - def containerId = ContainerHelper.makeContainerId(dockerFile, null, null, ContainerPlatform.of('amd64'), buildRepo, null) - def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, null, null, null) + def containerId = ContainerHelper.makeContainerId(dockerFile, null, ContainerPlatform.of('amd64'), buildRepo, null) + def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, null, null) def req = new BuildRequest( containerId: containerId, @@ -420,7 +310,6 @@ class ContainerBuildServiceTest extends Specification { containerId: 'container1234', containerFile: 'test', condaFile: 'test', - spackFile: 'test', workspace: Path.of("."), targetImage: 'docker.io/my/repo:container1234', identity: PlatformId.NULL, @@ -452,7 +341,6 @@ class ContainerBuildServiceTest extends Specification { containerId: 'container1234', containerFile:'test', condaFile: 'test', - spackFile: 'test', workspace: Path.of("."), targetImage: 'docker.io/my/repo:container1234', identity: PlatformId.NULL, @@ -488,7 +376,6 @@ class ContainerBuildServiceTest extends Specification { containerId: 'container1234', containerFile: 'test', condaFile: 'test', - spackFile: 'test', workspace: Path.of("."), targetImage: 'docker.io/my/repo:container1234', identity: PlatformId.NULL, diff --git a/src/test/groovy/io/seqera/wave/service/builder/DockerBuildStrategyTest.groovy b/src/test/groovy/io/seqera/wave/service/builder/DockerBuildStrategyTest.groovy index 3294f88a0..52c4c1f8c 100644 --- a/src/test/groovy/io/seqera/wave/service/builder/DockerBuildStrategyTest.groovy +++ b/src/test/groovy/io/seqera/wave/service/builder/DockerBuildStrategyTest.groovy @@ -24,7 +24,6 @@ import java.nio.file.Path import io.micronaut.context.ApplicationContext import io.micronaut.test.extensions.spock.annotation.MicronautTest -import io.seqera.wave.configuration.SpackConfig import io.seqera.wave.core.ContainerPlatform /** * @@ -35,17 +34,13 @@ class DockerBuildStrategyTest extends Specification { def 'should get docker command' () { given: - def props = [ - 'wave.build.spack.secretKeyFile':'/host/spack/key', - 'wave.build.spack.secretMountPath':'/opt/spack/key' ] - def ctx = ApplicationContext.run(props) + def ctx = ApplicationContext.run() and: def service = ctx.getBean(DockerBuildStrategy) - def spackConfig = ctx.getBean(SpackConfig) and: def work = Path.of('/work/foo') when: - def cmd = service.cmdForBuildkit('build-job-name', work, null, null, null) + def cmd = service.cmdForBuildkit('build-job-name', work, null, null) then: cmd == ['docker', 'run', @@ -59,7 +54,7 @@ class DockerBuildStrategyTest extends Specification { 'moby/buildkit:v0.14.1-rootless'] when: - cmd = service.cmdForBuildkit('build-job-name', work, Path.of('/foo/creds.json'), null, ContainerPlatform.of('arm64')) + cmd = service.cmdForBuildkit('build-job-name', work, Path.of('/foo/creds.json'), ContainerPlatform.of('arm64')) then: cmd == ['docker', 'run', @@ -75,7 +70,7 @@ class DockerBuildStrategyTest extends Specification { 'moby/buildkit:v0.14.1-rootless'] when: - cmd = service.cmdForBuildkit('build-job-name', work, Path.of('/foo/creds.json'), spackConfig, null) + cmd = service.cmdForBuildkit('build-job-name', work, Path.of('/foo/creds.json'), null) then: cmd == ['docker', 'run', @@ -87,7 +82,6 @@ class DockerBuildStrategyTest extends Specification { '--entrypoint', 'buildctl-daemonless.sh', '-v', '/foo/creds.json:/home/user/.docker/config.json:ro', - '-v', '/host/spack/key:/opt/spack/key:ro', 'moby/buildkit:v0.14.1-rootless'] cleanup: @@ -146,13 +140,8 @@ class DockerBuildStrategyTest extends Specification { def 'should get singularity build command' () { given: - def props = [ - 'wave.build.spack.secretKeyFile':'/host/spack/key', - 'wave.build.spack.secretMountPath':'/opt/spack/key' ] - def ctx = ApplicationContext.run(props) + def ctx = ApplicationContext.run() def service = ctx.getBean(DockerBuildStrategy) - SpackConfig spackConfig = ctx.getBean(SpackConfig) - service.setSpackConfig(spackConfig) and: def creds = Path.of('/work/creds.json') and: @@ -161,8 +150,7 @@ class DockerBuildStrategyTest extends Specification { platform: ContainerPlatform.of('linux/amd64'), targetImage: 'oras://repo:d4869cc39b8d7d55', cacheRepository: 'reg.io/wave/build/cache', - format: BuildFormat.SINGULARITY, - isSpackBuild: true ) + format: BuildFormat.SINGULARITY ) when: def cmd = service.buildCmd('build-job-name', req, creds) then: @@ -176,27 +164,21 @@ class DockerBuildStrategyTest extends Specification { '-v', '/work/foo/d4869cc39b8d7d55:/work/foo/d4869cc39b8d7d55', '-v', '/work/creds.json:/root/.singularity/docker-config.json:ro', '-v', '/work/singularity-remote.yaml:/root/.singularity/remote.yaml:ro', - '-v', '/host/spack/key:/opt/spack/key:ro', '--platform', 'linux/amd64', 'quay.io/singularity/singularity:v3.11.4-slim', 'sh', '-c', 'singularity build image.sif /work/foo/d4869cc39b8d7d55/Containerfile && singularity push image.sif oras://repo:d4869cc39b8d7d55' ] - + cleanup: ctx.close() } def 'should get singularity build command for arm64 architecture' () { given: - def props = [ - 'wave.build.spack.secretKeyFile':'/host/spack/key', - 'wave.build.spack.secretMountPath':'/opt/spack/key' ] - def ctx = ApplicationContext.run(props) + def ctx = ApplicationContext.run() def service = ctx.getBean(DockerBuildStrategy) - SpackConfig spackConfig = ctx.getBean(SpackConfig) - service.setSpackConfig(spackConfig) and: def creds = Path.of('/work/creds.json') and: @@ -205,8 +187,7 @@ class DockerBuildStrategyTest extends Specification { platform: ContainerPlatform.of('linux/arm64'), targetImage: 'oras://repo:9c68af894bb2419c', cacheRepository: 'reg.io/wave/build/cache', - format: BuildFormat.SINGULARITY, - isSpackBuild: true ) + format: BuildFormat.SINGULARITY ) when: def cmd = service.buildCmd('build-job-name', req, creds) then: @@ -220,7 +201,6 @@ class DockerBuildStrategyTest extends Specification { '-v', '/work/foo/9c68af894bb2419c:/work/foo/9c68af894bb2419c', '-v', '/work/creds.json:/root/.singularity/docker-config.json:ro', '-v', '/work/singularity-remote.yaml:/root/.singularity/remote.yaml:ro', - '-v', '/host/spack/key:/opt/spack/key:ro', '--platform', 'linux/arm64', 'quay.io/singularity/singularity:v3.11.4-slim-arm64', 'sh', diff --git a/src/test/groovy/io/seqera/wave/service/builder/FutureContainerBuildServiceTest.groovy b/src/test/groovy/io/seqera/wave/service/builder/FutureContainerBuildServiceTest.groovy index 957a32146..0904a7988 100644 --- a/src/test/groovy/io/seqera/wave/service/builder/FutureContainerBuildServiceTest.groovy +++ b/src/test/groovy/io/seqera/wave/service/builder/FutureContainerBuildServiceTest.groovy @@ -46,9 +46,9 @@ class FutureContainerBuildServiceTest extends Specification { RUN echo 'hello' > hello.txt """.stripIndent() and: - def containerId = ContainerHelper.makeContainerId(dockerfile, null, null, ContainerPlatform.of('amd64'), buildRepo, null) - def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, null, null, null) - def req = new BuildRequest(containerId, dockerfile, null, null, folder, targetImage, Mock(PlatformId), ContainerPlatform.of('amd64'), cacheRepo, "10.20.30.40", '{"config":"json"}', null,null , null, null, BuildFormat.DOCKER, Duration.ofMinutes(1)).withBuildId('1') + def containerId = ContainerHelper.makeContainerId(dockerfile, null, ContainerPlatform.of('amd64'), buildRepo, null) + def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, null, null) + def req = new BuildRequest(containerId, dockerfile, null, folder, targetImage, Mock(PlatformId), ContainerPlatform.of('amd64'), cacheRepo, "10.20.30.40", '{"config":"json"}', null,null , null, null, BuildFormat.DOCKER, Duration.ofMinutes(1)).withBuildId('1') def res = new BuildResult("", 0, "a fake build result in a test", Instant.now(), Duration.ofSeconds(3), 'abc') and: def buildStore = Mock(BuildStore) @@ -81,9 +81,9 @@ class FutureContainerBuildServiceTest extends Specification { RUN echo 'hello' > hello.txt """.stripIndent() and: - def containerId = ContainerHelper.makeContainerId(dockerfile, null, null, ContainerPlatform.of('amd64'), buildRepo, null) - def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, null, null, null) - def req = new BuildRequest(containerId, dockerfile, null, null, folder, targetImage, Mock(PlatformId), ContainerPlatform.of('amd64'), cacheRepo, "10.20.30.40", '{"config":"json"}', null,null , null, null, BuildFormat.DOCKER, Duration.ofMinutes(1)).withBuildId('1') + def containerId = ContainerHelper.makeContainerId(dockerfile, null, ContainerPlatform.of('amd64'), buildRepo, null) + def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, buildRepo, containerId, null, null) + def req = new BuildRequest(containerId, dockerfile, null, folder, targetImage, Mock(PlatformId), ContainerPlatform.of('amd64'), cacheRepo, "10.20.30.40", '{"config":"json"}', null,null , null, null, BuildFormat.DOCKER, Duration.ofMinutes(1)).withBuildId('1') def res = new BuildResult("", 1, "a fake build result in a test", Instant.now(), Duration.ofSeconds(3), 'abc') and: def buildStore = Mock(BuildStore) diff --git a/src/test/groovy/io/seqera/wave/service/builder/KubeBuildStrategyTest.groovy b/src/test/groovy/io/seqera/wave/service/builder/KubeBuildStrategyTest.groovy index 1b8ea51de..5e79c4824 100644 --- a/src/test/groovy/io/seqera/wave/service/builder/KubeBuildStrategyTest.groovy +++ b/src/test/groovy/io/seqera/wave/service/builder/KubeBuildStrategyTest.groovy @@ -68,22 +68,22 @@ class KubeBuildStrategyTest extends Specification { def dockerfile = 'from foo' when: - def containerId = ContainerHelper.makeContainerId(dockerfile, null, null, ContainerPlatform.of('amd64'), repo, null) - def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, repo, containerId, null, null, null) - def req = new BuildRequest(containerId, dockerfile, null, null, PATH, targetImage, USER, ContainerPlatform.of('amd64'), cache, "10.20.30.40", '{}', null,null , null, null, BuildFormat.DOCKER, Duration.ofMinutes(1)).withBuildId('1') + def containerId = ContainerHelper.makeContainerId(dockerfile, null, ContainerPlatform.of('amd64'), repo, null) + def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, repo, containerId, null, null) + def req = new BuildRequest(containerId, dockerfile, null, PATH, targetImage, USER, ContainerPlatform.of('amd64'), cache, "10.20.30.40", '{}', null,null , null, null, BuildFormat.DOCKER, Duration.ofMinutes(1)).withBuildId('1') Files.createDirectories(req.workDir) strategy.build('build-job-name', req) then: - 1 * k8sService.launchBuildJob(_, _, _, _, _, _, _, [service:'wave-build']) >> null + 1 * k8sService.launchBuildJob( _, _, _, _, _, _, [service:'wave-build']) >> null when: - def req2 = new BuildRequest(containerId, dockerfile, null, null, PATH, targetImage, USER, ContainerPlatform.of('arm64'), cache, "10.20.30.40", '{}', null,null , null, null, BuildFormat.DOCKER, Duration.ofMinutes(1)).withBuildId('1') + def req2 = new BuildRequest(containerId, dockerfile, null, PATH, targetImage, USER, ContainerPlatform.of('arm64'), cache, "10.20.30.40", '{}', null,null , null, null, BuildFormat.DOCKER, Duration.ofMinutes(1)).withBuildId('1') Files.createDirectories(req2.workDir) strategy.build('job-name', req2) then: - 1 * k8sService.launchBuildJob(_, _, _, _, _, _, _, [service:'wave-build-arm64']) >> null + 1 * k8sService.launchBuildJob( _, _, _, _, _, _, [service:'wave-build-arm64']) >> null } @@ -96,21 +96,21 @@ class KubeBuildStrategyTest extends Specification { def dockerfile = 'from foo' when:'getting docker with amd64 arch in build request' - def containerId = ContainerHelper.makeContainerId(dockerfile, null, null, ContainerPlatform.of('amd64'), repo, null) - def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, repo, containerId, null, null, null) - def req = new BuildRequest(containerId, dockerfile, null, null, PATH, targetImage, USER, ContainerPlatform.of('amd64'), cache, "10.20.30.40", '{"config":"json"}', null,null , null, null, BuildFormat.DOCKER, Duration.ofMinutes(1)).withBuildId('1') + def containerId = ContainerHelper.makeContainerId(dockerfile, null, ContainerPlatform.of('amd64'), repo, null) + def targetImage = ContainerHelper.makeTargetImage(BuildFormat.DOCKER, repo, containerId, null, null) + def req = new BuildRequest(containerId, dockerfile, null, PATH, targetImage, USER, ContainerPlatform.of('amd64'), cache, "10.20.30.40", '{"config":"json"}', null,null , null, null, BuildFormat.DOCKER, Duration.ofMinutes(1)).withBuildId('1') then: 'should return buildkit image' strategy.getBuildImage(req) == 'moby/buildkit:v0.14.1-rootless' when:'getting singularity with amd64 arch in build request' - req = new BuildRequest(containerId, dockerfile, null, null, PATH, targetImage, USER, ContainerPlatform.of('amd64'), cache, "10.20.30.40", '{}', null,null , null, null, BuildFormat.SINGULARITY,Duration.ofMinutes(1)).withBuildId('1') + req = new BuildRequest(containerId, dockerfile, null, PATH, targetImage, USER, ContainerPlatform.of('amd64'), cache, "10.20.30.40", '{}', null,null , null, null, BuildFormat.SINGULARITY,Duration.ofMinutes(1)).withBuildId('1') then:'should return singularity amd64 image' strategy.getBuildImage(req) == 'quay.io/singularity/singularity:v3.11.4-slim' when:'getting singularity with arm64 arch in build request' - req = new BuildRequest(containerId, dockerfile, null, null, PATH, targetImage, USER, ContainerPlatform.of('arm64'), cache, "10.20.30.40", '{}', null,null , null, null, BuildFormat.SINGULARITY, Duration.ofMinutes(1)).withBuildId('1') + req = new BuildRequest(containerId, dockerfile, null, PATH, targetImage, USER, ContainerPlatform.of('arm64'), cache, "10.20.30.40", '{}', null,null , null, null, BuildFormat.SINGULARITY, Duration.ofMinutes(1)).withBuildId('1') then:'should return singularity arm64 image' strategy.getBuildImage(req) == 'quay.io/singularity/singularity:v3.11.4-slim-arm64' diff --git a/src/test/groovy/io/seqera/wave/service/cache/impl/LocalCacheProviderTest.groovy b/src/test/groovy/io/seqera/wave/service/cache/impl/LocalCacheProviderTest.groovy index 5c2a14956..d1eaee375 100644 --- a/src/test/groovy/io/seqera/wave/service/cache/impl/LocalCacheProviderTest.groovy +++ b/src/test/groovy/io/seqera/wave/service/cache/impl/LocalCacheProviderTest.groovy @@ -80,45 +80,4 @@ class LocalCacheProviderTest extends Specification { localCacheProvider.get('key') == 'new-value' } - def 'should add and find keys for values' () { - when: - localCacheProvider.biPut('x1', 'a', Duration.ofMinutes(1)) - localCacheProvider.biPut('x2', 'b', Duration.ofMinutes(1)) - localCacheProvider.biPut('x3', 'a', Duration.ofMinutes(1)) - localCacheProvider.biPut('x4', 'c', Duration.ofMinutes(1)) - - then: - localCacheProvider.biKeysFor('a') == ['x1', 'x3'] as Set - localCacheProvider.biKeysFor('c') == ['x4'] as Set - localCacheProvider.biKeysFor('d') == [] as Set - - when: - localCacheProvider.biRemove('x1') - then: - localCacheProvider.biKeysFor('a') == ['x3'] as Set - - when: - localCacheProvider.biRemove('x3') - then: - localCacheProvider.biKeysFor('a') == [] as Set - } - - def 'should add and find keys for values' () { - when: - localCacheProvider.biPut('x1', 'a', Duration.ofMillis(100)) - localCacheProvider.biPut('x2', 'b', Duration.ofMinutes(1)) - localCacheProvider.biPut('x3', 'a', Duration.ofMinutes(1)) - localCacheProvider.biPut('x4', 'c', Duration.ofMinutes(1)) - - then: - localCacheProvider.biKeyFind('a', true) == 'x1' - and: - localCacheProvider.biKeysFor('a') == ['x1','x3'] as Set - and: - sleep 500 - and: - localCacheProvider.biKeyFind('a', true) == 'x3' - localCacheProvider.biKeysFor('a') == ['x3'] as Set - - } } diff --git a/src/test/groovy/io/seqera/wave/service/cache/impl/RedisCacheProviderTest.groovy b/src/test/groovy/io/seqera/wave/service/cache/impl/RedisCacheProviderTest.groovy index 79c679b7e..6f548d7f4 100644 --- a/src/test/groovy/io/seqera/wave/service/cache/impl/RedisCacheProviderTest.groovy +++ b/src/test/groovy/io/seqera/wave/service/cache/impl/RedisCacheProviderTest.groovy @@ -89,70 +89,4 @@ class RedisCacheProviderTest extends Specification implements RedisTestContainer redisCacheProvider.get('key') == 'new-value' } - def 'should add and find keys for values' () { - when: - redisCacheProvider.biPut('x1', 'a', Duration.ofMinutes(1)) - redisCacheProvider.biPut('x2', 'b', Duration.ofMinutes(1)) - redisCacheProvider.biPut('x3', 'a', Duration.ofMinutes(1)) - redisCacheProvider.biPut('x4', 'c', Duration.ofMinutes(1)) - - then: - redisCacheProvider.biKeysFor('a') == ['x1', 'x3'] as Set - redisCacheProvider.biKeysFor('c') == ['x4'] as Set - redisCacheProvider.biKeysFor('d') == [] as Set - - when: - redisCacheProvider.biRemove('x1') - then: - redisCacheProvider.biKeysFor('a') == ['x3'] as Set - - when: - redisCacheProvider.biRemove('x3') - then: - redisCacheProvider.biKeysFor('a') == [] as Set - - cleanup: - redisCacheProvider.clear() - } - - def 'should add and find single key for value' () { - when: - redisCacheProvider.biPut('x1', 'a', Duration.ofSeconds(1)) - redisCacheProvider.biPut('x2', 'b', Duration.ofMinutes(1)) - redisCacheProvider.biPut('x3', 'a', Duration.ofMinutes(1)) - redisCacheProvider.biPut('x4', 'c', Duration.ofMinutes(1)) - - then: - redisCacheProvider.biKeyFind('a', true) == 'x1' - and: - redisCacheProvider.biKeysFor('a') == ['x1','x3'] as Set - and: - sleep 1500 - and: - redisCacheProvider.biKeyFind('a', true) == 'x3' - redisCacheProvider.biKeysFor('a') == ['x3'] as Set - - cleanup: - redisCacheProvider.clear() - } - - def 'should update expiration when re-putting the value' () { - when: - redisCacheProvider.biPut('x1', 'a', Duration.ofSeconds(1)) - then: - redisCacheProvider.biKeyFind('a', true) == 'x1' - - when: - sleep 500 - redisCacheProvider.biPut('x1', 'a', Duration.ofSeconds(1)) - sleep 500 - redisCacheProvider.biPut('x1', 'a', Duration.ofSeconds(1)) - sleep 500 - then: - redisCacheProvider.biKeyFind('a', true) == 'x1' - - cleanup: - redisCacheProvider.clear() - } - } diff --git a/src/test/groovy/io/seqera/wave/service/k8s/K8sClientTest.groovy b/src/test/groovy/io/seqera/wave/service/k8s/K8sClientTest.groovy deleted file mode 100644 index 27ef124ac..000000000 --- a/src/test/groovy/io/seqera/wave/service/k8s/K8sClientTest.groovy +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Wave, containers provisioning service - * Copyright (c) 2023-2024, Seqera Labs - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package io.seqera.wave.service.k8s - -import spock.lang.Ignore -import spock.lang.Specification - -import java.nio.file.Path - -import io.micronaut.test.extensions.spock.annotation.MicronautTest -import jakarta.inject.Inject -/** - * - * @author Paolo Di Tommaso - */ -@Ignore -@MicronautTest -class K8sClientTest extends Specification { - - @Inject K8sService k8sService - - def 'should create job' () { - when: - def job = k8sService.createJob('foo-2', 'busybox', ['sh', '-c', 'slep 10']) - println job - then: - job - } - - def 'should get job' () { - when: - def job = k8sService.getJob('foo-2') - job.status.succeeded == 1 - then: - true - } - - def 'should create pod' () { - when: - def pod = k8sService.buildContainer( - 'my-pod', - 'busybox', - ['cat','/home/user/.docker/config.json'], - Path.of('/work/dir'), - Path.of('/creds'), - Path.of('/spack/dir'), - ['my-creds': 'selector']) - then: - true - - when: - def str = k8sService.logsPod('my-pod') - then: - str - - } -} diff --git a/src/test/groovy/io/seqera/wave/service/k8s/K8sServiceImplTest.groovy b/src/test/groovy/io/seqera/wave/service/k8s/K8sServiceImplTest.groovy index 3f85ebb63..b9c50d94f 100644 --- a/src/test/groovy/io/seqera/wave/service/k8s/K8sServiceImplTest.groovy +++ b/src/test/groovy/io/seqera/wave/service/k8s/K8sServiceImplTest.groovy @@ -42,7 +42,6 @@ import io.micronaut.context.annotation.Replaces import io.micronaut.test.extensions.spock.annotation.MicronautTest import io.seqera.wave.configuration.BlobCacheConfig import io.seqera.wave.configuration.ScanConfig -import io.seqera.wave.configuration.SpackConfig /** * * @author Paolo Di Tommaso @@ -188,30 +187,6 @@ class K8sServiceImplTest extends Specification { ctx.close() } - def 'should get spack dir vol' () { - given: - def PROPS = [ - 'wave.build.workspace': '/build/work', - 'wave.build.k8s.namespace': 'foo', - 'wave.build.k8s.configPath': '/home/kube.config', - 'wave.build.k8s.storage.claimName': 'bar', - 'wave.build.k8s.storage.mountPath': '/build' ] - and: - def ctx = ApplicationContext.run(PROPS) - def k8sService = ctx.getBean(K8sServiceImpl) - - when: - def mount = k8sService.mountSpackCacheDir(Path.of('/foo/work/x1'), '/foo', '/opt/spack/cache') - then: - mount.name == 'build-data' - mount.mountPath == '/opt/spack/cache' - mount.subPath == 'work/x1' - !mount.readOnly - - cleanup: - ctx.close() - } - def 'should create build pod for buildkit' () { given: def PROPS = [ @@ -225,7 +200,7 @@ class K8sServiceImplTest extends Specification { def k8sService = ctx.getBean(K8sServiceImpl) when: - def result = k8sService.buildSpec('foo', 'my-image:latest', ['this', 'that'], Path.of('/build/work/xyz'), Path.of('/build/work/xyz/config.json'), Duration.ofSeconds(10), null, [:]) + def result = k8sService.buildSpec('foo', 'my-image:latest', ['this', 'that'], Path.of('/build/work/xyz'), Path.of('/build/work/xyz/config.json'), Duration.ofSeconds(10), [:]) then: result.metadata.name == 'foo' result.metadata.namespace == 'my-ns' @@ -268,7 +243,7 @@ class K8sServiceImplTest extends Specification { def k8sService = ctx.getBean(K8sServiceImpl) def workDir = Path.of('/build/work/xyz') when: - def result = k8sService.buildSpec('foo', 'singularity:latest', ['this','that'], workDir, workDir.resolve('config.json'), Duration.ofSeconds(10), null, [:]) + def result = k8sService.buildSpec('foo', 'singularity:latest', ['this','that'], workDir, workDir.resolve('config.json'), Duration.ofSeconds(10), [:]) then: result.metadata.name == 'foo' result.metadata.namespace == 'my-ns' @@ -302,52 +277,6 @@ class K8sServiceImplTest extends Specification { ctx.close() } - def 'should create build pod with spack cache' () { - given: - def PROPS = [ - 'wave.build.workspace': '/build/work', - 'wave.build.k8s.namespace': 'my-ns', - 'wave.build.k8s.configPath': '/home/kube.config', - 'wave.build.k8s.storage.claimName': 'build-claim', - 'wave.build.k8s.storage.mountPath': '/build', - 'wave.build.spack.secretKeyFile':'/build/host/spack/key', - 'wave.build.spack.secretMountPath':'/opt/container/spack/key' - ] - and: - def ctx = ApplicationContext.run(PROPS) - def k8sService = ctx.getBean(K8sServiceImpl) - def spackConfig = ctx.getBean(SpackConfig) - when: - def result = k8sService.buildSpec('foo', 'my-image:latest', ['this','that'], Path.of('/build/work/xyz'), null,Duration.ofSeconds(10), spackConfig, [:]) - then: - result.metadata.name == 'foo' - result.metadata.namespace == 'my-ns' - and: - result.spec.activeDeadlineSeconds == 10 - and: - verifyAll(result.spec.containers.get(0)) { - name == 'foo' - image == 'my-image:latest' - args == ['this', 'that'] - env.name == ['BUILDKITD_FLAGS'] - env.value == ['--oci-worker-no-process-sandbox'] - volumeMounts.size() == 2 - volumeMounts.get(0).name == 'build-data' - volumeMounts.get(0).mountPath == '/build/work/xyz' - volumeMounts.get(0).subPath == 'work/xyz' - volumeMounts.get(1).name == 'build-data' - volumeMounts.get(1).mountPath == '/opt/container/spack/key' - volumeMounts.get(1).subPath == 'host/spack/key' - volumeMounts.get(1).readOnly - } - and: - result.spec.volumes.get(0).name == 'build-data' - result.spec.volumes.get(0).persistentVolumeClaim.claimName == 'build-claim' - - cleanup: - ctx.close() - } - def 'should create build pod without init container' () { given: def PROPS = [ @@ -361,7 +290,8 @@ class K8sServiceImplTest extends Specification { def k8sService = ctx.getBean(K8sServiceImpl) when: - def result = k8sService.buildSpec('foo', 'my-image:latest', ['this','that'], Path.of('/build/work/xyz'), null, Duration.ofSeconds(10), null,[:]) + def result = k8sService.buildSpec('foo', 'my-image:latest', ['this','that'], Path.of('/build/work/xyz'), null, Duration.ofSeconds(10), [:]) + then: result.metadata.name == 'foo' result.metadata.namespace == 'my-ns' @@ -405,7 +335,8 @@ class K8sServiceImplTest extends Specification { def k8sService = ctx.getBean(K8sServiceImpl) when: - def result = k8sService.buildSpec('foo', 'my-image:latest', ['this','that'], Path.of('/build/work/xyz'), null, Duration.ofSeconds(10), null,[:]) + def result = k8sService.buildSpec('foo', 'my-image:latest', ['this','that'], Path.of('/build/work/xyz'), null, Duration.ofSeconds(10), [:]) + then: result.metadata.name == 'foo' result.metadata.labels.toString() == PROPS['wave.build.k8s.labels'].toString() @@ -433,7 +364,8 @@ class K8sServiceImplTest extends Specification { def k8sService = ctx.getBean(K8sServiceImpl) when: - def result = k8sService.buildSpec('foo', 'my-image:latest', ['this','that'], Path.of('/build/work/xyz'), null, Duration.ofSeconds(10), null, PROPS['wave.build.k8s.node-selector'] as Map) + def result = k8sService.buildSpec('foo', 'my-image:latest', ['this','that'], Path.of('/build/work/xyz'), null, Duration.ofSeconds(10), PROPS['wave.build.k8s.node-selector'] as Map) + then: result.spec.nodeSelector.toString() == PROPS['wave.build.k8s.node-selector'].toString() and: @@ -458,7 +390,8 @@ class K8sServiceImplTest extends Specification { def k8sService = ctx.getBean(K8sServiceImpl) when: - def result = k8sService.buildSpec('foo', 'my-image:latest', ['this','that'], Path.of('/build/work/xyz'), null, Duration.ofSeconds(10), null,[:]) + def result = k8sService.buildSpec('foo', 'my-image:latest', ['this','that'], Path.of('/build/work/xyz'), null, Duration.ofSeconds(10), [:]) + then: result.spec.serviceAccount == PROPS['wave.build.k8s.service-account'] and: @@ -712,11 +645,10 @@ class K8sServiceImplTest extends Specification { def workDir = Path.of('/work/dir') def credsFile = Path.of('/creds/file') def timeout = Duration.ofMinutes(10) - def spackConfig = new SpackConfig(secretKeyFile: Path.of('/build/secret/key'), secretMountPath: '/secret/mount') def nodeSelector = [key: 'value'] when: - def job = k8sService.buildJobSpec(name, containerImage, args, workDir, credsFile, timeout, spackConfig, nodeSelector) + def job = k8sService.buildJobSpec(name, containerImage, args, workDir, credsFile, timeout, nodeSelector) then: job.spec.backoffLimit == 3 @@ -747,11 +679,10 @@ class K8sServiceImplTest extends Specification { def workDir = Path.of('/work/dir') def credsFile = Path.of('/creds/file') def timeout = Duration.ofMinutes(10) - def spackConfig = new SpackConfig(secretKeyFile: Path.of('/build/secret/key'), secretMountPath: '/secret/mount') def nodeSelector = [key: 'value'] when: - def job = k8sService.buildJobSpec(name, containerImage, args, workDir, credsFile, timeout, spackConfig, nodeSelector) + def job = k8sService.buildJobSpec(name, containerImage, args, workDir, credsFile, timeout, nodeSelector) then: job.spec.template.spec.containers[0].image == containerImage diff --git a/src/test/groovy/io/seqera/wave/service/mail/MailServiceImplTest.groovy b/src/test/groovy/io/seqera/wave/service/mail/MailServiceImplTest.groovy index e29034537..bd082d6fe 100644 --- a/src/test/groovy/io/seqera/wave/service/mail/MailServiceImplTest.groovy +++ b/src/test/groovy/io/seqera/wave/service/mail/MailServiceImplTest.groovy @@ -50,13 +50,11 @@ class MailServiceImplTest extends Specification { 1* request.getTargetImage() >> 'wave/build:xyz' 1* request.getPlatform() >> ContainerPlatform.DEFAULT 1* request.getCondaFile() >> null - 1* request.getSpackFile() >> null and: mail.to == recipient mail.body.contains('from foo') and: !mail.body.contains('Conda file') - !mail.body.contains('Spack file') // check it adds the Conda file content when: @@ -70,17 +68,6 @@ class MailServiceImplTest extends Specification { mail.body.contains('Conda file') mail.body.contains('bioconda::foo') - // check it add the spack file content - when: - mail = service.buildCompletionMail(request, result, recipient) - then: - 1* request.getTargetImage() >> 'wave/build:xyz' - 1* request.getPlatform() >> ContainerPlatform.DEFAULT - 1* request.getSpackFile() >> 'some-spac-recipe' - and: - mail.to == recipient - mail.body.contains('Spack file') - mail.body.contains('some-spac-recipe') } } diff --git a/src/test/groovy/io/seqera/wave/service/persistence/WaveBuildRecordTest.groovy b/src/test/groovy/io/seqera/wave/service/persistence/WaveBuildRecordTest.groovy index fa9d5cf36..75ebd6c8c 100644 --- a/src/test/groovy/io/seqera/wave/service/persistence/WaveBuildRecordTest.groovy +++ b/src/test/groovy/io/seqera/wave/service/persistence/WaveBuildRecordTest.groovy @@ -45,7 +45,6 @@ class WaveBuildRecordTest extends Specification { 'container1234', 'FROM foo:latest', 'conda::recipe', - 'some-spack-recipe', Path.of("/some/path"), 'docker.io/my/repo:container1234', PlatformId.NULL, @@ -77,7 +76,6 @@ class WaveBuildRecordTest extends Specification { 'container1234', 'FROM foo:latest', 'conda::recipe', - 'some-spack-recipe', Path.of("/some/path"), 'docker.io/my/repo:container1234', PlatformId.NULL, diff --git a/src/test/groovy/io/seqera/wave/service/persistence/impl/SurrealPersistenceServiceTest.groovy b/src/test/groovy/io/seqera/wave/service/persistence/impl/SurrealPersistenceServiceTest.groovy index b21215e5f..c9ff7749e 100644 --- a/src/test/groovy/io/seqera/wave/service/persistence/impl/SurrealPersistenceServiceTest.groovy +++ b/src/test/groovy/io/seqera/wave/service/persistence/impl/SurrealPersistenceServiceTest.groovy @@ -104,7 +104,6 @@ class SurrealPersistenceServiceTest extends Specification implements SurrealDBTe 'container1234', dockerFile, condaFile, - null, Path.of("."), 'docker.io/my/repo:container1234', PlatformId.NULL, @@ -141,7 +140,6 @@ class SurrealPersistenceServiceTest extends Specification implements SurrealDBTe 'container1234', 'FROM foo:latest', 'conda::recipe', - null, Path.of("."), 'docker.io/my/repo:container1234', PlatformId.NULL, @@ -178,7 +176,6 @@ class SurrealPersistenceServiceTest extends Specification implements SurrealDBTe 'container1234', 'FROM foo:latest', 'conda::recipe', - null, Path.of("/some/path"), 'buildrepo:recipe-container1234', PlatformId.NULL, diff --git a/src/test/groovy/io/seqera/wave/util/ContainerHelperTest.groovy b/src/test/groovy/io/seqera/wave/util/ContainerHelperTest.groovy index 7aedec722..bb68799fe 100644 --- a/src/test/groovy/io/seqera/wave/util/ContainerHelperTest.groovy +++ b/src/test/groovy/io/seqera/wave/util/ContainerHelperTest.groovy @@ -28,7 +28,6 @@ import io.seqera.wave.api.ImageNameStrategy import io.seqera.wave.api.PackagesSpec import io.seqera.wave.api.SubmitContainerTokenRequest import io.seqera.wave.config.CondaOpts -import io.seqera.wave.config.SpackOpts import io.seqera.wave.exception.BadRequestException import io.seqera.wave.service.ContainerRequestData import io.seqera.wave.service.builder.BuildFormat @@ -134,73 +133,6 @@ class ContainerHelperTest extends Specification { '''.stripIndent() } - def 'should create spack singularity file'() { - given: - def SPACK_OPTS = new SpackOpts([ - basePackages: 'foo bar', - commands: ['run','--this','--that'] - ]) - def packages = new PackagesSpec(type: PackagesSpec.Type.SPACK, spackOpts: SPACK_OPTS) - - when: - def result = ContainerHelper.containerFileFromPackages(packages, true) - - then: - result == '''\ - Bootstrap: docker - From: {{spack_runner_image}} - stage: final - - %files from build - /opt/spack-env /opt/spack-env - /opt/software /opt/software - /opt/._view /opt/._view - /opt/spack-env/z10_spack_environment.sh /.singularity.d/env/91-environment.sh - - %post - run - --this - --that - '''.stripIndent() - } - - def 'should create spack docker file'() { - given: - def SPACK_OPTS = new SpackOpts([ - basePackages: 'foo bar', - commands: ['run','--this','--that'] - ]) - def packages = new PackagesSpec(type: PackagesSpec.Type.SPACK, spackOpts: SPACK_OPTS) - - when: - def result = ContainerHelper.containerFileFromPackages(packages, false) - - then: - result == '''\ - # Runner image - FROM {{spack_runner_image}} - - COPY --from=builder /opt/spack-env /opt/spack-env - COPY --from=builder /opt/software /opt/software - COPY --from=builder /opt/._view /opt/._view - - # Entrypoint for Singularity - RUN mkdir -p /.singularity.d/env && \\ - cp -p /opt/spack-env/z10_spack_environment.sh /.singularity.d/env/91-environment.sh - # Entrypoint for Docker - RUN echo "#!/usr/bin/env bash\\n\\nset -ef -o pipefail\\nsource /opt/spack-env/z10_spack_environment.sh\\nexec \\"\\$@\\"" \\ - >/opt/spack-env/spack_docker_entrypoint.sh && chmod a+x /opt/spack-env/spack_docker_entrypoint.sh - - run - --this - --that - - ENTRYPOINT [ "/opt/spack-env/spack_docker_entrypoint.sh" ] - CMD [ "/bin/bash" ] - '''.stripIndent() - } - - def 'should validate conda file helper' () { given: def CONDA = 'this and that' @@ -274,48 +206,6 @@ class ContainerHelperTest extends Specification { '''.stripIndent() } - def 'should validate spack file helper' () { - given: - def SPACK = 'this and that' - def req = new SubmitContainerTokenRequest(spackFile: SPACK.bytes.encodeBase64().toString()) - when: - def result = ContainerHelper.spackFileFromRequest(req) - then: - result == SPACK - } - - def 'should validate spack env file helper' () { - given: - def SPACK = '''\ - spack: - specs: [bwa@0.7.15, salmon@1.1.1] - concretizer: {unify: true, reuse: false} - '''.stripIndent(true) - and: - def spec = new PackagesSpec(type: PackagesSpec.Type.SPACK, environment: SPACK.bytes.encodeBase64().toString()) - def req = new SubmitContainerTokenRequest(packages: spec) - - when: - def result = ContainerHelper.spackFileFromRequest(req) - then: - result == SPACK - } - - def 'should validate spack env packages helper' () { - given: - def spec = new PackagesSpec(type: PackagesSpec.Type.SPACK, entries: ['foo', 'bar']) - def req = new SubmitContainerTokenRequest(packages: spec) - - when: - def result = ContainerHelper.spackFileFromRequest(req) - then: - result == '''\ - spack: - specs: [foo, bar] - concretizer: {unify: true, reuse: false} - '''.stripIndent(true) - } - def 'should create response v1' () { given: def data = new ContainerRequestData(null, @@ -474,48 +364,6 @@ class ContainerHelperTest extends Specification { ContainerHelper.guessCondaRecipeName(CONDA,true) == new NameVersionPair(['pip','pandas'] as Set, [null, '2.2.2'] as Set) } - def 'should find spack recipe names from spack yaml file' () { - def SPACK = '''\ - spack: - specs: [bwa@0.7.15, salmon@1.1.1, nano@1.0 x=one] - concretizer: {unify: true, reuse: true} - '''.stripIndent(true) - - expect: - ContainerHelper.guessSpackRecipeName(null) == null - ContainerHelper.guessSpackRecipeName(SPACK) == new NameVersionPair(['bwa-0.7.15', 'salmon-1.1.1', 'nano-1.0'] as Set) - and: - ContainerHelper.guessSpackRecipeName(SPACK,true) == new NameVersionPair(['bwa', 'salmon', 'nano'] as Set, ['0.7.15', '1.1.1', '1.0'] as Set) - } - - def 'should throw an exception when spack section is not present in spack yaml file' () { - def SPACK = '''\ - specs: [bwa@0.7.15, salmon@1.1.1, nano@1.0 x=one] - concretizer: {unify: true, reuse: true} - '''.stripIndent(true) - - when: - ContainerHelper.guessSpackRecipeName(SPACK) - then: - def e = thrown(BadRequestException) - and: - e.message == 'Malformed Spack environment file - missing "spack:" section' - } - - def 'should throw an exception when spack.specs section is not present in spack yaml file' () { - def SPACK = '''\ - spack: - concretizer: {unify: true, reuse: true} - '''.stripIndent(true) - - when: - ContainerHelper.guessSpackRecipeName(SPACK) - then: - def e = thrown(BadRequestException) - and: - e.message == 'Malformed Spack environment file - missing "spack.specs:" section' - } - @Unroll def 'should normalise tag' () { expect: @@ -565,10 +413,10 @@ class ContainerHelperTest extends Specification { def 'should make request target' () { expect: - ContainerHelper.makeTargetImage(BuildFormat.DOCKER, 'quay.io/org/name', '12345', null, null, null) + ContainerHelper.makeTargetImage(BuildFormat.DOCKER, 'quay.io/org/name', '12345', null, null) == 'quay.io/org/name:12345' and: - ContainerHelper.makeTargetImage(BuildFormat.SINGULARITY, 'quay.io/org/name', '12345', null, null, null) + ContainerHelper.makeTargetImage(BuildFormat.SINGULARITY, 'quay.io/org/name', '12345', null, null) == 'oras://quay.io/org/name:12345' and: @@ -576,16 +424,9 @@ class ContainerHelperTest extends Specification { dependencies: - salmon=1.2.3 ''' - ContainerHelper.makeTargetImage(BuildFormat.DOCKER, 'quay.io/org/name', '12345', conda, null, null) + ContainerHelper.makeTargetImage(BuildFormat.DOCKER, 'quay.io/org/name', '12345', conda, null) == 'quay.io/org/name:salmon-1.2.3--12345' - and: - def spack = '''\ - spack: - specs: [bwa@0.7.15] - ''' - ContainerHelper.makeTargetImage(BuildFormat.DOCKER, 'quay.io/org/name', '12345', null, spack, null) - == 'quay.io/org/name:bwa-0.7.15--12345' } @@ -633,16 +474,6 @@ class ContainerHelperTest extends Specification { - numpy=1.0 '''.stripIndent(true) - @Shared def SPACK1 = '''\ - spack: - specs: [bwa@0.7.15] - ''' - - @Shared def SPACK2 = '''\ - spack: - specs: [bwa@0.7.15, salmon@1.1.1] - ''' - @Unroll def 'should make request target with name strategy' () { expect: @@ -651,62 +482,50 @@ class ContainerHelperTest extends Specification { REPO, ID, CONDA, - SPACK, STRATEGY ? ImageNameStrategy.valueOf(STRATEGY) : null) == EXPECTED where: - FORMAT | REPO | ID | CONDA | SPACK | STRATEGY | EXPECTED - 'DOCKER' | 'foo.com/build' | '123' | null | null | null | 'foo.com/build:123' - 'DOCKER' | 'foo.com/build' | '123' | null | null | 'none' | 'foo.com/build:123' - 'DOCKER' | 'foo.com/build' | '123' | null | null | 'tagPrefix' | 'foo.com/build:123' - 'DOCKER' | 'foo.com/build' | '123' | null | null | 'imageSuffix' | 'foo.com/build:123' - and: - 'SINGULARITY' | 'foo.com/build' | '123' | null | null | null | 'oras://foo.com/build:123' - 'SINGULARITY' | 'foo.com/build' | '123' | null | null | 'none' | 'oras://foo.com/build:123' - 'SINGULARITY' | 'foo.com/build' | '123' | null | null | 'tagPrefix' | 'oras://foo.com/build:123' - 'SINGULARITY' | 'foo.com/build' | '123' | null | null | 'imageSuffix' | 'oras://foo.com/build:123' + FORMAT | REPO | ID | CONDA | STRATEGY | EXPECTED + 'DOCKER' | 'foo.com/build' | '123' | null | null | 'foo.com/build:123' + 'DOCKER' | 'foo.com/build' | '123' | null | 'none' | 'foo.com/build:123' + 'DOCKER' | 'foo.com/build' | '123' | null | 'tagPrefix' | 'foo.com/build:123' + 'DOCKER' | 'foo.com/build' | '123' | null | 'imageSuffix' | 'foo.com/build:123' and: - 'DOCKER' | 'foo.com/build' | '123' | CONDA1| null | null | 'foo.com/build:samtools-1.0--123' - 'DOCKER' | 'foo.com/build' | '123' | CONDA1| null | 'none' | 'foo.com/build:123' - 'DOCKER' | 'foo.com/build' | '123' | CONDA1| null | 'tagPrefix' | 'foo.com/build:samtools-1.0--123' - 'DOCKER' | 'foo.com/build' | '123' | CONDA1| null | 'imageSuffix' | 'foo.com/build/samtools:1.0--123' + 'SINGULARITY' | 'foo.com/build' | '123' | null | null | 'oras://foo.com/build:123' + 'SINGULARITY' | 'foo.com/build' | '123' | null | 'none' | 'oras://foo.com/build:123' + 'SINGULARITY' | 'foo.com/build' | '123' | null | 'tagPrefix' | 'oras://foo.com/build:123' + 'SINGULARITY' | 'foo.com/build' | '123' | null | 'imageSuffix' | 'oras://foo.com/build:123' and: - 'SINGULARITY' | 'foo.com/build' | '123' | CONDA1| null | null | 'oras://foo.com/build:samtools-1.0--123' - 'SINGULARITY' | 'foo.com/build' | '123' | CONDA1| null | 'none' | 'oras://foo.com/build:123' - 'SINGULARITY' | 'foo.com/build' | '123' | CONDA1| null | 'tagPrefix' | 'oras://foo.com/build:samtools-1.0--123' - 'SINGULARITY' | 'foo.com/build' | '123' | CONDA1| null | 'imageSuffix' | 'oras://foo.com/build/samtools:1.0--123' + 'DOCKER' | 'foo.com/build' | '123' | CONDA1| null | 'foo.com/build:samtools-1.0--123' + 'DOCKER' | 'foo.com/build' | '123' | CONDA1| 'none' | 'foo.com/build:123' + 'DOCKER' | 'foo.com/build' | '123' | CONDA1| 'tagPrefix' | 'foo.com/build:samtools-1.0--123' + 'DOCKER' | 'foo.com/build' | '123' | CONDA1| 'imageSuffix' | 'foo.com/build/samtools:1.0--123' and: - 'DOCKER' | 'foo.com/build' | '123' | CONDA2| null | null | 'foo.com/build:samtools-1.0_bamtools-2.0_multiqc-1.15--123' - 'DOCKER' | 'foo.com/build' | '123' | CONDA2| null | 'none' | 'foo.com/build:123' - 'DOCKER' | 'foo.com/build' | '123' | CONDA2| null | 'tagPrefix' | 'foo.com/build:samtools-1.0_bamtools-2.0_multiqc-1.15--123' - 'DOCKER' | 'foo.com/build' | '123' | CONDA2| null | 'imageSuffix' | 'foo.com/build/samtools_bamtools_multiqc:123' + 'SINGULARITY' | 'foo.com/build' | '123' | CONDA1| null | 'oras://foo.com/build:samtools-1.0--123' + 'SINGULARITY' | 'foo.com/build' | '123' | CONDA1| 'none' | 'oras://foo.com/build:123' + 'SINGULARITY' | 'foo.com/build' | '123' | CONDA1| 'tagPrefix' | 'oras://foo.com/build:samtools-1.0--123' + 'SINGULARITY' | 'foo.com/build' | '123' | CONDA1| 'imageSuffix' | 'oras://foo.com/build/samtools:1.0--123' and: - 'DOCKER' | 'foo.com/build' | '123' | CONDA3| null | null | 'foo.com/build:samtools-1.0_bamtools-2.0_multiqc-1.15_bwa-1.2.3_pruned--123' - 'DOCKER' | 'foo.com/build' | '123' | CONDA3| null | 'none' | 'foo.com/build:123' - 'DOCKER' | 'foo.com/build' | '123' | CONDA3| null | 'tagPrefix' | 'foo.com/build:samtools-1.0_bamtools-2.0_multiqc-1.15_bwa-1.2.3_pruned--123' - 'DOCKER' | 'foo.com/build' | '123' | CONDA3| null | 'imageSuffix' | 'foo.com/build/samtools_bamtools_multiqc_bwa_pruned:123' + 'DOCKER' | 'foo.com/build' | '123' | CONDA2| null | 'foo.com/build:samtools-1.0_bamtools-2.0_multiqc-1.15--123' + 'DOCKER' | 'foo.com/build' | '123' | CONDA2| 'none' | 'foo.com/build:123' + 'DOCKER' | 'foo.com/build' | '123' | CONDA2| 'tagPrefix' | 'foo.com/build:samtools-1.0_bamtools-2.0_multiqc-1.15--123' + 'DOCKER' | 'foo.com/build' | '123' | CONDA2| 'imageSuffix' | 'foo.com/build/samtools_bamtools_multiqc:123' and: - 'DOCKER' | 'foo.com/build' | '123' | PIP1 | null | null | 'foo.com/build:pip_pandas-2.2.2--123' - 'DOCKER' | 'foo.com/build' | '123' | PIP1 | null | 'none' | 'foo.com/build:123' - 'DOCKER' | 'foo.com/build' | '123' | PIP1 | null | 'tagPrefix' | 'foo.com/build:pip_pandas-2.2.2--123' - 'DOCKER' | 'foo.com/build' | '123' | PIP1 | null | 'imageSuffix' | 'foo.com/build/pip_pandas:123' + 'DOCKER' | 'foo.com/build' | '123' | CONDA3| null | 'foo.com/build:samtools-1.0_bamtools-2.0_multiqc-1.15_bwa-1.2.3_pruned--123' + 'DOCKER' | 'foo.com/build' | '123' | CONDA3| 'none' | 'foo.com/build:123' + 'DOCKER' | 'foo.com/build' | '123' | CONDA3| 'tagPrefix' | 'foo.com/build:samtools-1.0_bamtools-2.0_multiqc-1.15_bwa-1.2.3_pruned--123' + 'DOCKER' | 'foo.com/build' | '123' | CONDA3| 'imageSuffix' | 'foo.com/build/samtools_bamtools_multiqc_bwa_pruned:123' and: - 'DOCKER' | 'foo.com/build' | '123' | PIP2 | null | null | 'foo.com/build:pip_pandas-2.2.2_numpy-1.0--123' - 'DOCKER' | 'foo.com/build' | '123' | PIP2 | null | 'tagPrefix' | 'foo.com/build:pip_pandas-2.2.2_numpy-1.0--123' - 'DOCKER' | 'foo.com/build' | '123' | PIP2 | null | 'imageSuffix' | 'foo.com/build/pip_pandas_numpy:123' - 'DOCKER' | 'foo.com/build' | '123' | PIP2 | null | 'none' | 'foo.com/build:123' - + 'DOCKER' | 'foo.com/build' | '123' | PIP1 | null | 'foo.com/build:pip_pandas-2.2.2--123' + 'DOCKER' | 'foo.com/build' | '123' | PIP1 | 'none' | 'foo.com/build:123' + 'DOCKER' | 'foo.com/build' | '123' | PIP1 | 'tagPrefix' | 'foo.com/build:pip_pandas-2.2.2--123' + 'DOCKER' | 'foo.com/build' | '123' | PIP1 | 'imageSuffix' | 'foo.com/build/pip_pandas:123' and: - 'DOCKER' | 'foo.com/build' | '123' | null | SPACK1| null | 'foo.com/build:bwa-0.7.15--123' - 'DOCKER' | 'foo.com/build' | '123' | null | SPACK1| 'none' | 'foo.com/build:123' - 'DOCKER' | 'foo.com/build' | '123' | null | SPACK1| 'tagPrefix' | 'foo.com/build:bwa-0.7.15--123' - 'DOCKER' | 'foo.com/build' | '123' | null | SPACK1| 'imageSuffix' | 'foo.com/build/bwa:0.7.15--123' + 'DOCKER' | 'foo.com/build' | '123' | PIP2 | null | 'foo.com/build:pip_pandas-2.2.2_numpy-1.0--123' + 'DOCKER' | 'foo.com/build' | '123' | PIP2 | 'tagPrefix' | 'foo.com/build:pip_pandas-2.2.2_numpy-1.0--123' + 'DOCKER' | 'foo.com/build' | '123' | PIP2 | 'imageSuffix' | 'foo.com/build/pip_pandas_numpy:123' + 'DOCKER' | 'foo.com/build' | '123' | PIP2 | 'none' | 'foo.com/build:123' - and: - 'DOCKER' | 'foo.com/build' | '123' | null | SPACK2| null | 'foo.com/build:bwa-0.7.15_salmon-1.1.1--123' - 'DOCKER' | 'foo.com/build' | '123' | null | SPACK2| 'none' | 'foo.com/build:123' - 'DOCKER' | 'foo.com/build' | '123' | null | SPACK2| 'tagPrefix' | 'foo.com/build:bwa-0.7.15_salmon-1.1.1--123' - 'DOCKER' | 'foo.com/build' | '123' | null | SPACK2| 'imageSuffix' | 'foo.com/build/bwa_salmon:123' } def 'should validate containerfile' () { diff --git a/src/test/groovy/io/seqera/wave/util/SpackHelperTest.groovy b/src/test/groovy/io/seqera/wave/util/SpackHelperTest.groovy deleted file mode 100644 index 6e5ac880f..000000000 --- a/src/test/groovy/io/seqera/wave/util/SpackHelperTest.groovy +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Wave, containers provisioning service - * Copyright (c) 2023-2024, Seqera Labs - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package io.seqera.wave.util - -import spock.lang.Specification - -import io.seqera.wave.core.ContainerPlatform -import io.seqera.wave.service.builder.BuildFormat - -/** - * - * @author Paolo Di Tommaso - */ -class SpackHelperTest extends Specification { - - def 'should load builder template' () { - expect: - SpackHelper.builderDockerTemplate().startsWith('# Builder image') - } - - def 'should prepend builder template' () { - expect: - SpackHelper.prependBuilderTemplate('foo', BuildFormat.DOCKER).startsWith('# Builder image') - SpackHelper.prependBuilderTemplate('foo', BuildFormat.SINGULARITY).endsWith('\nfoo') - } - - def 'should map platform to spack arch' () { - expect: - SpackHelper.toSpackArch(ContainerPlatform.of('x86_64')) == 'x86_64' - SpackHelper.toSpackArch(ContainerPlatform.of('linux/x86_64')) == 'x86_64' - SpackHelper.toSpackArch(ContainerPlatform.of('amd64')) == 'x86_64' - SpackHelper.toSpackArch(ContainerPlatform.of('aarch64')) == 'aarch64' - SpackHelper.toSpackArch(ContainerPlatform.of('arm64')) == 'aarch64' - SpackHelper.toSpackArch(ContainerPlatform.of('linux/arm64/v8')) == 'aarch64' - - when: - SpackHelper.toSpackArch(ContainerPlatform.of('linux/arm64/v7')) - then: - thrown(IllegalArgumentException) - } -} diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index f7ff1edfa..b56b9bd5d 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -53,8 +53,6 @@ wave: password: 'bar' build: workspace: 'build-workspace' - spack: - cacheDirectory: "spack-cache" logs : enabled : true bucket : 'nextflow-ci' diff --git a/typespec/models/Packages.tsp b/typespec/models/Packages.tsp index 6fa59e16d..ce534501b 100644 --- a/typespec/models/Packages.tsp +++ b/typespec/models/Packages.tsp @@ -1,5 +1,4 @@ import "./CondaOpts.tsp"; -import "./SpackOpts.tsp"; @doc("Package configurations for container builds.") model Packages { @@ -7,6 +6,5 @@ model Packages { condaOpts?: CondaOpts; entries: string[]; environment: string; - spackOpts?: SpackOpts; - type: "CONDA" | "SPACK"; -} \ No newline at end of file + type: "CONDA"; +} diff --git a/typespec/models/SpackOpts.tsp b/typespec/models/SpackOpts.tsp deleted file mode 100644 index 813caa6a2..000000000 --- a/typespec/models/SpackOpts.tsp +++ /dev/null @@ -1,5 +0,0 @@ -@doc("Options for Spack environments. Spack support will be removed in future releases") -model SpackOpts { - basePackages: string; - commands: string[]; -} diff --git a/typespec/models/WaveBuildRecord.tsp b/typespec/models/WaveBuildRecord.tsp index f8e40bc4c..50f6966d1 100644 --- a/typespec/models/WaveBuildRecord.tsp +++ b/typespec/models/WaveBuildRecord.tsp @@ -10,7 +10,6 @@ model WaveBuildRecord { platform: string; requestIp: string; scanId: string; - spackFile: string; startTime: string; succeeded: boolean; targetImage: string; @@ -18,4 +17,3 @@ model WaveBuildRecord { userId: int64; userName: string; } - \ No newline at end of file