mirror of https://github.com/k3s-io/k3s
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
commit
b2566bc469
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue