Include metrics of BlockVolumes in volumeStatCalculator

This commit is contained in:
Niels de Vos 2021-01-20 09:19:36 +01:00
parent de3a4429d9
commit fb703b4cc1
3 changed files with 91 additions and 8 deletions

View File

@ -566,3 +566,7 @@ func (f *stubBlockVolume) TearDownDevice(mapPath string, devicePath string) erro
func (f *stubBlockVolume) UnmapPodDevice() error {
return nil
}
func (f *stubBlockVolume) GetMetrics() (*volume.Metrics, error) {
return nil, nil
}

View File

@ -96,10 +96,27 @@ func (s *volumeStatCalculator) GetLatest() (PodVolumeStats, bool) {
func (s *volumeStatCalculator) calcAndStoreStats() {
// Find all Volumes for the Pod
volumes, found := s.statsProvider.ListVolumesForPod(s.pod.UID)
if !found {
blockVolumes, bvFound := s.statsProvider.ListBlockVolumesForPod(s.pod.UID)
if !found && !bvFound {
return
}
metricVolumes := make(map[string]volume.MetricsProvider)
if found {
for name, v := range volumes {
metricVolumes[name] = v
}
}
if bvFound {
for name, v := range blockVolumes {
// Only add the blockVolume if it implements the MetricsProvider interface
if _, ok := v.(volume.MetricsProvider); ok {
metricVolumes[name] = v
}
}
}
// Get volume specs for the pod - key'd by volume name
volumesSpec := make(map[string]v1.Volume)
for _, v := range s.pod.Spec.Volumes {
@ -109,7 +126,7 @@ func (s *volumeStatCalculator) calcAndStoreStats() {
// Call GetMetrics on each Volume and copy the result to a new VolumeStats.FsStats
var ephemeralStats []stats.VolumeStats
var persistentStats []stats.VolumeStats
for name, v := range volumes {
for name, v := range metricVolumes {
metric, err := v.GetMetrics()
if err != nil {
// Expected for Volumes that don't support Metrics

View File

@ -45,9 +45,11 @@ const (
inodesTotal = int64(2000)
inodesFree = int64(1000)
vol0 = "vol0"
vol1 = "vol1"
pvcClaimName = "pvc-fake"
vol0 = "vol0"
vol1 = "vol1"
vol2 = "vol2"
pvcClaimName0 = "pvc-fake0"
pvcClaimName1 = "pvc-fake1"
)
var (
@ -66,7 +68,15 @@ var (
Name: vol1,
VolumeSource: k8sv1.VolumeSource{
PersistentVolumeClaim: &k8sv1.PersistentVolumeClaimVolumeSource{
ClaimName: pvcClaimName,
ClaimName: pvcClaimName0,
},
},
},
{
Name: vol2,
VolumeSource: k8sv1.VolumeSource{
PersistentVolumeClaim: &k8sv1.PersistentVolumeClaimVolumeSource{
ClaimName: pvcClaimName1,
},
},
},
@ -91,6 +101,8 @@ func TestPVCRef(t *testing.T) {
mockStats := new(statstest.StatsProvider)
volumes := map[string]volume.Volume{vol0: &fakeVolume{}, vol1: &fakeVolume{}}
mockStats.On("ListVolumesForPod", fakePod.UID).Return(volumes, true)
blockVolumes := map[string]volume.BlockVolume{vol2: &fakeBlockVolume{}}
mockStats.On("ListBlockVolumesForPod", fakePod.UID).Return(blockVolumes, true)
eventStore := make(chan string, 1)
fakeEventRecorder := record.FakeRecorder{
@ -102,7 +114,7 @@ func TestPVCRef(t *testing.T) {
statsCalculator.calcAndStoreStats()
vs, _ := statsCalculator.GetLatest()
assert.Len(t, append(vs.EphemeralVolumes, vs.PersistentVolumes...), 2)
assert.Len(t, append(vs.EphemeralVolumes, vs.PersistentVolumes...), 3)
// Verify 'vol0' doesn't have a PVC reference
assert.Contains(t, append(vs.EphemeralVolumes, vs.PersistentVolumes...), kubestats.VolumeStats{
Name: vol0,
@ -112,17 +124,28 @@ func TestPVCRef(t *testing.T) {
assert.Contains(t, append(vs.EphemeralVolumes, vs.PersistentVolumes...), kubestats.VolumeStats{
Name: vol1,
PVCRef: &kubestats.PVCReference{
Name: pvcClaimName,
Name: pvcClaimName0,
Namespace: namespace0,
},
FsStats: expectedFSStats(),
})
// Verify 'vol2' has a PVC reference
assert.Contains(t, append(vs.EphemeralVolumes, vs.PersistentVolumes...), kubestats.VolumeStats{
Name: vol2,
PVCRef: &kubestats.PVCReference{
Name: pvcClaimName1,
Namespace: namespace0,
},
FsStats: expectedBlockStats(),
})
}
func TestNormalVolumeEvent(t *testing.T) {
mockStats := new(statstest.StatsProvider)
volumes := map[string]volume.Volume{vol0: &fakeVolume{}, vol1: &fakeVolume{}}
mockStats.On("ListVolumesForPod", fakePod.UID).Return(volumes, true)
blockVolumes := map[string]volume.BlockVolume{vol2: &fakeBlockVolume{}}
mockStats.On("ListBlockVolumesForPod", fakePod.UID).Return(blockVolumes, true)
eventStore := make(chan string, 2)
fakeEventRecorder := record.FakeRecorder{
@ -144,6 +167,8 @@ func TestAbnormalVolumeEvent(t *testing.T) {
mockStats := new(statstest.StatsProvider)
volumes := map[string]volume.Volume{vol0: &fakeVolume{}}
mockStats.On("ListVolumesForPod", fakePod.UID).Return(volumes, true)
blockVolumes := map[string]volume.BlockVolume{vol1: &fakeBlockVolume{}}
mockStats.On("ListBlockVolumesForPod", fakePod.UID).Return(blockVolumes, true)
eventStore := make(chan string, 2)
fakeEventRecorder := record.FakeRecorder{
@ -211,3 +236,40 @@ func expectedFSStats() kubestats.FsStats {
InodesUsed: &inodesUsed,
}
}
// Fake block-volume/metrics provider, block-devices have no inodes
var _ volume.BlockVolume = &fakeBlockVolume{}
type fakeBlockVolume struct{}
func (v *fakeBlockVolume) GetGlobalMapPath(*volume.Spec) (string, error) { return "", nil }
func (v *fakeBlockVolume) GetPodDeviceMapPath() (string, string) { return "", "" }
func (v *fakeBlockVolume) GetMetrics() (*volume.Metrics, error) {
return expectedBlockMetrics(), nil
}
func expectedBlockMetrics() *volume.Metrics {
return &volume.Metrics{
Available: resource.NewQuantity(available, resource.BinarySI),
Capacity: resource.NewQuantity(capacity, resource.BinarySI),
Used: resource.NewQuantity(available-capacity, resource.BinarySI),
}
}
func expectedBlockStats() kubestats.FsStats {
metric := expectedBlockMetrics()
available := uint64(metric.Available.Value())
capacity := uint64(metric.Capacity.Value())
used := uint64(metric.Used.Value())
null := uint64(0)
return kubestats.FsStats{
AvailableBytes: &available,
CapacityBytes: &capacity,
UsedBytes: &used,
Inodes: &null,
InodesFree: &null,
InodesUsed: &null,
}
}