Merge pull request #55112 from mtanino/pr/BlockVolumesSupport-cmdline

Automatic merge from submit-queue (batch tested with PRs 55112, 56029, 55740, 56095, 55845). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Block volume: Command line printer update

**What this PR does / why we need it**:

Add cmdline printer support changes.

**Which issue this PR fixes**: 
Based on this proposal (kubernetes/community#805 & kubernetes/community#1265) and this feature issue: kubernetes/features#351

**Special notes for your reviewer**:

There are another PRs related to this functionality.

(#50457) API Change
(#53385) VolumeMode PV-PVC Binding change
(#51494) Container runtime interface change, volumemanager changes, operationexecutor changes
(#55112) Block volume: Command line printer update
Plugins
(#51493) Block volumes Support: FC plugin update
(#54752) Block volumes Support: iSCSI plugin update

**Release note**:

```
NONE
```

/sig storage
/cc @msau42  @jsafrane @saad-ali  @erinboyd @screeley44 
@kubernetes/sig-storage-pr-reviews


- Command results
```
~/sample/storage/fc_loop/file % k get pv,pvc,pod                
NAME              CAPACITY   ACCESS MODES   VOLUME MODE   RECLAIM POLICY   STATUS    CLAIM                       STORAGECLASS   REASON    AGE
pv/block-pv0001   1Gi        RWO            Block         Retain           Bound     default/nginx-block-pvc01   slow                     2m
pv/file-pv0001    1Gi        RWO            Filesystem    Retain           Bound     default/nginx-file-pvc01    slow                     24s

NAME                    STATUS    VOLUME         CAPACITY   ACCESS MODES   VOLUME MODE   STORAGECLASS   AGE
pvc/nginx-block-pvc01   Bound     block-pv0001   1Gi        RWO            Block         slow           2m
pvc/nginx-file-pvc01    Bound     file-pv0001    1Gi        RWO            Filesystem    slow           25s

NAME                 READY     STATUS              RESTARTS   AGE
po/nginx-file-pod1   0/1       ContainerCreating   0          4s
po/nginx-pod1        1/1       Running             0          2m
 ~/sample/storage/fc_loop/file % k get pv,pvc,pod
NAME              CAPACITY   ACCESS MODES   VOLUME MODE   RECLAIM POLICY   STATUS    CLAIM                       STORAGECLASS   REASON    AGE
pv/block-pv0001   1Gi        RWO            Block         Retain           Bound     default/nginx-block-pvc01   slow                     2m
pv/file-pv0001    1Gi        RWO            Filesystem    Retain           Bound     default/nginx-file-pvc01    slow                     40s

NAME                    STATUS    VOLUME         CAPACITY   ACCESS MODES   VOLUME MODE   STORAGECLASS   AGE
pvc/nginx-block-pvc01   Bound     block-pv0001   1Gi        RWO            Block         slow           2m
pvc/nginx-file-pvc01    Bound     file-pv0001    1Gi        RWO            Filesystem    slow           40s

NAME                 READY     STATUS    RESTARTS   AGE
po/nginx-file-pod1   1/1       Running   0          19s
po/nginx-pod1        1/1       Running   0          2m

 ~/sample/storage/fc_loop/file % k describe pv/block-pv0001      
Name:            block-pv0001
Labels:          <none>
Annotations:     pv.kubernetes.io/bound-by-controller=yes
                 volume.beta.kubernetes.io/storage-class=slow
StorageClass:    slow
Status:          Bound
Claim:           default/nginx-block-pvc01
Reclaim Policy:  Retain
Access Modes:    RWO
VolumeMode:      Block
Capacity:        1Gi
Message:         
Source:
    Type:        FC (a Fibre Channel disk)
    TargetWWNs:  28000001ff0414e2
    LUN:         0
    FSType:      
    ReadOnly:    true
Events:          <none>

 ~/sample/storage/fc_loop/file % k describe pv/file-pv0001 
Name:            file-pv0001
Labels:          <none>
Annotations:     pv.kubernetes.io/bound-by-controller=yes
                 volume.beta.kubernetes.io/storage-class=slow
StorageClass:    slow
Status:          Bound
Claim:           default/nginx-file-pvc01
Reclaim Policy:  Retain
Access Modes:    RWO
VolumeMode:      Filesystem
Capacity:        1Gi
Message:         
Source:
    Type:        FC (a Fibre Channel disk)
    TargetWWNs:  28000001ff0414e2
    LUN:         0
    FSType:      
    ReadOnly:    true
Events:          <none>

 ~/sample/storage/fc_loop/file % k describe pvc/nginx-block-pvc01
Name:          nginx-block-pvc01
Namespace:     default
StorageClass:  slow
Status:        Bound
Volume:        block-pv0001
Labels:        <none>
Annotations:   pv.kubernetes.io/bind-completed=yes
               pv.kubernetes.io/bound-by-controller=yes
               volume.beta.kubernetes.io/storage-class=slow
Capacity:      1Gi
Access Modes:  RWO
VolumeMode:    Block
Events:        <none>
 ~/sample/storage/fc_loop/file % k describe pvc/nginx-file-pvc01 
Name:          nginx-file-pvc01
Namespace:     default
StorageClass:  slow
Status:        Bound
Volume:        file-pv0001
Labels:        <none>
Annotations:   pv.kubernetes.io/bind-completed=yes
               pv.kubernetes.io/bound-by-controller=yes
               volume.beta.kubernetes.io/storage-class=slow
Capacity:      1Gi
Access Modes:  RWO
VolumeMode:    Filesystem
Events:        <none>
```
pull/6/head
Kubernetes Submit Queue 2017-11-20 21:03:38 -08:00 committed by GitHub
commit b2566bc469
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 267 additions and 56 deletions

View File

@ -1136,6 +1136,9 @@ func describePersistentVolume(pv *api.PersistentVolume, events *api.EventList) (
}
w.Write(LEVEL_0, "Reclaim Policy:\t%v\n", pv.Spec.PersistentVolumeReclaimPolicy)
w.Write(LEVEL_0, "Access Modes:\t%s\n", helper.GetAccessModesAsString(pv.Spec.AccessModes))
if pv.Spec.VolumeMode != nil {
w.Write(LEVEL_0, "VolumeMode:\t%v\n", *pv.Spec.VolumeMode)
}
storage := pv.Spec.Capacity[api.ResourceStorage]
w.Write(LEVEL_0, "Capacity:\t%s\n", storage.String())
w.Write(LEVEL_0, "Message:\t%s\n", pv.Status.Message)
@ -1235,6 +1238,9 @@ func describePersistentVolumeClaim(pvc *api.PersistentVolumeClaim, events *api.E
}
w.Write(LEVEL_0, "Capacity:\t%s\n", capacity)
w.Write(LEVEL_0, "Access Modes:\t%s\n", accessModes)
if pvc.Spec.VolumeMode != nil {
w.Write(LEVEL_0, "VolumeMode:\t%v\n", *pvc.Spec.VolumeMode)
}
if events != nil {
DescribeEvents(events, w)
}
@ -1365,6 +1371,7 @@ func describeContainerProbe(container api.Container, w PrefixWriter) {
}
func describeContainerVolumes(container api.Container, w PrefixWriter) {
// Show volumeMounts
none := ""
if len(container.VolumeMounts) == 0 {
none = "\t<none>"
@ -1383,6 +1390,14 @@ func describeContainerVolumes(container api.Container, w PrefixWriter) {
}
w.Write(LEVEL_3, "%s from %s (%s)\n", mount.MountPath, mount.Name, strings.Join(flags, ","))
}
// Show volumeDevices if exists
if len(container.VolumeDevices) > 0 {
w.Write(LEVEL_2, "Devices:%s\n", none)
sort.Sort(SortableVolumeDevices(container.VolumeDevices))
for _, device := range container.VolumeDevices {
w.Write(LEVEL_3, "%s from %s\n", device.DevicePath, device.Name)
}
}
}
func describeContainerEnvVars(container api.Container, resolverFn EnvVarResolverFunc, w PrefixWriter) {
@ -3803,6 +3818,20 @@ func (list SortableVolumeMounts) Less(i, j int) bool {
return list[i].MountPath < list[j].MountPath
}
type SortableVolumeDevices []api.VolumeDevice
func (list SortableVolumeDevices) Len() int {
return len(list)
}
func (list SortableVolumeDevices) Swap(i, j int) {
list[i], list[j] = list[j], list[i]
}
func (list SortableVolumeDevices) Less(i, j int) bool {
return list[i].DevicePath < list[j].DevicePath
}
// TODO: get rid of this and plumb the caller correctly
func versionedExtensionsClientV1beta1(internalClient clientset.Interface) clientextensionsv1beta1.ExtensionsV1beta1Interface {
if internalClient == nil {

View File

@ -634,6 +634,50 @@ func TestDescribeContainers(t *testing.T) {
},
expectedElements: []string{"cpu", "1k", "memory", "4G", "storage", "20G"},
},
// volumeMounts read/write
{
container: api.Container{
Name: "test",
Image: "image",
VolumeMounts: []api.VolumeMount{
{
Name: "mounted-volume",
MountPath: "/opt/",
},
},
},
expectedElements: []string{"mounted-volume", "/opt/", "(rw)"},
},
// volumeMounts readonly
{
container: api.Container{
Name: "test",
Image: "image",
VolumeMounts: []api.VolumeMount{
{
Name: "mounted-volume",
MountPath: "/opt/",
ReadOnly: true,
},
},
},
expectedElements: []string{"Mounts", "mounted-volume", "/opt/", "(ro)"},
},
// volumeDevices
{
container: api.Container{
Name: "test",
Image: "image",
VolumeDevices: []api.VolumeDevice{
{
Name: "volume-device",
DevicePath: "/dev/xvda",
},
},
},
expectedElements: []string{"Devices", "volume-device", "/dev/xvda"},
},
}
for i, testCase := range testCases {
@ -815,9 +859,17 @@ func TestGetPodsTotalRequests(t *testing.T) {
}
func TestPersistentVolumeDescriber(t *testing.T) {
tests := map[string]*api.PersistentVolume{
"hostpath": {
block := api.PersistentVolumeBlock
file := api.PersistentVolumeFilesystem
testCases := []struct {
plugin string
pv *api.PersistentVolume
expectedElements []string
unexpectedElements []string
}{
{
plugin: "hostpath",
pv: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{Name: "bar"},
Spec: api.PersistentVolumeSpec{
PersistentVolumeSource: api.PersistentVolumeSource{
@ -825,15 +877,24 @@ func TestPersistentVolumeDescriber(t *testing.T) {
},
},
},
"gce": {
unexpectedElements: []string{"VolumeMode", "Filesystem"},
},
{
plugin: "gce",
pv: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{Name: "bar"},
Spec: api.PersistentVolumeSpec{
PersistentVolumeSource: api.PersistentVolumeSource{
GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{},
},
VolumeMode: &file,
},
},
"ebs": {
expectedElements: []string{"VolumeMode", "Filesystem"},
},
{
plugin: "ebs",
pv: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{Name: "bar"},
Spec: api.PersistentVolumeSpec{
PersistentVolumeSource: api.PersistentVolumeSource{
@ -841,7 +902,11 @@ func TestPersistentVolumeDescriber(t *testing.T) {
},
},
},
"nfs": {
unexpectedElements: []string{"VolumeMode", "Filesystem"},
},
{
plugin: "nfs",
pv: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{Name: "bar"},
Spec: api.PersistentVolumeSpec{
PersistentVolumeSource: api.PersistentVolumeSource{
@ -849,15 +914,24 @@ func TestPersistentVolumeDescriber(t *testing.T) {
},
},
},
"iscsi": {
unexpectedElements: []string{"VolumeMode", "Filesystem"},
},
{
plugin: "iscsi",
pv: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{Name: "bar"},
Spec: api.PersistentVolumeSpec{
PersistentVolumeSource: api.PersistentVolumeSource{
ISCSI: &api.ISCSIPersistentVolumeSource{},
},
VolumeMode: &block,
},
},
"gluster": {
expectedElements: []string{"VolumeMode", "Block"},
},
{
plugin: "gluster",
pv: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{Name: "bar"},
Spec: api.PersistentVolumeSpec{
PersistentVolumeSource: api.PersistentVolumeSource{
@ -865,7 +939,11 @@ func TestPersistentVolumeDescriber(t *testing.T) {
},
},
},
"rbd": {
unexpectedElements: []string{"VolumeMode", "Filesystem"},
},
{
plugin: "rbd",
pv: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{Name: "bar"},
Spec: api.PersistentVolumeSpec{
PersistentVolumeSource: api.PersistentVolumeSource{
@ -873,7 +951,11 @@ func TestPersistentVolumeDescriber(t *testing.T) {
},
},
},
"quobyte": {
unexpectedElements: []string{"VolumeMode", "Filesystem"},
},
{
plugin: "quobyte",
pv: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{Name: "bar"},
Spec: api.PersistentVolumeSpec{
PersistentVolumeSource: api.PersistentVolumeSource{
@ -881,7 +963,11 @@ func TestPersistentVolumeDescriber(t *testing.T) {
},
},
},
"cinder": {
unexpectedElements: []string{"VolumeMode", "Filesystem"},
},
{
plugin: "cinder",
pv: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{Name: "bar"},
Spec: api.PersistentVolumeSpec{
PersistentVolumeSource: api.PersistentVolumeSource{
@ -889,25 +975,121 @@ func TestPersistentVolumeDescriber(t *testing.T) {
},
},
},
"fc": {
unexpectedElements: []string{"VolumeMode", "Filesystem"},
},
{
plugin: "fc",
pv: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{Name: "bar"},
Spec: api.PersistentVolumeSpec{
PersistentVolumeSource: api.PersistentVolumeSource{
FC: &api.FCVolumeSource{},
},
VolumeMode: &block,
},
},
expectedElements: []string{"VolumeMode", "Block"},
},
}
for name, pv := range tests {
fake := fake.NewSimpleClientset(pv)
for _, test := range testCases {
fake := fake.NewSimpleClientset(test.pv)
c := PersistentVolumeDescriber{fake}
str, err := c.Describe("foo", "bar", printers.DescriberSettings{ShowEvents: true})
if err != nil {
t.Errorf("Unexpected error for test %s: %v", name, err)
t.Errorf("Unexpected error for test %s: %v", test.plugin, err)
}
if str == "" {
t.Errorf("Unexpected empty string for test %s. Expected PV Describer output", name)
t.Errorf("Unexpected empty string for test %s. Expected PV Describer output", test.plugin)
}
for _, expected := range test.expectedElements {
if !strings.Contains(str, expected) {
t.Errorf("expected to find %q in output: %q", expected, str)
}
}
for _, unexpected := range test.unexpectedElements {
if strings.Contains(str, unexpected) {
t.Errorf("unexpected to find %q in output: %q", unexpected, str)
}
}
}
}
func TestPersistentVolumeClaimDescriber(t *testing.T) {
block := api.PersistentVolumeBlock
file := api.PersistentVolumeFilesystem
goldClassName := "gold"
testCases := []struct {
name string
pvc *api.PersistentVolumeClaim
expectedElements []string
unexpectedElements []string
}{
{
name: "default",
pvc: &api.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"},
Spec: api.PersistentVolumeClaimSpec{
VolumeName: "volume1",
StorageClassName: &goldClassName,
},
Status: api.PersistentVolumeClaimStatus{
Phase: api.ClaimBound,
},
},
unexpectedElements: []string{"VolumeMode", "Filesystem"},
},
{
name: "filesystem",
pvc: &api.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"},
Spec: api.PersistentVolumeClaimSpec{
VolumeName: "volume2",
StorageClassName: &goldClassName,
VolumeMode: &file,
},
Status: api.PersistentVolumeClaimStatus{
Phase: api.ClaimBound,
},
},
expectedElements: []string{"VolumeMode", "Filesystem"},
},
{
name: "block",
pvc: &api.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"},
Spec: api.PersistentVolumeClaimSpec{
VolumeName: "volume3",
StorageClassName: &goldClassName,
VolumeMode: &block,
},
Status: api.PersistentVolumeClaimStatus{
Phase: api.ClaimBound,
},
},
expectedElements: []string{"VolumeMode", "Block"},
},
}
for _, test := range testCases {
fake := fake.NewSimpleClientset(test.pvc)
c := PersistentVolumeClaimDescriber{fake}
str, err := c.Describe("foo", "bar", printers.DescriberSettings{ShowEvents: true})
if err != nil {
t.Errorf("Unexpected error for test %s: %v", test.name, err)
}
if str == "" {
t.Errorf("Unexpected empty string for test %s. Expected PVC Describer output", test.name)
}
for _, expected := range test.expectedElements {
if !strings.Contains(str, expected) {
t.Errorf("expected to find %q in output: %q", expected, str)
}
}
for _, unexpected := range test.unexpectedElements {
if strings.Contains(str, unexpected) {
t.Errorf("unexpected to find %q in output: %q", unexpected, str)
}
}
}
}