diff --git a/changelogs/2.5.2.md b/changelogs/2.5.2.md index 1048632..5d51591 100644 --- a/changelogs/2.5.2.md +++ b/changelogs/2.5.2.md @@ -1,4 +1,4 @@ -# 2.5.2-rc.1 +# 2.5.2 ## Fixed diff --git a/command/main.ts b/command/main.ts index 908f2ba..3ef49f1 100644 --- a/command/main.ts +++ b/command/main.ts @@ -37,7 +37,7 @@ function cloneCommand(command: T): T { export const main = defineCommand({ meta: { name: "kuber", - version: "2.5.2-rc.1", + version: "2.5.2", description: "Docker Compose -> K8s translation layer", }, args: { diff --git a/lib/convert.ts b/lib/convert.ts index 7871582..d01b610 100644 --- a/lib/convert.ts +++ b/lib/convert.ts @@ -1286,7 +1286,9 @@ export function volumesToPvc( labels: LABELS, }, spec: { - accessModes: ["ReadWriteMany"], + accessModes: [ + policy?.replicaCount === 1 ? "ReadWriteOnce" : "ReadWriteMany", + ], storageClassName: policy ? toLonghornStorageClassName(policy) : undefined, diff --git a/package.json b/package.json index 9288da2..517e039 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@dmgnr/kuber", - "version": "2.5.2-rc.1", + "version": "2.5.2", "description": "Docker Compose to Kubernetes translation layer", "bin": { "kuber": "dist/index.js" diff --git a/tests/lib/convert-storage-env.test.ts b/tests/lib/convert-storage-env.test.ts index 3101bc4..3eee062 100644 --- a/tests/lib/convert-storage-env.test.ts +++ b/tests/lib/convert-storage-env.test.ts @@ -38,6 +38,7 @@ describe("volume conversion", () => { expect(claims[0]).toMatchObject({ metadata: { name: "project-data", namespace: "project" }, spec: { + accessModes: ["ReadWriteMany"], resources: { requests: { storage: "1Gi" } }, storageClassName: expect.stringMatching( /^kuber-longhorn-[a-f0-9]{12}$/, @@ -64,6 +65,16 @@ describe("volume conversion", () => { ]); }); + test("renders single-replica Longhorn volumes as ReadWriteOnce", () => { + const service = { volumes: ["data(20Gi on 1 fast):/data"] } as Service; + const [claim] = volumesToPvc("project", service); + const [storageClass] = volumesToStorageClasses(service); + + expect(claim?.spec?.accessModes).toEqual(["ReadWriteOnce"]); + expect(claim?.spec?.storageClassName).toBe(storageClass?.metadata?.name); + expect(storageClass?.parameters?.numberOfReplicas).toBe("1"); + }); + test("parses inline storage sizing and placement", () => { const [claim] = volumesToPvc("project", { volumes: ["data(20Gi on 3 fast,archive):/data"], @@ -71,6 +82,7 @@ describe("volume conversion", () => { expect(claim).toMatchObject({ metadata: { name: "project-data" }, spec: { + accessModes: ["ReadWriteMany"], resources: { requests: { storage: "20Gi" } }, storageClassName: expect.stringMatching(/^kuber-longhorn-/), }, @@ -103,11 +115,29 @@ describe("volume conversion", () => { } as ComposeSpecification["volumes"], ); expect(claim?.spec).toMatchObject({ + accessModes: ["ReadWriteMany"], resources: { requests: { storage: "50Gi" } }, storageClassName: expect.stringMatching(/^kuber-longhorn-/), }); }); + test("uses top-level single-replica policy for the PVC and StorageClass", () => { + const service = { volumes: ["data:/data"] } as Service; + const volumes = { + data: { "x-replicaCount": 1 }, + } as ComposeSpecification["volumes"]; + const [claim] = volumesToPvc("project", service, process.cwd(), volumes); + const [storageClass] = volumesToStorageClasses( + service, + process.cwd(), + volumes, + ); + + expect(claim?.spec?.accessModes).toEqual(["ReadWriteOnce"]); + expect(claim?.spec?.storageClassName).toBe(storageClass?.metadata?.name); + expect(storageClass?.parameters?.numberOfReplicas).toBe("1"); + }); + test("does not create PVCs for anonymous, tmpfs, or managed claims", () => { expect( volumesToPvc("project", {