Change:
See This happens on ubuntu images on GKE when the node ephemeral storage is on local SSD. /sig storage /assign
/triage accepted

Discussion:
// VolumeDynamicallyCreatedByKey is the key of the annotation on PersistentVolume // object created dynamically VolumeDynamicallyCreatedByKey = "kubernetes.io/createdby" <ins> // kubernetesPluginPathPrefix is the prefix of kubernetes plugin mount paths. kubernetesPluginPathPrefix = "/plugins/kubernetes.io/" </ins> ) // IsReady checks for the existence of a regular file

// In GCI cluster, if gci mounter is used for mounting, the container started by mounter // script will cause additional mounts created in the container. Since these mounts are // irrelevant to the original mounts, they should be not considered when checking the <del> // mount references. Current solution is to filter out those mount paths that contain // the string of original mount path. // Plan to work on better approach to solve this issue. </del> <ins> // mount references. The current solution is to filter out those mount paths that contain // the k8s plugin suffix of original mount path. </ins> func HasMountRefs(mountPath string, mountRefs []string) bool { <ins> // A mountPath typically is like //   /var/lib/kubelet/plugins/kubernetes.io/some-plugin/mounts/volume-XXXX // Mount refs can look like //   /home/somewhere/var/lib/kubelet/plugins/kubernetes.io/some-plugin/... // but if /var/lib/kubelet is mounted to a different device a ref might be like //   /mnt/some-other-place/kubelet/plugins/kubernetes.io/some-plugin/... // Neither of the above should be counted as a mount ref as those are handled // by the kubelet. What we're concerned about is a path like //   /data/local/some/manual/mount // As unmonting could interrupt usage from that mountpoint. // // So instead of looking for the entire /var/lib/... path, the plugins/kuberentes.io/ // suffix is trimmed off and searched for. // // If there isn't a /plugins/... path, the whole mountPath is used instead. pathToFind := mountPath if i := strings.Index(mountPath, kubernetesPluginPathPrefix); i > -1 { pathToFind = mountPath[i:] } </ins> for _, ref := range mountRefs { <del> if !strings.Contains(ref, mountPath) { </del> <ins> if !strings.Contains(ref, pathToFind) { </ins> return true } }

} } <ins> func TestHasMountRefs(t *testing.T) { testCases := map[string]struct { mountPath string mountRefs []string expected  bool }{ "plugin mounts only": { mountPath: "/var/lib/kubelet/plugins/kubernetes.io/some-plugin/mounts/volume-XXXX", mountRefs: []string{ "/home/somewhere/var/lib/kubelet/plugins/kubernetes.io/some-plugin/mounts/volume-XXXX", "/var/lib/kubelet/plugins/kubernetes.io/some-plugin/mounts/volume-XXXX", "/mnt/kubelet/plugins/kubernetes.io/some-plugin/mounts/volume-XXXX", "/mnt/plugins/kubernetes.io/some-plugin/mounts/volume-XXXX", }, expected: false, }, "extra local mount": { mountPath: "/var/lib/kubelet/plugins/kubernetes.io/some-plugin/mounts/volume-XXXX", mountRefs: []string{ "/home/somewhere/var/lib/kubelet/plugins/kubernetes.io/some-plugin/mounts/volume-XXXX", "/local/data/kubernetes.io/some-plugin/mounts/volume-XXXX", "/mnt/kubelet/plugins/kubernetes.io/some-plugin/mounts/volume-XXXX", "/mnt/plugins/kubernetes.io/some-plugin/mounts/volume-XXXX", }, expected: true, }, } for name, test := range testCases { actual := HasMountRefs(test.mountPath, test.mountRefs) if actual != test.expected { t.Errorf("for %s expected %v but got %v", name, test.expected, actual) } } } </ins> func TestMountOptionFromSpec(t *testing.T) { scenarios := map[string]struct { volume            *volume.Spec