...

Source file src/k8s.io/kubernetes/pkg/kubelet/stats/cri_stats_provider_windows_test.go

Documentation: k8s.io/kubernetes/pkg/kubelet/stats

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package stats
    18  
    19  import (
    20  	"reflect"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/Microsoft/hcsshim"
    25  	cadvisorapiv2 "github.com/google/cadvisor/info/v2"
    26  	"github.com/stretchr/testify/assert"
    27  	"k8s.io/apimachinery/pkg/api/resource"
    28  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/types"
    30  	runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
    31  	statsapi "k8s.io/kubelet/pkg/apis/stats/v1alpha1"
    32  	kubecontainertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
    33  	"k8s.io/kubernetes/pkg/kubelet/kuberuntime"
    34  	"k8s.io/kubernetes/pkg/volume"
    35  	testingclock "k8s.io/utils/clock/testing"
    36  )
    37  
    38  type fakeNetworkStatsProvider struct {
    39  	containers []containerStats
    40  }
    41  
    42  type containerStats struct {
    43  	container hcsshim.ContainerProperties
    44  	hcsStats  []hcsshim.NetworkStats
    45  }
    46  
    47  func (s fakeNetworkStatsProvider) GetHNSEndpointStats(endpointName string) (*hcsshim.HNSEndpointStats, error) {
    48  	eps := hcsshim.HNSEndpointStats{}
    49  	for _, c := range s.containers {
    50  		for _, stat := range c.hcsStats {
    51  			if endpointName == stat.InstanceId {
    52  				eps = hcsshim.HNSEndpointStats{
    53  					EndpointID:      stat.EndpointId,
    54  					BytesSent:       stat.BytesSent,
    55  					BytesReceived:   stat.BytesReceived,
    56  					PacketsReceived: stat.PacketsReceived,
    57  					PacketsSent:     stat.PacketsSent,
    58  				}
    59  			}
    60  		}
    61  	}
    62  
    63  	return &eps, nil
    64  }
    65  
    66  func (s fakeNetworkStatsProvider) HNSListEndpointRequest() ([]hcsshim.HNSEndpoint, error) {
    67  	uniqueEndpoints := map[string]*hcsshim.HNSEndpoint{}
    68  
    69  	for _, c := range s.containers {
    70  		for _, stat := range c.hcsStats {
    71  			e, found := uniqueEndpoints[stat.EndpointId]
    72  			if found {
    73  				// add the container
    74  				e.SharedContainers = append(e.SharedContainers, c.container.ID)
    75  				continue
    76  			}
    77  
    78  			uniqueEndpoints[stat.EndpointId] = &hcsshim.HNSEndpoint{
    79  				Name:             stat.EndpointId,
    80  				Id:               stat.EndpointId,
    81  				SharedContainers: []string{c.container.ID},
    82  			}
    83  		}
    84  	}
    85  
    86  	eps := []hcsshim.HNSEndpoint{}
    87  	for _, ep := range uniqueEndpoints {
    88  		eps = append(eps, *ep)
    89  	}
    90  
    91  	return eps, nil
    92  }
    93  
    94  func Test_criStatsProvider_listContainerNetworkStats(t *testing.T) {
    95  	fakeClock := testingclock.NewFakeClock(time.Time{})
    96  	tests := []struct {
    97  		name    string
    98  		fields  fakeNetworkStatsProvider
    99  		want    map[string]*statsapi.NetworkStats
   100  		wantErr bool
   101  		skipped bool
   102  	}{
   103  		{
   104  			name: "basic example",
   105  			fields: fakeNetworkStatsProvider{
   106  				containers: []containerStats{
   107  					{
   108  						container: hcsshim.ContainerProperties{
   109  							ID: "c1",
   110  						}, hcsStats: []hcsshim.NetworkStats{
   111  							{
   112  								BytesReceived: 1,
   113  								BytesSent:     10,
   114  								EndpointId:    "test",
   115  								InstanceId:    "test",
   116  							},
   117  						},
   118  					},
   119  					{
   120  						container: hcsshim.ContainerProperties{
   121  							ID: "c2",
   122  						}, hcsStats: []hcsshim.NetworkStats{
   123  							{
   124  								BytesReceived: 2,
   125  								BytesSent:     20,
   126  								EndpointId:    "test2",
   127  								InstanceId:    "test2",
   128  							},
   129  						},
   130  					},
   131  				},
   132  			},
   133  			want: map[string]*statsapi.NetworkStats{
   134  				"c1": {
   135  					Time: v1.NewTime(fakeClock.Now()),
   136  					InterfaceStats: statsapi.InterfaceStats{
   137  						Name:    "test",
   138  						RxBytes: toP(1),
   139  						TxBytes: toP(10),
   140  					},
   141  					Interfaces: []statsapi.InterfaceStats{
   142  						{
   143  							Name:    "test",
   144  							RxBytes: toP(1),
   145  
   146  							TxBytes: toP(10),
   147  						},
   148  					},
   149  				},
   150  				"c2": {
   151  					Time: v1.Time{},
   152  					InterfaceStats: statsapi.InterfaceStats{
   153  						Name:    "test2",
   154  						RxBytes: toP(2),
   155  						TxBytes: toP(20),
   156  					},
   157  					Interfaces: []statsapi.InterfaceStats{
   158  						{
   159  							Name:    "test2",
   160  							RxBytes: toP(2),
   161  							TxBytes: toP(20),
   162  						},
   163  					},
   164  				},
   165  			},
   166  			wantErr: false,
   167  		},
   168  		{
   169  			name: "multiple containers same endpoint",
   170  			fields: fakeNetworkStatsProvider{
   171  				containers: []containerStats{
   172  					{
   173  						container: hcsshim.ContainerProperties{
   174  							ID: "c1",
   175  						}, hcsStats: []hcsshim.NetworkStats{
   176  							{
   177  								BytesReceived: 1,
   178  								BytesSent:     10,
   179  								EndpointId:    "test",
   180  								InstanceId:    "test",
   181  							},
   182  						},
   183  					},
   184  					{
   185  						container: hcsshim.ContainerProperties{
   186  							ID: "c2",
   187  						}, hcsStats: []hcsshim.NetworkStats{
   188  							{
   189  								BytesReceived: 2,
   190  								BytesSent:     20,
   191  								EndpointId:    "test2",
   192  								InstanceId:    "test2",
   193  							},
   194  						},
   195  					},
   196  					{
   197  						container: hcsshim.ContainerProperties{
   198  							ID: "c3",
   199  						}, hcsStats: []hcsshim.NetworkStats{
   200  							{
   201  								BytesReceived: 3,
   202  								BytesSent:     30,
   203  								EndpointId:    "test2",
   204  								InstanceId:    "test3",
   205  							},
   206  						},
   207  					},
   208  				},
   209  			},
   210  			want: map[string]*statsapi.NetworkStats{
   211  				"c1": {
   212  					Time: v1.NewTime(fakeClock.Now()),
   213  					InterfaceStats: statsapi.InterfaceStats{
   214  						Name:    "test",
   215  						RxBytes: toP(1),
   216  						TxBytes: toP(10),
   217  					},
   218  					Interfaces: []statsapi.InterfaceStats{
   219  						{
   220  							Name:    "test",
   221  							RxBytes: toP(1),
   222  
   223  							TxBytes: toP(10),
   224  						},
   225  					},
   226  				},
   227  				"c2": {
   228  					Time: v1.Time{},
   229  					InterfaceStats: statsapi.InterfaceStats{
   230  						Name:    "test2",
   231  						RxBytes: toP(2),
   232  						TxBytes: toP(20),
   233  					},
   234  					Interfaces: []statsapi.InterfaceStats{
   235  						{
   236  							Name:    "test2",
   237  							RxBytes: toP(2),
   238  							TxBytes: toP(20),
   239  						},
   240  					},
   241  				},
   242  				"c3": {
   243  					Time: v1.Time{},
   244  					InterfaceStats: statsapi.InterfaceStats{
   245  						Name:    "test2",
   246  						RxBytes: toP(2),
   247  						TxBytes: toP(20),
   248  					},
   249  					Interfaces: []statsapi.InterfaceStats{
   250  						{
   251  							Name:    "test2",
   252  							RxBytes: toP(2),
   253  							TxBytes: toP(20),
   254  						},
   255  					},
   256  				},
   257  			},
   258  			wantErr: false,
   259  		},
   260  		{
   261  			name: "multiple stats instances of same interface only picks up first",
   262  			fields: fakeNetworkStatsProvider{
   263  				containers: []containerStats{
   264  					{
   265  						container: hcsshim.ContainerProperties{
   266  							ID: "c1",
   267  						}, hcsStats: []hcsshim.NetworkStats{
   268  							{
   269  								BytesReceived: 1,
   270  								BytesSent:     10,
   271  								EndpointId:    "test",
   272  								InstanceId:    "test",
   273  							},
   274  							{
   275  								BytesReceived: 3,
   276  								BytesSent:     30,
   277  								EndpointId:    "test",
   278  								InstanceId:    "test3",
   279  							},
   280  						},
   281  					},
   282  					{
   283  						container: hcsshim.ContainerProperties{
   284  							ID: "c2",
   285  						}, hcsStats: []hcsshim.NetworkStats{
   286  							{
   287  								BytesReceived: 2,
   288  								BytesSent:     20,
   289  								EndpointId:    "test2",
   290  								InstanceId:    "test2",
   291  							},
   292  						},
   293  					},
   294  				},
   295  			},
   296  			want: map[string]*statsapi.NetworkStats{
   297  				"c1": {
   298  					Time: v1.NewTime(fakeClock.Now()),
   299  					InterfaceStats: statsapi.InterfaceStats{
   300  						Name:    "test",
   301  						RxBytes: toP(1),
   302  						TxBytes: toP(10),
   303  					},
   304  					Interfaces: []statsapi.InterfaceStats{
   305  						{
   306  							Name:    "test",
   307  							RxBytes: toP(1),
   308  
   309  							TxBytes: toP(10),
   310  						},
   311  					},
   312  				},
   313  				"c2": {
   314  					Time: v1.Time{},
   315  					InterfaceStats: statsapi.InterfaceStats{
   316  						Name:    "test2",
   317  						RxBytes: toP(2),
   318  						TxBytes: toP(20),
   319  					},
   320  					Interfaces: []statsapi.InterfaceStats{
   321  						{
   322  							Name:    "test2",
   323  							RxBytes: toP(2),
   324  							TxBytes: toP(20),
   325  						},
   326  					},
   327  				},
   328  			},
   329  			wantErr: false,
   330  		},
   331  		{
   332  			name: "multiple endpoints per container",
   333  			fields: fakeNetworkStatsProvider{
   334  				containers: []containerStats{
   335  					{
   336  						container: hcsshim.ContainerProperties{
   337  							ID: "c1",
   338  						}, hcsStats: []hcsshim.NetworkStats{
   339  							{
   340  								BytesReceived: 1,
   341  								BytesSent:     10,
   342  								EndpointId:    "test",
   343  								InstanceId:    "test",
   344  							},
   345  							{
   346  								BytesReceived: 3,
   347  								BytesSent:     30,
   348  								EndpointId:    "test3",
   349  								InstanceId:    "test3",
   350  							},
   351  						},
   352  					},
   353  					{
   354  						container: hcsshim.ContainerProperties{
   355  							ID: "c2",
   356  						}, hcsStats: []hcsshim.NetworkStats{
   357  							{
   358  								BytesReceived: 2,
   359  								BytesSent:     20,
   360  								EndpointId:    "test2",
   361  								InstanceId:    "test2",
   362  							},
   363  						},
   364  					},
   365  				},
   366  			},
   367  			want: map[string]*statsapi.NetworkStats{
   368  				"c1": {
   369  					Time: v1.NewTime(fakeClock.Now()),
   370  					InterfaceStats: statsapi.InterfaceStats{
   371  						Name:    "test",
   372  						RxBytes: toP(1),
   373  						TxBytes: toP(10),
   374  					},
   375  					Interfaces: []statsapi.InterfaceStats{
   376  						{
   377  							Name:    "test",
   378  							RxBytes: toP(1),
   379  
   380  							TxBytes: toP(10),
   381  						},
   382  						{
   383  							Name:    "test3",
   384  							RxBytes: toP(3),
   385  
   386  							TxBytes: toP(30),
   387  						},
   388  					},
   389  				},
   390  				"c2": {
   391  					Time: v1.Time{},
   392  					InterfaceStats: statsapi.InterfaceStats{
   393  						Name:    "test2",
   394  						RxBytes: toP(2),
   395  						TxBytes: toP(20),
   396  					},
   397  					Interfaces: []statsapi.InterfaceStats{
   398  						{
   399  							Name:    "test2",
   400  							RxBytes: toP(2),
   401  							TxBytes: toP(20),
   402  						},
   403  					},
   404  				},
   405  			},
   406  			wantErr: false,
   407  			skipped: true,
   408  		},
   409  	}
   410  	for _, tt := range tests {
   411  		t.Run(tt.name, func(t *testing.T) {
   412  			// TODO: Remove skip once https://github.com/kubernetes/kubernetes/issues/116692 is fixed.
   413  			if tt.skipped {
   414  				t.Skip("Test temporarily skipped.")
   415  			}
   416  			p := &criStatsProvider{
   417  				windowsNetworkStatsProvider: fakeNetworkStatsProvider{
   418  					containers: tt.fields.containers,
   419  				},
   420  				clock: fakeClock,
   421  			}
   422  			got, err := p.listContainerNetworkStats()
   423  			if (err != nil) != tt.wantErr {
   424  				t.Errorf("listContainerNetworkStats() error = %v, wantErr %v", err, tt.wantErr)
   425  				return
   426  			}
   427  			if !reflect.DeepEqual(got, tt.want) {
   428  				t.Errorf("listContainerNetworkStats() got = %v, want %v", got, tt.want)
   429  			}
   430  		})
   431  	}
   432  }
   433  
   434  func toP(i uint64) *uint64 {
   435  	return &i
   436  }
   437  
   438  func Test_criStatsProvider_makeWinContainerStats(t *testing.T) {
   439  	fakeClock := testingclock.NewFakeClock(time.Time{})
   440  	containerStartTime := fakeClock.Now()
   441  
   442  	cpuUsageTimestamp := int64(555555)
   443  	cpuUsageNanoSeconds := uint64(0x123456)
   444  	cpuUsageNanoCores := uint64(0x4000)
   445  	memoryUsageTimestamp := int64(666666)
   446  	memoryUsageWorkingSetBytes := uint64(0x11223344)
   447  	memoryUsageAvailableBytes := uint64(0x55667788)
   448  	memoryUsagePageFaults := uint64(200)
   449  	logStatsUsed := uint64(5000)
   450  	logStatsInodesUsed := uint64(5050)
   451  
   452  	// getPodContainerLogStats is called during makeWindowsContainerStats to populate ContainerStats.Logs
   453  	c0LogStats := &volume.Metrics{
   454  		Used:       resource.NewQuantity(int64(logStatsUsed), resource.BinarySI),
   455  		InodesUsed: resource.NewQuantity(int64(logStatsInodesUsed), resource.BinarySI),
   456  	}
   457  	fakeStats := map[string]*volume.Metrics{
   458  		kuberuntime.BuildContainerLogsDirectory(testPodLogDirectory, "sb0-ns", "sb0-name", types.UID("sb0-uid"), "c0"): c0LogStats,
   459  	}
   460  	fakeOS := &kubecontainertest.FakeOS{}
   461  	fakeHostStatsProvider := NewFakeHostStatsProviderWithData(fakeStats, fakeOS)
   462  
   463  	p := &criStatsProvider{
   464  		clock:             fakeClock,
   465  		hostStatsProvider: fakeHostStatsProvider,
   466  	}
   467  
   468  	inputStats := &runtimeapi.WindowsContainerStats{
   469  		Attributes: &runtimeapi.ContainerAttributes{
   470  			Metadata: &runtimeapi.ContainerMetadata{
   471  				Name: "c0",
   472  			},
   473  		},
   474  		Cpu: &runtimeapi.WindowsCpuUsage{
   475  			Timestamp: cpuUsageTimestamp,
   476  			UsageCoreNanoSeconds: &runtimeapi.UInt64Value{
   477  				Value: cpuUsageNanoSeconds,
   478  			},
   479  			UsageNanoCores: &runtimeapi.UInt64Value{
   480  				Value: cpuUsageNanoCores,
   481  			},
   482  		},
   483  		Memory: &runtimeapi.WindowsMemoryUsage{
   484  			Timestamp: memoryUsageTimestamp,
   485  			AvailableBytes: &runtimeapi.UInt64Value{
   486  				Value: memoryUsageAvailableBytes,
   487  			},
   488  			WorkingSetBytes: &runtimeapi.UInt64Value{
   489  				Value: memoryUsageWorkingSetBytes,
   490  			},
   491  			PageFaults: &runtimeapi.UInt64Value{
   492  				Value: memoryUsagePageFaults,
   493  			},
   494  		},
   495  	}
   496  
   497  	inputContainer := &runtimeapi.Container{
   498  		CreatedAt: containerStartTime.Unix(),
   499  		Metadata: &runtimeapi.ContainerMetadata{
   500  			Name: "c0",
   501  		},
   502  	}
   503  
   504  	inputRootFsInfo := &cadvisorapiv2.FsInfo{}
   505  
   506  	// Used by the getPodContainerLogStats() call in makeWinContainerStats()
   507  	inputPodSandboxMetadata := &runtimeapi.PodSandboxMetadata{
   508  		Namespace: "sb0-ns",
   509  		Name:      "sb0-name",
   510  		Uid:       "sb0-uid",
   511  	}
   512  
   513  	got, err := p.makeWinContainerStats(inputStats, inputContainer, inputRootFsInfo, make(map[runtimeapi.FilesystemIdentifier]*cadvisorapiv2.FsInfo), inputPodSandboxMetadata)
   514  
   515  	expected := &statsapi.ContainerStats{
   516  		Name:      "c0",
   517  		StartTime: v1.NewTime(time.Unix(0, containerStartTime.Unix())),
   518  		CPU: &statsapi.CPUStats{
   519  			Time:                 v1.NewTime(time.Unix(0, cpuUsageTimestamp)),
   520  			UsageNanoCores:       toP(cpuUsageNanoCores),
   521  			UsageCoreNanoSeconds: toP(cpuUsageNanoSeconds),
   522  		},
   523  		Memory: &statsapi.MemoryStats{
   524  			Time:            v1.NewTime(time.Unix(0, memoryUsageTimestamp)),
   525  			AvailableBytes:  toP(memoryUsageAvailableBytes),
   526  			WorkingSetBytes: toP(memoryUsageWorkingSetBytes),
   527  			PageFaults:      toP(memoryUsagePageFaults),
   528  		},
   529  		Rootfs: &statsapi.FsStats{},
   530  		Logs: &statsapi.FsStats{
   531  			Time:       c0LogStats.Time,
   532  			UsedBytes:  toP(logStatsUsed),
   533  			InodesUsed: toP(logStatsInodesUsed),
   534  		},
   535  	}
   536  
   537  	if err != nil {
   538  		t.Fatalf("makeWinContainerStats() error = %v, expected no error", err)
   539  	}
   540  
   541  	if !reflect.DeepEqual(got.CPU, expected.CPU) {
   542  		t.Errorf("makeWinContainerStats() CPU = %v, expected %v", got.CPU, expected.CPU)
   543  	}
   544  
   545  	if !reflect.DeepEqual(got.Memory, expected.Memory) {
   546  		t.Errorf("makeWinContainerStats() Memory = %v, want %v", got.Memory, expected.Memory)
   547  	}
   548  
   549  	if !reflect.DeepEqual(got.Rootfs, expected.Rootfs) {
   550  		t.Errorf("makeWinContainerStats() Rootfs = %v, want %v", got.Rootfs, expected.Rootfs)
   551  	}
   552  
   553  	// Log stats contain pointers to calculated resource values so we cannot use DeepEqual here
   554  	assert.Equal(t, *got.Logs.UsedBytes, logStatsUsed, "Logs.UsedBytes does not match expected value")
   555  	assert.Equal(t, *got.Logs.InodesUsed, logStatsInodesUsed, "Logs.InodesUsed does not match expected value")
   556  }
   557  

View as plain text