Repository: cdk8s-team/cdk8s-plus (ts)

Issue:
Add ability to create NFS Volumes
<!--Please make sure to read the "Reporting Issues" section of the contribution guide before submitting an issue
https://github.com/cdk8s-team/cdk8s/blob/master/CONTRIBUTING.md#reporting-issues
-->

### Description of the feature or enhancement:
NFS Volumes

### Use Case:
I need to mount NFS volumes to my pods. Unfortunately, due to how the `Volume` class is set up, I cannot easily create an NFS volume to pass into the `Deployment` construct.

My current workaround for this is the following:
```
const volume = Volume.fromEmptyDir(this, 'data-volume', 'data-volume', { });
const obj: any = volume as any;
obj.config = {
    nfs: {
        server: '169.254.0.1'
    }
};
```

### Proposed Solution:
 - Add a new static constructor on the `Volume` class to support creating NFS volumes. The underlying `k8s.Volume` already has `nfs` property defined on it.
 - To support other volume types that do not have static constructors defined, I suggest making the `Volume` class constructor public or protected, so that users are left with flexibility to pass in their own configuration objects if they have to.

### Other:
I can help put together a PR if needed

* [X] :wave: I may be able to implement this feature request
* [ ] :warning: This feature might incur a breaking change

---

This is a :rocket: Feature Request

Pull request description:
feat(volume): nfs volume support (#2300)
Fixes #2300 

Patch:
diff --git a/src/volume.ts b/src/volume.ts
index a881f4b5..9e3c50ad 100644
--- a/src/volume.ts
+++ b/src/volume.ts
@@ -221,6 +221,21 @@ export class Volume extends Construct implements IStorage {
     });
   }
 
+  /**
+   * Used to mount an NFS share into a Pod.
+   *
+   * @see https://kubernetes.io/docs/concepts/storage/volumes/#nfs
+   */
+  public static fromNfs(scope: Construct, id: string, name: string, options: NfsVolumeOptions): Volume {
+    return new Volume(scope, id, name, {
+      nfs: {
+        server: options.server,
+        path: options.path,
+        readOnly: options.readOnly,
+      },
+    });
+  }
+
   /**
    * Populate the volume from a CSI driver, for example the Secrets Store CSI
    * Driver: https://secrets-store-csi-driver.sigs.k8s.io/introduction.html.
@@ -680,6 +695,29 @@ export enum HostPathVolumeType {
   BLOCK_DEVICE = 'BlockDevice'
 }
 
+/**
+ * Options for the NFS based volume.
+ */
+export interface NfsVolumeOptions {
+
+  /**
+   * Server is the hostname or IP address of the NFS server.
+   */
+  readonly server: string;
+
+  /**
+   * If set to true, will force the NFS export to be mounted with read-only permissions.
+   *
+   * @default - false
+   */
+  readonly readOnly?: boolean;
+
+  /**
+   * Path that is exported by the NFS server
+   */
+  readonly path: string;
+}
+
 /**
  * Options for the CSI driver based volume.
  */


Test patch:
diff --git a/test/volume.test.ts b/test/volume.test.ts
index 1a931b62..f30ae887 100644
--- a/test/volume.test.ts
+++ b/test/volume.test.ts
@@ -577,6 +577,48 @@ describe('fromHostPath', () => {
 
 });
 
+describe('fromNfs', () => {
+
+  test('defaults', () => {
+
+    const chart = Testing.chart();
+    const volume = Volume.fromNfs(chart, 'Volume', 'disk', {
+      server: '169.254.1.1',
+      path: '/nfs/path',
+    });
+    const spec = volume._toKube();
+    expect(spec).toEqual({
+      name: 'disk',
+      nfs: {
+        server: '169.254.1.1',
+        path: '/nfs/path',
+      },
+    });
+
+  });
+
+  test('custom', () => {
+
+    const chart = Testing.chart();
+    const volume = Volume.fromNfs(chart, 'Volume', 'disk', {
+      server: '169.254.1.1',
+      path: '/nfs/path',
+      readOnly: true,
+    });
+    const spec = volume._toKube();
+    expect(spec).toEqual({
+      name: 'disk',
+      nfs: {
+        server: '169.254.1.1',
+        path: '/nfs/path',
+        readOnly: true,
+      },
+    });
+
+  });
+
+});
+
 describe('fromCsi', () => {
   test('minimal definition', () => {
     // GIVEN