From d15b1d30bcfd483901759d6b3498ead3d9594d6d Mon Sep 17 00:00:00 2001 From: Julien DOCHE Date: Fri, 10 Nov 2023 14:59:39 +0100 Subject: [PATCH 1/6] Add iscsi pv Signed-off-by: Julien DOCHE --- src/pv.ts | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 1 deletion(-) diff --git a/src/pv.ts b/src/pv.ts index e02495ecd..062d784c2 100644 --- a/src/pv.ts +++ b/src/pv.ts @@ -4,6 +4,7 @@ import * as base from './base'; import * as k8s from './imports/k8s'; import * as pvc from './pvc'; import * as volume from './volume'; +import * as secret from './secret'; /** * Contract of a `PersistentVolumeClaim`. @@ -599,4 +600,137 @@ export class GCEPersistentDiskPersistentVolume extends PersistentVolume { }, }; } -} \ No newline at end of file +} + +/** + * Properties for `IscsiPersistentVolume`. + */ +export interface IscsiPersistentVolumeProps extends PersistentVolumeProps { + + /** + * chapAuthDiscovery defines whether support iSCSI Discovery CHAP authentication + * + * @default false + */ + readonly chapAuthDiscovery?: boolean; + + /** + * chapAuthSession defines whether support iSCSI Session CHAP authentication + * + * @default false + */ + readonly chapAuthSession?: boolean; + + /** + * fsType is the filesystem type of the volume that you want to mount. + * + * Tip: * Ensure that the filesystem type is supported by the host operating + * system. + * + * Examples: "ext4", "xfs", "ntfs". + * + * @see https://kubernetes.io/docs/concepts/storage/volumes#iscsi + * @default 'ext4' + */ + readonly fsType?: string; + + /** + * initiatorName is the custom iSCSI Initiator Name. If initiatorName is + * specified with iscsiInterface simultaneously, new iSCSI interface : will be created for the connection. + */ + readonly initiatorName?: string; + + /** + * iqn is Target iSCSI Qualified Name. + */ + readonly iqn?: string; + + /** + * iscsiInterface is the interface Name that uses an iSCSI transport. Defaults to 'default' (tcp). + * + * @default 'default' + */ + readonly iscsiInterface?: string; + + /** + * lun is iSCSI Target Lun number. + */ + readonly lun?: number; + + /** + * portals is the iSCSI Target Portal List. The Portal is either an IP or + * ip_addr:port if the port is other than default (typically TCP ports 860 + * and 3260). + */ + readonly portals?: string[]; + + /** + * readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. + */ + readonly readOnly?: boolean; + + /** + * secretRef is the CHAP Secret for iSCSI target and initiator authentication + */ + readonly secretRef?: secret.ISecret; + + /** + * targetPortal is iSCSI Target Portal. The Portal is either an IP or + * ip_addr:port if the port is other than default (typically TCP ports 860 + * and 3260). + */ + readonly targetPortal?: string; + +} + +/** + * Iscsi + * + * @see https://kubernetes.io/docs/concepts/storage/volumes#iscsi + */ +export class IscsiPersistentVolume extends PersistentVolume { + + + constructor(scope: Construct, id: string, props: IscsiPersistentVolumeProps) { + super(scope, id, props); + + this.chapAuthDiscovery = props.chapAuthDiscovery; + this.chapAuthSession = props.chapAuthSession; + this.fsType = props.fsType; + this.initiatorName = props.initiatorName; + this.iqn = props.iqn,; + this.iscsiInterface = props.iscsiInterface; + this.lun = props.lun,; + this.portals = props.portals; + this.readOnly = props.readOnly; + this.secretRef = props.secretRef; + this.targetPortal = props.targetPortal; + + } + + /** + * @internal + * + * @see https://github.com/kubernetes/examples/tree/master/volumes/iscsi + */ + public _toKube(): k8s.PersistentVolumeSpec { + const spec = super._toKube(); + return { + ...spec, + iscsi: { + chapAuthDiscovery: this.chapAuthDiscovery ? { this.chapAuthDiscovery } : undefined, + chapAuthSession: this.chapAuthSession ? { this.chapAuthSession } : undefined, + fsType: this.fsType ? { this.fsType } : undefined, + initiatorName: this.initiatorName ? { this.initiatorName } : undefined, + iqn: this.iqn, + iscsiInterface: this.iscsiInterface ? { this.iscsiInterface } : undefined, + lun: this.lun, + portals: this.portals ? { this.portals } : undefined, + readOnly: this.readOnly ? { this.readOnly } : undefined, + secretRef: this.secretRef ? { this.secretRef } : undefined, + targetPortal: this.targetPortal + }, + }; + } +} From f0f5fe4b43f89ea38d7115273bfdee400dc5d027 Mon Sep 17 00:00:00 2001 From: Julien DOCHE Date: Fri, 10 Nov 2023 15:03:36 +0100 Subject: [PATCH 2/6] Remove defaults and fix constructor Signed-off-by: Julien DOCHE --- src/pv.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/pv.ts b/src/pv.ts index 062d784c2..b79738bb2 100644 --- a/src/pv.ts +++ b/src/pv.ts @@ -609,15 +609,11 @@ export interface IscsiPersistentVolumeProps extends PersistentVolumeProps { /** * chapAuthDiscovery defines whether support iSCSI Discovery CHAP authentication - * - * @default false */ readonly chapAuthDiscovery?: boolean; /** * chapAuthSession defines whether support iSCSI Session CHAP authentication - * - * @default false */ readonly chapAuthSession?: boolean; @@ -630,7 +626,6 @@ export interface IscsiPersistentVolumeProps extends PersistentVolumeProps { * Examples: "ext4", "xfs", "ntfs". * * @see https://kubernetes.io/docs/concepts/storage/volumes#iscsi - * @default 'ext4' */ readonly fsType?: string; @@ -648,8 +643,6 @@ export interface IscsiPersistentVolumeProps extends PersistentVolumeProps { /** * iscsiInterface is the interface Name that uses an iSCSI transport. Defaults to 'default' (tcp). - * - * @default 'default' */ readonly iscsiInterface?: string; @@ -699,9 +692,9 @@ export class IscsiPersistentVolume extends PersistentVolume { this.chapAuthSession = props.chapAuthSession; this.fsType = props.fsType; this.initiatorName = props.initiatorName; - this.iqn = props.iqn,; + this.iqn = props.iqn; this.iscsiInterface = props.iscsiInterface; - this.lun = props.lun,; + this.lun = props.lun; this.portals = props.portals; this.readOnly = props.readOnly; this.secretRef = props.secretRef; From 11daf0ca698a885146e1a6e3488f713ab5c86af2 Mon Sep 17 00:00:00 2001 From: Julien DOCHE Date: Sat, 11 Nov 2023 11:26:03 +0100 Subject: [PATCH 3/6] Fix iscsi and namespace pvc Signed-off-by: Julien DOCHE --- src/pv.ts | 98 +++++++++++++++++++++++++++++++++++++++++++++--------- src/pvc.ts | 11 +++++- 2 files changed, 93 insertions(+), 16 deletions(-) diff --git a/src/pv.ts b/src/pv.ts index b79738bb2..a86907598 100644 --- a/src/pv.ts +++ b/src/pv.ts @@ -211,7 +211,8 @@ export class PersistentVolume extends base.Resource implements IPersistentVolume */ public reserve(): pvc.PersistentVolumeClaim { const claim = new pvc.PersistentVolumeClaim(this, `${this.name}PVC`, { - metadata: { name: `pvc-${this.name}`, namespace: this.metadata.namespace }, + metadata: { name: `pvc-${this.name}` }, + namespace: this.metadata.namespace, // the storage classes must match, otherwise the claim // will use the default class (or no class at all), which may be different than the class @@ -235,7 +236,7 @@ export class PersistentVolume extends base.Resource implements IPersistentVolume * @param claim The PVC to bind to. */ public bind(claim: pvc.IPersistentVolumeClaim) { - if (this._claim && this._claim.name !== claim.name) { + if (this._claim && (this._claim.name !== claim.name || this._claim.namespace !== claim.namespace)) { throw new Error(`Cannot bind volume '${this.name}' to claim '${claim.name}' since it is already bound to claim '${this._claim.name}'`); } this._claim = claim; @@ -639,7 +640,7 @@ export interface IscsiPersistentVolumeProps extends PersistentVolumeProps { /** * iqn is Target iSCSI Qualified Name. */ - readonly iqn?: string; + readonly iqn: string; /** * iscsiInterface is the interface Name that uses an iSCSI transport. Defaults to 'default' (tcp). @@ -649,7 +650,7 @@ export interface IscsiPersistentVolumeProps extends PersistentVolumeProps { /** * lun is iSCSI Target Lun number. */ - readonly lun?: number; + readonly lun: number; /** * portals is the iSCSI Target Portal List. The Portal is either an IP or @@ -673,7 +674,7 @@ export interface IscsiPersistentVolumeProps extends PersistentVolumeProps { * ip_addr:port if the port is other than default (typically TCP ports 860 * and 3260). */ - readonly targetPortal?: string; + readonly targetPortal: string; } @@ -684,6 +685,73 @@ export interface IscsiPersistentVolumeProps extends PersistentVolumeProps { */ export class IscsiPersistentVolume extends PersistentVolume { + /** + * chapAuthDiscovery defines whether support iSCSI Discovery CHAP authentication + */ + readonly chapAuthDiscovery?: boolean; + + /** + * chapAuthSession defines whether support iSCSI Session CHAP authentication + */ + readonly chapAuthSession?: boolean; + + /** + * fsType is the filesystem type of the volume that you want to mount. + * + * Tip: * Ensure that the filesystem type is supported by the host operating + * system. + * + * Examples: "ext4", "xfs", "ntfs". + * + * @see https://kubernetes.io/docs/concepts/storage/volumes#iscsi + */ + readonly fsType?: string; + + /** + * initiatorName is the custom iSCSI Initiator Name. If initiatorName is + * specified with iscsiInterface simultaneously, new iSCSI interface : will be created for the connection. + */ + readonly initiatorName?: string; + + /** + * iqn is Target iSCSI Qualified Name. + */ + readonly iqn: string; + + /** + * iscsiInterface is the interface Name that uses an iSCSI transport. Defaults to 'default' (tcp). + */ + readonly iscsiInterface?: string; + + /** + * lun is iSCSI Target Lun number. + */ + readonly lun: number; + + /** + * portals is the iSCSI Target Portal List. The Portal is either an IP or + * ip_addr:port if the port is other than default (typically TCP ports 860 + * and 3260). + */ + readonly portals?: string[]; + + /** + * readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. + */ + readonly readOnly?: boolean; + + /** + * secretRef is the CHAP Secret for iSCSI target and initiator authentication + */ + readonly secretRef?: secret.ISecret; + + /** + * targetPortal is iSCSI Target Portal. The Portal is either an IP or + * ip_addr:port if the port is other than default (typically TCP ports 860 + * and 3260). + */ + readonly targetPortal: string; constructor(scope: Construct, id: string, props: IscsiPersistentVolumeProps) { super(scope, id, props); @@ -712,18 +780,18 @@ export class IscsiPersistentVolume extends PersistentVolume { return { ...spec, iscsi: { - chapAuthDiscovery: this.chapAuthDiscovery ? { this.chapAuthDiscovery } : undefined, - chapAuthSession: this.chapAuthSession ? { this.chapAuthSession } : undefined, - fsType: this.fsType ? { this.fsType } : undefined, - initiatorName: this.initiatorName ? { this.initiatorName } : undefined, + chapAuthDiscovery: this.chapAuthDiscovery ? this.chapAuthDiscovery : undefined, + chapAuthSession: this.chapAuthSession ? this.chapAuthSession : undefined, + fsType: this.fsType ? this.fsType : undefined, + initiatorName: this.initiatorName ? this.initiatorName : undefined, iqn: this.iqn, - iscsiInterface: this.iscsiInterface ? { this.iscsiInterface } : undefined, + iscsiInterface: this.iscsiInterface ? this.iscsiInterface : undefined, lun: this.lun, - portals: this.portals ? { this.portals } : undefined, - readOnly: this.readOnly ? { this.readOnly } : undefined, - secretRef: this.secretRef ? { this.secretRef } : undefined, - targetPortal: this.targetPortal - }, + portals: this.portals ? this.portals : undefined, + readOnly: this.readOnly ? this.readOnly : undefined, + secretRef: this.secretRef ? this.secretRef : undefined, + targetPortal: this.targetPortal, + }, }; } } diff --git a/src/pvc.ts b/src/pvc.ts index 25c133ae7..9683992c2 100644 --- a/src/pvc.ts +++ b/src/pvc.ts @@ -9,6 +9,8 @@ import * as pv from './pv'; */ export interface IPersistentVolumeClaim extends base.IResource { + readonly namespace?: string; + } /** @@ -69,6 +71,7 @@ export interface PersistentVolumeClaimProps extends base.ResourceProps { */ readonly volume?: pv.IPersistentVolume; + readonly namespace?: string; } class ImportedPersistentVolumeClaim extends Construct implements IPersistentVolumeClaim { @@ -145,9 +148,12 @@ export class PersistentVolumeClaim extends base.Resource implements IPersistentV private _volume?: pv.IPersistentVolume; + public readonly namespace?: string; + public constructor(scope: Construct, id: string, props: PersistentVolumeClaimProps = { }) { super(scope, id); + this.namespace = props.namespace; this.storage = props.storage; this.volumeMode = props.volumeMode ?? PersistentVolumeMode.FILE_SYSTEM; this.storageClassName = props.storageClassName; @@ -158,7 +164,10 @@ export class PersistentVolumeClaim extends base.Resource implements IPersistentV } this.apiObject = new k8s.KubePersistentVolumeClaim(this, 'Resource', { - metadata: props.metadata, + metadata: { + ...props.metadata, + namespace: this.namespace ? this.namespace : undefined, + }, spec: Lazy.any({ produce: () => this._toKube() }), }); } From 6b1da8d56e28189309cdc779e458f78d4ed2fa72 Mon Sep 17 00:00:00 2001 From: Julien DOCHE Date: Sat, 11 Nov 2023 15:08:45 +0100 Subject: [PATCH 4/6] Add namespace in bound claim Signed-off-by: Julien DOCHE --- src/pv.ts | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/pv.ts b/src/pv.ts index a86907598..732b43863 100644 --- a/src/pv.ts +++ b/src/pv.ts @@ -236,8 +236,14 @@ export class PersistentVolume extends base.Resource implements IPersistentVolume * @param claim The PVC to bind to. */ public bind(claim: pvc.IPersistentVolumeClaim) { - if (this._claim && (this._claim.name !== claim.name || this._claim.namespace !== claim.namespace)) { - throw new Error(`Cannot bind volume '${this.name}' to claim '${claim.name}' since it is already bound to claim '${this._claim.name}'`); + if ( + this._claim && + (this._claim.name !== claim.name || + this._claim.namespace !== claim.namespace) + ) { + throw new Error( + `Cannot bind volume '${this.name}' to claim '${claim.name}' since it is already bound to claim '${this._claim.name}' in namespace '${this._claim.namespace}'`, + ); } this._claim = claim; } @@ -254,8 +260,15 @@ export class PersistentVolume extends base.Resource implements IPersistentVolume const storage = this.storage ? k8s.Quantity.fromString(this.storage.toGibibytes() + 'Gi') : undefined; return { - claimRef: this._claim ? { name: this._claim?.name } : undefined, - accessModes: this.accessModes?.map(a => a.toString()), + claimRef: this._claim + ? { + name: this._claim?.name, + namespace: this._claim?.namespace, + kind: this._claim?.kind, + apiVersion: this._claim?.apiVersion, + } + : undefined, + accessModes: this.accessModes?.map((a) => a.toString()), capacity: storage ? { storage } : undefined, mountOptions: this.mountOptions?.map(o => o), storageClassName: this.storageClassName, @@ -263,7 +276,6 @@ export class PersistentVolume extends base.Resource implements IPersistentVolume volumeMode: this.mode, }; } - } /** @@ -780,8 +792,12 @@ export class IscsiPersistentVolume extends PersistentVolume { return { ...spec, iscsi: { - chapAuthDiscovery: this.chapAuthDiscovery ? this.chapAuthDiscovery : undefined, - chapAuthSession: this.chapAuthSession ? this.chapAuthSession : undefined, + chapAuthDiscovery: this.chapAuthDiscovery + ? this.chapAuthDiscovery + : undefined, + chapAuthSession: this.chapAuthSession + ? this.chapAuthSession + : undefined, fsType: this.fsType ? this.fsType : undefined, initiatorName: this.initiatorName ? this.initiatorName : undefined, iqn: this.iqn, From 03afa8327b44b489d8c53b0508f092302fabd4d1 Mon Sep 17 00:00:00 2001 From: Julien DOCHE Date: Sun, 12 Nov 2023 12:20:31 +0100 Subject: [PATCH 5/6] Add ability to specify reserve claim namespace Signed-off-by: Julien DOCHE --- src/pv.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pv.ts b/src/pv.ts index 732b43863..ea63512fe 100644 --- a/src/pv.ts +++ b/src/pv.ts @@ -209,11 +209,10 @@ export class PersistentVolume extends base.Resource implements IPersistentVolume * * @see https://kubernetes.io/docs/concepts/storage/persistent-volumes/#reserving-a-persistentvolume */ - public reserve(): pvc.PersistentVolumeClaim { + public reserve(namespace?: string): pvc.PersistentVolumeClaim { const claim = new pvc.PersistentVolumeClaim(this, `${this.name}PVC`, { metadata: { name: `pvc-${this.name}` }, - namespace: this.metadata.namespace, - + namespace: namespace ? namespace : this.metadata.namespace, // the storage classes must match, otherwise the claim // will use the default class (or no class at all), which may be different than the class // of this volume. note that other requirements are not needed since From 988652b92f8b5e53095e148dc269f96d118a1733 Mon Sep 17 00:00:00 2001 From: Julien DOCHE Date: Sun, 12 Nov 2023 12:37:42 +0100 Subject: [PATCH 6/6] Cleanup Signed-off-by: Julien DOCHE --- src/pv.ts | 110 ++++------------------ test/__snapshots__/container.test.ts.snap | 2 + test/__snapshots__/pv.test.ts.snap | 8 ++ 3 files changed, 26 insertions(+), 94 deletions(-) diff --git a/src/pv.ts b/src/pv.ts index ea63512fe..37bc72eb9 100644 --- a/src/pv.ts +++ b/src/pv.ts @@ -3,8 +3,8 @@ import { Construct } from 'constructs'; import * as base from './base'; import * as k8s from './imports/k8s'; import * as pvc from './pvc'; -import * as volume from './volume'; import * as secret from './secret'; +import * as volume from './volume'; /** * Contract of a `PersistentVolumeClaim`. @@ -696,89 +696,11 @@ export interface IscsiPersistentVolumeProps extends PersistentVolumeProps { */ export class IscsiPersistentVolume extends PersistentVolume { - /** - * chapAuthDiscovery defines whether support iSCSI Discovery CHAP authentication - */ - readonly chapAuthDiscovery?: boolean; - - /** - * chapAuthSession defines whether support iSCSI Session CHAP authentication - */ - readonly chapAuthSession?: boolean; - - /** - * fsType is the filesystem type of the volume that you want to mount. - * - * Tip: * Ensure that the filesystem type is supported by the host operating - * system. - * - * Examples: "ext4", "xfs", "ntfs". - * - * @see https://kubernetes.io/docs/concepts/storage/volumes#iscsi - */ - readonly fsType?: string; - - /** - * initiatorName is the custom iSCSI Initiator Name. If initiatorName is - * specified with iscsiInterface simultaneously, new iSCSI interface : will be created for the connection. - */ - readonly initiatorName?: string; - - /** - * iqn is Target iSCSI Qualified Name. - */ - readonly iqn: string; - - /** - * iscsiInterface is the interface Name that uses an iSCSI transport. Defaults to 'default' (tcp). - */ - readonly iscsiInterface?: string; - - /** - * lun is iSCSI Target Lun number. - */ - readonly lun: number; - - /** - * portals is the iSCSI Target Portal List. The Portal is either an IP or - * ip_addr:port if the port is other than default (typically TCP ports 860 - * and 3260). - */ - readonly portals?: string[]; - - /** - * readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. - */ - readonly readOnly?: boolean; - - /** - * secretRef is the CHAP Secret for iSCSI target and initiator authentication - */ - readonly secretRef?: secret.ISecret; - - /** - * targetPortal is iSCSI Target Portal. The Portal is either an IP or - * ip_addr:port if the port is other than default (typically TCP ports 860 - * and 3260). - */ - readonly targetPortal: string; + readonly props: IscsiPersistentVolumeProps; constructor(scope: Construct, id: string, props: IscsiPersistentVolumeProps) { super(scope, id, props); - - this.chapAuthDiscovery = props.chapAuthDiscovery; - this.chapAuthSession = props.chapAuthSession; - this.fsType = props.fsType; - this.initiatorName = props.initiatorName; - this.iqn = props.iqn; - this.iscsiInterface = props.iscsiInterface; - this.lun = props.lun; - this.portals = props.portals; - this.readOnly = props.readOnly; - this.secretRef = props.secretRef; - this.targetPortal = props.targetPortal; - + this.props = props; } /** @@ -791,21 +713,21 @@ export class IscsiPersistentVolume extends PersistentVolume { return { ...spec, iscsi: { - chapAuthDiscovery: this.chapAuthDiscovery - ? this.chapAuthDiscovery + chapAuthDiscovery: this.props.chapAuthDiscovery + ? this.props.chapAuthDiscovery : undefined, - chapAuthSession: this.chapAuthSession - ? this.chapAuthSession + chapAuthSession: this.props.chapAuthSession + ? this.props.chapAuthSession : undefined, - fsType: this.fsType ? this.fsType : undefined, - initiatorName: this.initiatorName ? this.initiatorName : undefined, - iqn: this.iqn, - iscsiInterface: this.iscsiInterface ? this.iscsiInterface : undefined, - lun: this.lun, - portals: this.portals ? this.portals : undefined, - readOnly: this.readOnly ? this.readOnly : undefined, - secretRef: this.secretRef ? this.secretRef : undefined, - targetPortal: this.targetPortal, + fsType: this.props.fsType ? this.props.fsType : undefined, + initiatorName: this.props.initiatorName ? this.props.initiatorName : undefined, + iqn: this.props.iqn, + iscsiInterface: this.props.iscsiInterface ? this.props.iscsiInterface : undefined, + lun: this.props.lun, + portals: this.props.portals ? this.props.portals : undefined, + readOnly: this.props.readOnly ? this.props.readOnly : undefined, + secretRef: this.props.secretRef ? this.props.secretRef : undefined, + targetPortal: this.props.targetPortal, }, }; } diff --git a/test/__snapshots__/container.test.ts.snap b/test/__snapshots__/container.test.ts.snap index 5271e3314..d27674766 100644 --- a/test/__snapshots__/container.test.ts.snap +++ b/test/__snapshots__/container.test.ts.snap @@ -257,6 +257,8 @@ Array [ "volumeID": "vol", }, "claimRef": Object { + "apiVersion": "v1", + "kind": "PersistentVolumeClaim", "name": "pvc-test-pv-c8b2a2c6", }, "persistentVolumeReclaimPolicy": "Retain", diff --git a/test/__snapshots__/pv.test.ts.snap b/test/__snapshots__/pv.test.ts.snap index 0f4cc5688..74cebed3f 100644 --- a/test/__snapshots__/pv.test.ts.snap +++ b/test/__snapshots__/pv.test.ts.snap @@ -149,6 +149,8 @@ Array [ "volumeID": "vol1", }, "claimRef": Object { + "apiVersion": "v1", + "kind": "PersistentVolumeClaim", "name": "claim", }, "persistentVolumeReclaimPolicy": "Retain", @@ -173,6 +175,8 @@ Array [ "volumeID": "vol1", }, "claimRef": Object { + "apiVersion": "v1", + "kind": "PersistentVolumeClaim", "name": "claim", }, "persistentVolumeReclaimPolicy": "Retain", @@ -197,6 +201,8 @@ Array [ "volumeID": "vol1", }, "claimRef": Object { + "apiVersion": "v1", + "kind": "PersistentVolumeClaim", "name": "pvc-test-volume-c8db061e", }, "persistentVolumeReclaimPolicy": "Retain", @@ -234,6 +240,8 @@ Array [ "volumeID": "vol1", }, "claimRef": Object { + "apiVersion": "v1", + "kind": "PersistentVolumeClaim", "name": "pvc-test-volume-c8db061e", }, "persistentVolumeReclaimPolicy": "Retain",