From 00504f8c5b5ce7204388a618c123bb92da8b47a7 Mon Sep 17 00:00:00 2001 From: LucasSovre Date: Mon, 27 Jan 2025 14:02:45 -0500 Subject: [PATCH] fix #3, error while reading volumes --- src/translator/translator.ts | 63 +++++++++++++++++++++++++--------- test/translator/reader.test.ts | 11 ++++++ 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/src/translator/translator.ts b/src/translator/translator.ts index 382055f..7dc969a 100644 --- a/src/translator/translator.ts +++ b/src/translator/translator.ts @@ -2,12 +2,19 @@ import { Compose, ComposeVersion } from "@compose/compose"; import { Network } from "@compose/network"; import { Binding, Volume } from "@compose/volume"; -import { Service, Image, Build, PortMapping, Protocol } from "@compose/service"; +import { Build, Image, PortMapping, Protocol, Service } from "@compose/service"; import { getSimpleValues, turnObjectInArrayWithName } from "@commons/utils"; import { AccessType } from "@commons/volumeAccesType"; import { Env, KeyValue } from "@commons/keyValue"; import { SuperSet } from "@commons/superSet"; +type VolumeBindingRead = { + type: "bind" | "volume"; + source: string; // The source path (relative or absolute path on the host machine) + target: string; // The target path inside the container + read_only: boolean; // Boolean to specify if the mount is read-only or not +}; + /** * This is an implementation of the translator pattern, that allow us to have an instance over {@link Compose} that can smartly know about top level params and deep one without the need of any extra references. * @@ -206,13 +213,22 @@ export class Translator { }) } if (service?.volumes){ - service?.volumes?.forEach((vol:string)=>{ - if(vol.startsWith("/") || vol.startsWith(".")){ - const strippedVol = vol.split(":") + service?.volumes?.forEach((vol:string|VolumeBindingRead)=>{ + if(typeof(vol) === "string"){ + if(vol.startsWith("/") || vol.startsWith(".")){ + const strippedVol = vol.split(":") + ser.bindings.add(new Binding({ + source: strippedVol[0], + target: strippedVol[1], + mode: strippedVol[2] as AccessType + })) + } + } + if(typeof(vol) === "object"){ ser.bindings.add(new Binding({ - source: strippedVol[0], - target: strippedVol[1], - mode: strippedVol[2] as AccessType + source: vol.source, + target: vol.target, + mode: vol.read_only ? AccessType.READ_ONLY : AccessType.READ_WRITE })) } }) @@ -261,16 +277,29 @@ export class Translator { }) } if(rawSer.volumes){ - rawSer.volumes.forEach((vol_String:string)=>{ - if(!(vol_String.startsWith(".") || vol_String.startsWith("/"))){ - const strippedVol = vol_String.split(":") - const volume = Array.from(result.volumes).find(vol=>vol.name===strippedVol[0]) - if(service && volume){ - service.bindings.add(new Binding({ - source: volume, - target: strippedVol[1], - mode: strippedVol[2] as AccessType - })) + rawSer.volumes.forEach((volRaw:string|VolumeBindingRead)=>{ + if(typeof(volRaw) === "string"){ + if(!(volRaw.startsWith(".") || volRaw.startsWith("/"))){ + const strippedVol = volRaw.split(":") + const volume = Array.from(result.volumes).find(vol=>vol.name===strippedVol[0]) + if(service && volume){ + service.bindings.add(new Binding({ + source: volume, + target: strippedVol[1], + mode: strippedVol[2] as AccessType + })) + } + } + }else{ + if(volRaw.type === "volume"){ + const volume = Array.from(result.volumes).find(vol=>vol.name===volRaw.source) + if(service && volume){ + service.bindings.add(new Binding({ + source: volume, + target: volRaw.target, + mode: volRaw.read_only ? AccessType.READ_ONLY : AccessType.READ_WRITE + })) + } } } }) diff --git a/test/translator/reader.test.ts b/test/translator/reader.test.ts index d0c4c6b..c8e3ad5 100644 --- a/test/translator/reader.test.ts +++ b/test/translator/reader.test.ts @@ -220,4 +220,15 @@ describe("read compose file", () => { expect(serviceWebLabels?.length).toBe(3) } }) + + test("fix issue #3",()=>{ + const specialInput = `{"networks":{"default":{"name":"rxresume-network"},"tk-proxy":{"name":"tk-proxy","external":true}},"services":{"postgres":{"container_name":"rxresume-postgres","env_file":[".env"],"environment":{"POSTGRES_DB":"postgres","POSTGRES_PASSWORD":"\${DB_PASSWORD}","POSTGRES_USER":"postgres"},"healthcheck":{"test":["CMD-SHELL","pg_isready -U postgres -d postgres"],"interval":"10s","timeout":"5s","retries":5},"image":"postgres:16-alpine","restart":"unless-stopped","volumes":[{"type":"bind","source":"./container_data/postgres/data","target":"/var/lib/postgresql/data","read_only":false}]},"minio":{"command":"server --address \\":9000\\" --console-address \\":9001\\" /data","container_name":"rxresume-minio","env_file":[".env"],"environment":{"MINIO_ROOT_PASSWORD":"\${STORAGE_PASSWORD}","MINIO_ROOT_USER":"minioadmin"},"image":"minio/minio","labels":{"traefik.enable":true,"traefik.http.routers.rxresume-storage.entrypoints":"https","traefik.http.routers.rxresume-storage.rule":"Host(\`rxresume-storage.cf.domain.tld\`)","traefik.http.routers.rxresume-storage.service":"rxresume-storage","traefik.http.routers.rxresume-storage.tls":true,"traefik.http.services.rxresume-storage.loadbalancer.server.port":9000},"networks":["default","tk-proxy"],"restart":"unless-stopped","volumes":[{"type":"bind","source":"./container_data/minio/data","target":"/data","read_only":false}]},"chrome":{"container_name":"rxresume-chrome","environment":{"CONCURRENT":10,"EXIT_ON_HEALTH_FAILURE":true,"PRE_REQUEST_HEALTH_CHECK":true,"TIMEOUT":60000,"TOKEN":"\${CHROME_TOKEN}"},"extra_hosts":["host.docker.internal:host-gateway"],"image":"ghcr.io/browserless/chromium:v2.18.0","restart":"unless-stopped"},"rxresume":{"container_name":"rxresume","depends_on":["postgres","minio","chrome"],"environment":{"ACCESS_TOKEN_SECRET":"\${ACCESS_TOKEN_SECRET}","CHROME_TOKEN":"\${CHROME_TOKEN}","CHROME_URL":"ws://rxresume-chrome:3000","DATABASE_URL":"postgresql://postgres:\${DB_PASSWORD}@rxresume-postgres:5432/postgres","NODE_ENV":"production","PORT":3005,"PUBLIC_URL":"https://rxresume.cf.domain.tld","REFRESH_TOKEN_SECRET":"\${REFRESH_TOKEN_SECRET}","STORAGE_ACCESS_KEY":"minioadmin","STORAGE_BUCKET":"default","STORAGE_ENDPOINT":"rxresume-minio","STORAGE_PORT":9000,"STORAGE_REGION":"us-east-1","STORAGE_SECRET_KEY":"\${STORAGE_PASSWORD}","STORAGE_SKIP_BUCKET_CHECK":false,"STORAGE_URL":"https://rxresume-storage.cf.domain.tld/default","STORAGE_USE_SSL":false},"image":"amruthpillai/reactive-resume:latest","labels":{"traefik.enable":true,"traefik.http.routers.rxresume.entrypoints":"https","traefik.http.routers.rxresume.rule":"Host(\`rxresume.cf.domain.tld\`)","traefik.http.routers.rxresume.service":"rxresume","traefik.http.routers.rxresume.tls":true,"traefik.http.services.rxresume.loadbalancer.server.port":3005},"networks":["default","tk-proxy"],"restart":"unless-stopped"}}}` + const result = Translator.fromDict(JSON.parse(specialInput)) + expect(result).toBeDefined() + const minio = result.services.get("name","minio") + expect(minio).toBeDefined() + if(minio){ + expect(minio.bindings.size===1).toBeTruthy() + } + }) });