diff --git a/src/@types/C2D/C2D.ts b/src/@types/C2D/C2D.ts index 2045b000f..9bb978396 100644 --- a/src/@types/C2D/C2D.ts +++ b/src/@types/C2D/C2D.ts @@ -47,7 +47,7 @@ export interface ComputeEnvironment extends ComputeEnvironmentBaseConfig { consumerAddress: string lastSeen?: number free: boolean - platform?: DockerPlatform[] + platform?: DockerPlatform } export interface C2DDockerConfig { diff --git a/src/components/c2d/compute_engine_docker.ts b/src/components/c2d/compute_engine_docker.ts index 4e685b31b..ec48c3d42 100644 --- a/src/components/c2d/compute_engine_docker.ts +++ b/src/components/c2d/compute_engine_docker.ts @@ -135,7 +135,10 @@ export class C2DEngineDocker extends C2DEngine { * @param image name or tag * @returns boolean */ - public static async checkDockerImage(image: string): Promise { + public static async checkDockerImage( + image: string, + platform?: DockerPlatform + ): Promise { try { const info = drc.default.parseRepoAndRef(image) /** @@ -159,7 +162,7 @@ export class C2DEngineDocker extends C2DEngine { client.close() if (manifest) { return resolve({ - valid: checkManifestPlatform(manifest, null) // TODO: + valid: checkManifestPlatform(manifest.platform, platform) }) } @@ -210,7 +213,13 @@ export class C2DEngineDocker extends C2DEngine { )}` ) } - const validation = await C2DEngineDocker.checkDockerImage(image) + const envIdWithHash = environment && environment.indexOf('-') > -1 + const env = await this.getComputeEnvironment( + chainId, + envIdWithHash ? environment : null, + environment + ) + const validation = await C2DEngineDocker.checkDockerImage(image, env.platform) if (!validation.valid) throw new Error(`Unable to validate docker image ${image}: ${validation.reason}`) @@ -1081,10 +1090,11 @@ export function checkManifestPlatform( manifestPlatform: any, envPlatform: DockerPlatform ): boolean { - if (!manifestPlatform || !envPlatform) return true // skipping + if (!manifestPlatform || !envPlatform) return true // skips if not present if ( envPlatform.architecture !== manifestPlatform.architecture || envPlatform.os !== manifestPlatform.os ) return false + return true } diff --git a/src/components/core/compute/initialize.ts b/src/components/core/compute/initialize.ts index 269f9fb21..134442827 100644 --- a/src/components/core/compute/initialize.ts +++ b/src/components/core/compute/initialize.ts @@ -141,8 +141,13 @@ export class ComputeInitializeHandler extends Handler { if (hasDockerImages) { const algoImage = getAlgorithmImage(task.algorithm) if (algoImage) { - const validation: ValidateParams = - await C2DEngineDocker.checkDockerImage(algoImage) + const env = await this.getOceanNode() + .getC2DEngines() + .getExactComputeEnv(task.compute.env, ddo.chainId) + const validation: ValidateParams = await C2DEngineDocker.checkDockerImage( + algoImage, + env.platform + ) if (!validation.valid) { return { stream: null, diff --git a/src/test/data/assets.ts b/src/test/data/assets.ts index f1e2f9b70..632394f5f 100644 --- a/src/test/data/assets.ts +++ b/src/test/data/assets.ts @@ -326,3 +326,59 @@ export const completeDBComputeJob = { containerImage: 'node@sha256:1155995dda741e93afe4b1c6ced2d01734a6ec69865cc0997daf1f4db7259a36' } + +export const dockerImageManifest = { + schemaVersion: 2, + mediaType: 'application/vnd.docker.distribution.manifest.v2+json', + config: { + mediaType: 'application/vnd.docker.container.image.v1+json', + size: 7286, + digest: 'sha256:386e0be86bde5eff9f85ea9eda02727dd4641664d746688b4049f79ef0cdb1c9' + }, + platform: { + architecture: 'amd64', + os: 'linux' + }, + layers: [ + { + mediaType: 'application/vnd.docker.image.rootfs.diff.tar.gzip', + size: 49557601, + digest: 'sha256:167b8a53ca4504bc6aa3182e336fa96f4ef76875d158c1933d3e2fa19c57e0c3' + }, + { + mediaType: 'application/vnd.docker.image.rootfs.diff.tar.gzip', + size: 24030522, + digest: 'sha256:b47a222d28fa95680198398973d0a29b82a968f03e7ef361cc8ded562e4d84a3' + }, + { + mediaType: 'application/vnd.docker.image.rootfs.diff.tar.gzip', + size: 64112257, + digest: 'sha256:debce5f9f3a9709885f7f2ad3cf41f036a3b57b406b27ba3a883928315787042' + }, + { + mediaType: 'application/vnd.docker.image.rootfs.diff.tar.gzip', + size: 211039785, + digest: 'sha256:1d7ca7cd2e066ae77ac6284a9d027f72a31a02a18bfc2a249ef2e7b01074338b' + }, + { + mediaType: 'application/vnd.docker.image.rootfs.diff.tar.gzip', + size: 3371, + digest: 'sha256:94c7791033e87c3ab82bf56f778253138bbd5caf172ead6fc0ce39d459560607' + }, + { + mediaType: 'application/vnd.docker.image.rootfs.diff.tar.gzip', + size: 47856943, + digest: 'sha256:72ab0dfaf5cb14ab09fd3478f8a01e3c3e21b7ad06e7b04ccac2f304d455ff45' + }, + { + mediaType: 'application/vnd.docker.image.rootfs.diff.tar.gzip', + size: 2280920, + digest: 'sha256:3316ed2852d408595e2dfc601d96f39f4a39747bd1eb2eb1b63b1f3d49c42919' + }, + { + mediaType: 'application/vnd.docker.image.rootfs.diff.tar.gzip', + size: 451, + digest: 'sha256:ef5505406bea98d0f6adb559b937c0dad0aef6d98500b1120c6e27c50fdf172b' + } + ] +} diff --git a/src/test/unit/compute.test.ts b/src/test/unit/compute.test.ts index b1872b69c..85db3eee7 100644 --- a/src/test/unit/compute.test.ts +++ b/src/test/unit/compute.test.ts @@ -8,7 +8,8 @@ import { ComputeAsset, ComputeEnvironment, ComputeJob, - DBComputeJob + DBComputeJob, + DockerPlatform } from '../../@types/C2D/C2D.js' // import { computeAsset } from '../data/assets' import { assert, expect } from 'chai' @@ -25,8 +26,10 @@ import { } from '../utils/utils.js' import { OceanNodeConfig } from '../../@types/OceanNode.js' import { ENVIRONMENT_VARIABLES } from '../../utils/constants.js' -import { completeDBComputeJob } from '../data/assets.js' +import { completeDBComputeJob, dockerImageManifest } from '../data/assets.js' import { omitDBComputeFieldsFromComputeJob } from '../../components/c2d/index.js' +import os from 'os' +import { checkManifestPlatform } from '../../components/c2d/compute_engine_docker.js' describe('Compute Jobs Database', () => { let envOverrides: OverrideEnvConfig[] @@ -205,6 +208,29 @@ describe('Compute Jobs Database', () => { ) }) + it('should check manifest platform against local platform env', () => { + const arch = os.machine() // ex: arm + const platform = os.platform() // ex: linux + const env: DockerPlatform = { + architecture: arch, + os: platform + } + const result: boolean = checkManifestPlatform(dockerImageManifest.platform, env) + // if all defined and a match its OK + if ( + dockerImageManifest.platform.os === env.os && + dockerImageManifest.platform.architecture === env.architecture + ) { + expect(result).to.be.equal(true) + } else { + // oterwise its NOT + expect(result).to.be.equal(false) + } + + // all good anyway, nothing on the manifest + expect(checkManifestPlatform(null, env)).to.be.equal(true) + }) + after(async () => { await tearDownEnvironment(envOverrides) }) diff --git a/src/utils/config.ts b/src/utils/config.ts index 632657ac5..52838954f 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -401,7 +401,7 @@ function getDockerFreeComputeOptions( feeToken: ZeroAddress, chainId: 8996, free: true, - platform: [{ architecture: os.machine(), os: os.platform() }] + platform: { architecture: os.machine(), os: os.platform() } } if (existsEnvironmentVariable(ENVIRONMENT_VARIABLES.DOCKER_FREE_COMPUTE, isStartup)) { @@ -411,7 +411,7 @@ function getDockerFreeComputeOptions( ) as ComputeEnvironmentBaseConfig doComputeEnvChecks([options]) const env = { ...options } as ComputeEnvironment - env.platform = [{ architecture: os.machine(), os: os.platform() }] + env.platform = { architecture: os.machine(), os: os.platform() } return env } catch (error) { CONFIG_LOGGER.logMessageWithEmoji( @@ -462,7 +462,7 @@ function getDockerComputeEnvironments(isStartup?: boolean): ComputeEnvironment[] doComputeEnvChecks(options) const envs = { ...options } as ComputeEnvironment[] envs.forEach((env) => { - env.platform = [{ architecture: os.machine(), os: os.platform() }] + env.platform = { architecture: os.machine(), os: os.platform() } }) return envs } catch (error) {