diff --git a/.github/workflows/static.yaml b/.github/workflows/static.yaml index 1d887fb2..9efaaa44 100644 --- a/.github/workflows/static.yaml +++ b/.github/workflows/static.yaml @@ -12,7 +12,7 @@ jobs: - name: Run linter uses: golangci/golangci-lint-action@v2 with: - version: v1.29 + version: v1.31 args: -E=gofmt,deadcode,unused,varcheck,ineffassign,golint,misspell --timeout=30m0s verify-helm: name: Verify Helm diff --git a/charts/README.md b/charts/README.md index 216b82f9..3edef385 100644 --- a/charts/README.md +++ b/charts/README.md @@ -40,6 +40,7 @@ The following table lists the configurable parameters of the latest NFS CSI Driv | `driver.name` | alternative driver name | `nfs.csi.k8s.io` | | `driver.mountPermissions` | mounted folder permissions name | `0777` | `feature.enableFSGroupPolicy` | enable `fsGroupPolicy` on a k8s 1.20+ cluster | `false` | +| `feature.enableInlineVolume` | enable inline volume | `true` | | `image.nfs.repository` | csi-driver-nfs image | `mcr.microsoft.com/k8s/csi/nfs-csi` | | `image.nfs.tag` | csi-driver-nfs image tag | `latest` | | `image.nfs.pullPolicy` | csi-driver-nfs image pull policy | `IfNotPresent` | diff --git a/charts/latest/csi-driver-nfs-v3.1.0.tgz b/charts/latest/csi-driver-nfs-v3.1.0.tgz index f2247d23..30095df0 100644 Binary files a/charts/latest/csi-driver-nfs-v3.1.0.tgz and b/charts/latest/csi-driver-nfs-v3.1.0.tgz differ diff --git a/charts/latest/csi-driver-nfs/templates/csi-nfs-driverinfo.yaml b/charts/latest/csi-driver-nfs/templates/csi-nfs-driverinfo.yaml index ca50cb5a..231607a6 100644 --- a/charts/latest/csi-driver-nfs/templates/csi-nfs-driverinfo.yaml +++ b/charts/latest/csi-driver-nfs/templates/csi-nfs-driverinfo.yaml @@ -6,6 +6,9 @@ spec: attachRequired: false volumeLifecycleModes: - Persistent + {{- if .Values.feature.enableInlineVolume}} + - Ephemeral + {{- end}} {{- if .Values.feature.enableFSGroupPolicy}} fsGroupPolicy: File {{- end}} diff --git a/charts/latest/csi-driver-nfs/values.yaml b/charts/latest/csi-driver-nfs/values.yaml index aca7edd3..7e5a8f5f 100755 --- a/charts/latest/csi-driver-nfs/values.yaml +++ b/charts/latest/csi-driver-nfs/values.yaml @@ -30,6 +30,7 @@ driver: feature: enableFSGroupPolicy: false + enableInlineVolume: true controller: name: csi-nfs-controller diff --git a/deploy/csi-nfs-driverinfo.yaml b/deploy/csi-nfs-driverinfo.yaml index 50874c9a..3ac1e317 100644 --- a/deploy/csi-nfs-driverinfo.yaml +++ b/deploy/csi-nfs-driverinfo.yaml @@ -7,3 +7,4 @@ spec: attachRequired: false volumeLifecycleModes: - Persistent + - Ephemeral diff --git a/deploy/example/nginx-pod-inline-volume.yaml b/deploy/example/nginx-pod-inline-volume.yaml new file mode 100644 index 00000000..2ac79d33 --- /dev/null +++ b/deploy/example/nginx-pod-inline-volume.yaml @@ -0,0 +1,25 @@ +--- +kind: Pod +apiVersion: v1 +metadata: + name: nginx-pod-inline-volume +spec: + nodeSelector: + "kubernetes.io/os": linux + containers: + - image: mcr.microsoft.com/oss/nginx/nginx:1.19.5 + name: nginx-nfs + command: + - "/bin/bash" + - "-c" + - set -euo pipefail; while true; do echo $(date) >> /mnt/nfs/outfile; sleep 1; done + volumeMounts: + - name: persistent-storage + mountPath: "/mnt/nfs" + volumes: + - name: persistent-storage + csi: + driver: nfs.csi.k8s.io + volumeAttributes: + server: nfs-server.default.svc.cluster.local # required + share: / # required diff --git a/hack/verify-examples.sh b/hack/verify-examples.sh index aac53e58..3ff485a0 100755 --- a/hack/verify-examples.sh +++ b/hack/verify-examples.sh @@ -20,7 +20,7 @@ rollout_and_wait() { trap "echo \"Failed to apply config \\\"$1\\\"\" >&2" err APPNAME=$(kubectl apply -f $1 | grep -E "^(:?daemonset|deployment|statefulset|pod)" | awk '{printf $1}') - if [[ -n $(expr "${APPNAME}" : "\(daemonset\|deployment\|statefulset\)" || true) ]]; then + if [[ -n $(expr "${APPNAME}" : "\(daemonset\|deployment\|statefulset\|pod\)" || true) ]]; then kubectl rollout status $APPNAME --watch --timeout=5m else kubectl wait "${APPNAME}" --for condition=ready --timeout=5m diff --git a/test/e2e/dynamic_provisioning_test.go b/test/e2e/dynamic_provisioning_test.go index 700ccebd..3dd157e7 100644 --- a/test/e2e/dynamic_provisioning_test.go +++ b/test/e2e/dynamic_provisioning_test.go @@ -257,6 +257,32 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() { } test.Run(cs, ns) }) + + ginkgo.It("should create a CSI inline volume [nfs.csi.k8s.io]", func() { + pods := []testsuites.PodDetails{ + { + Cmd: convertToPowershellCommandIfNecessary("echo 'hello world' > /mnt/test-1/data && grep 'hello world' /mnt/test-1/data"), + Volumes: []testsuites.VolumeDetails{ + { + ClaimSize: "10Gi", + VolumeMount: testsuites.VolumeMountDetails{ + NameGenerate: "test-volume-", + MountPathGenerate: "/mnt/test-", + }, + }, + }, + }, + } + + test := testsuites.DynamicallyProvisionedInlineVolumeTest{ + CSIDriver: testDriver, + Pods: pods, + Server: nfsServerAddress, + Share: nfsShare, + ReadOnly: false, + } + test.Run(cs, ns) + }) }) func restClient(group string, version string) (restclientset.Interface, error) { diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index be5e9c28..2660eb84 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -37,6 +37,8 @@ import ( const ( kubeconfigEnvVar = "KUBECONFIG" testWindowsEnvVar = "TEST_WINDOWS" + nfsServerAddress = "nfs-server.default.svc.cluster.local" + nfsShare = "/" ) var ( @@ -44,8 +46,8 @@ var ( nfsDriver *nfs.Driver isWindowsCluster = os.Getenv(testWindowsEnvVar) != "" defaultStorageClassParameters = map[string]string{ - "server": "nfs-server.default.svc.cluster.local", - "share": "/", + "server": nfsServerAddress, + "share": nfsShare, "csi.storage.k8s.io/provisioner-secret-name": "mount-options", "csi.storage.k8s.io/provisioner-secret-namespace": "default", } diff --git a/test/e2e/testsuites/dynamically_provisioned_inline_volume.go b/test/e2e/testsuites/dynamically_provisioned_inline_volume.go new file mode 100644 index 00000000..eadacd13 --- /dev/null +++ b/test/e2e/testsuites/dynamically_provisioned_inline_volume.go @@ -0,0 +1,53 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package testsuites + +import ( + "github.com/kubernetes-csi/csi-driver-nfs/test/e2e/driver" + "github.com/onsi/ginkgo" + v1 "k8s.io/api/core/v1" + clientset "k8s.io/client-go/kubernetes" +) + +// DynamicallyProvisionedInlineVolumeTest will provision required server, share +// Waiting for the PV provisioner to create an inline volume +// Testing if the Pod(s) Cmd is run with a 0 exit code +type DynamicallyProvisionedInlineVolumeTest struct { + CSIDriver driver.DynamicPVTestDriver + Pods []PodDetails + Server string + Share string + ReadOnly bool +} + +func (t *DynamicallyProvisionedInlineVolumeTest) Run(client clientset.Interface, namespace *v1.Namespace) { + for _, pod := range t.Pods { + var tpod *TestPod + var cleanup []func() + tpod, cleanup = pod.SetupWithCSIInlineVolumes(client, namespace, t.CSIDriver, t.Server, t.Share, t.ReadOnly) + // defer must be called here for resources not get removed before using them + for i := range cleanup { + defer cleanup[i]() + } + + ginkgo.By("deploying the pod") + tpod.Create() + defer tpod.Cleanup() + ginkgo.By("checking that the pods command exits with no error") + tpod.WaitForSuccess() + } +} diff --git a/test/e2e/testsuites/specs.go b/test/e2e/testsuites/specs.go index 3c52a639..025fed5f 100644 --- a/test/e2e/testsuites/specs.go +++ b/test/e2e/testsuites/specs.go @@ -123,6 +123,15 @@ func (pod *PodDetails) SetupWithDynamicVolumes(client clientset.Interface, names return tpod, cleanupFuncs } +func (pod *PodDetails) SetupWithCSIInlineVolumes(client clientset.Interface, namespace *v1.Namespace, csiDriver driver.DynamicPVTestDriver, server, share string, readOnly bool) (*TestPod, []func()) { + tpod := NewTestPod(client, namespace, pod.Cmd) + cleanupFuncs := make([]func(), 0) + for n, v := range pod.Volumes { + tpod.SetupCSIInlineVolume(fmt.Sprintf("%s%d", v.VolumeMount.NameGenerate, n+1), fmt.Sprintf("%s%d", v.VolumeMount.MountPathGenerate, n+1), server, share, readOnly) + } + return tpod, cleanupFuncs +} + func (pod *PodDetails) SetupDeployment(client clientset.Interface, namespace *v1.Namespace, csiDriver driver.DynamicPVTestDriver, storageClassParameters map[string]string) (*TestDeployment, []func()) { cleanupFuncs := make([]func(), 0) volume := pod.Volumes[0] diff --git a/test/e2e/testsuites/testsuites.go b/test/e2e/testsuites/testsuites.go index c9812274..a7d8f730 100644 --- a/test/e2e/testsuites/testsuites.go +++ b/test/e2e/testsuites/testsuites.go @@ -595,3 +595,27 @@ func (t *TestPod) SetupVolumeMountWithSubpath(pvc *v1.PersistentVolumeClaim, nam t.pod.Spec.Volumes = append(t.pod.Spec.Volumes, volume) } + +func (t *TestPod) SetupCSIInlineVolume(name, mountPath, server, share string, readOnly bool) { + volumeMount := v1.VolumeMount{ + Name: name, + MountPath: mountPath, + ReadOnly: readOnly, + } + t.pod.Spec.Containers[0].VolumeMounts = append(t.pod.Spec.Containers[0].VolumeMounts, volumeMount) + + volume := v1.Volume{ + Name: name, + VolumeSource: v1.VolumeSource{ + CSI: &v1.CSIVolumeSource{ + Driver: nfs.DefaultDriverName, + VolumeAttributes: map[string]string{ + "server": server, + "share": share, + }, + ReadOnly: &readOnly, + }, + }, + } + t.pod.Spec.Volumes = append(t.pod.Spec.Volumes, volume) +}