github.com/cilium/cilium@v1.16.2/pkg/hubble/container/ring_reader_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Hubble
     3  
     4  package container
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"io"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/stretchr/testify/assert"
    15  	"go.uber.org/goleak"
    16  	"google.golang.org/protobuf/types/known/timestamppb"
    17  
    18  	flowpb "github.com/cilium/cilium/api/v1/flow"
    19  	v1 "github.com/cilium/cilium/pkg/hubble/api/v1"
    20  )
    21  
    22  func TestRingReader_Previous(t *testing.T) {
    23  	ring := NewRing(Capacity15)
    24  	for i := 0; i < 15; i++ {
    25  		ring.Write(&v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: int64(i)}})
    26  	}
    27  	tests := []struct {
    28  		start   uint64
    29  		count   int
    30  		want    []*v1.Event
    31  		wantErr error
    32  	}{
    33  		{
    34  			start: 13,
    35  			count: 1,
    36  			want: []*v1.Event{
    37  				{Timestamp: &timestamppb.Timestamp{Seconds: 13}},
    38  			},
    39  		}, {
    40  			start: 13,
    41  			count: 2,
    42  			want: []*v1.Event{
    43  				{Timestamp: &timestamppb.Timestamp{Seconds: 13}},
    44  				{Timestamp: &timestamppb.Timestamp{Seconds: 12}},
    45  			},
    46  		}, {
    47  			start: 5,
    48  			count: 5,
    49  			want: []*v1.Event{
    50  				{Timestamp: &timestamppb.Timestamp{Seconds: 5}},
    51  				{Timestamp: &timestamppb.Timestamp{Seconds: 4}},
    52  				{Timestamp: &timestamppb.Timestamp{Seconds: 3}},
    53  				{Timestamp: &timestamppb.Timestamp{Seconds: 2}},
    54  				{Timestamp: &timestamppb.Timestamp{Seconds: 1}},
    55  			},
    56  		}, {
    57  			start: 0,
    58  			count: 1,
    59  			want: []*v1.Event{
    60  				{Timestamp: &timestamppb.Timestamp{Seconds: 0}},
    61  			},
    62  		}, {
    63  			start: 0,
    64  			count: 1,
    65  			want: []*v1.Event{
    66  				{Timestamp: &timestamppb.Timestamp{Seconds: 0}},
    67  			},
    68  		}, {
    69  			start:   14,
    70  			count:   1,
    71  			wantErr: io.EOF,
    72  		},
    73  	}
    74  	for _, tt := range tests {
    75  		name := fmt.Sprintf("read %d, start at position %d", tt.count, tt.start)
    76  		t.Run(name, func(t *testing.T) {
    77  			reader := NewRingReader(ring, tt.start)
    78  			var got []*v1.Event
    79  			for i := 0; i < tt.count; i++ {
    80  				event, err := reader.Previous()
    81  				if !errors.Is(err, tt.wantErr) {
    82  					t.Errorf(`"%s" error = %v, wantErr %v`, name, err, tt.wantErr)
    83  				}
    84  				if err != nil {
    85  					return
    86  				}
    87  				got = append(got, event)
    88  			}
    89  			assert.Equal(t, tt.want, got)
    90  			assert.Nil(t, reader.Close())
    91  		})
    92  	}
    93  }
    94  
    95  func TestRingReader_PreviousLost(t *testing.T) {
    96  	ring := NewRing(Capacity15)
    97  	for i := 0; i < 15; i++ {
    98  		ring.Write(&v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: int64(i)}})
    99  	}
   100  	reader := NewRingReader(ring, ^uint64(0))
   101  	expected := &v1.Event{
   102  		Event: &flowpb.LostEvent{
   103  			Source:        flowpb.LostEventSource_HUBBLE_RING_BUFFER,
   104  			NumEventsLost: 1,
   105  			Cpu:           nil,
   106  		},
   107  	}
   108  	actual, err := reader.Previous()
   109  	assert.NoError(t, err)
   110  	assert.Equal(t, expected.GetLostEvent(), actual.GetLostEvent())
   111  	assert.Nil(t, reader.Close())
   112  }
   113  
   114  func TestRingReader_Next(t *testing.T) {
   115  	ring := NewRing(Capacity15)
   116  	for i := 0; i < 15; i++ {
   117  		ring.Write(&v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: int64(i)}})
   118  	}
   119  
   120  	tests := []struct {
   121  		start   uint64
   122  		count   int
   123  		want    []*v1.Event
   124  		wantErr error
   125  	}{
   126  		{
   127  			start: 0,
   128  			count: 1,
   129  			want: []*v1.Event{
   130  				{Timestamp: &timestamppb.Timestamp{Seconds: 0}},
   131  			},
   132  		}, {
   133  			start: 0,
   134  			count: 2,
   135  			want: []*v1.Event{
   136  				{Timestamp: &timestamppb.Timestamp{Seconds: 0}},
   137  				{Timestamp: &timestamppb.Timestamp{Seconds: 1}},
   138  			},
   139  		}, {
   140  			start: 5,
   141  			count: 5,
   142  			want: []*v1.Event{
   143  				{Timestamp: &timestamppb.Timestamp{Seconds: 5}},
   144  				{Timestamp: &timestamppb.Timestamp{Seconds: 6}},
   145  				{Timestamp: &timestamppb.Timestamp{Seconds: 7}},
   146  				{Timestamp: &timestamppb.Timestamp{Seconds: 8}},
   147  				{Timestamp: &timestamppb.Timestamp{Seconds: 9}},
   148  			},
   149  		}, {
   150  			start: 13,
   151  			count: 1,
   152  			want: []*v1.Event{
   153  				{Timestamp: &timestamppb.Timestamp{Seconds: 13}},
   154  			},
   155  		}, {
   156  			start:   14,
   157  			count:   1,
   158  			wantErr: io.EOF,
   159  		},
   160  	}
   161  	for _, tt := range tests {
   162  		name := fmt.Sprintf("read %d, start at position %d", tt.count, tt.start)
   163  		t.Run(name, func(t *testing.T) {
   164  			reader := NewRingReader(ring, tt.start)
   165  			var got []*v1.Event
   166  			for i := 0; i < tt.count; i++ {
   167  				event, err := reader.Next()
   168  				if !errors.Is(err, tt.wantErr) {
   169  					t.Errorf(`"%s" error = %v, wantErr %v`, name, err, tt.wantErr)
   170  				}
   171  				if err != nil {
   172  					return
   173  				}
   174  				got = append(got, event)
   175  			}
   176  			assert.Equal(t, tt.want, got)
   177  			assert.Nil(t, reader.Close())
   178  		})
   179  	}
   180  }
   181  
   182  func TestRingReader_NextLost(t *testing.T) {
   183  	ring := NewRing(Capacity15)
   184  	for i := 0; i < 15; i++ {
   185  		ring.Write(&v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: int64(i)}})
   186  	}
   187  	expected := &v1.Event{
   188  		Event: &flowpb.LostEvent{
   189  			Source:        flowpb.LostEventSource_HUBBLE_RING_BUFFER,
   190  			NumEventsLost: 1,
   191  			Cpu:           nil,
   192  		},
   193  	}
   194  	reader := NewRingReader(ring, ^uint64(0))
   195  	actual, err := reader.Next()
   196  	assert.NoError(t, err)
   197  	assert.Equal(t, expected.GetLostEvent(), actual.GetLostEvent())
   198  	assert.Nil(t, reader.Close())
   199  }
   200  
   201  func TestRingReader_NextFollow(t *testing.T) {
   202  	defer goleak.VerifyNone(
   203  		t,
   204  		// ignore goroutines started by the redirect we do from klog to logrus
   205  		goleak.IgnoreTopFunction("k8s.io/klog.(*loggingT).flushDaemon"),
   206  		goleak.IgnoreTopFunction("k8s.io/klog/v2.(*loggingT).flushDaemon"),
   207  		goleak.IgnoreTopFunction("io.(*pipe).read"))
   208  	ring := NewRing(Capacity15)
   209  	for i := 0; i < 15; i++ {
   210  		ring.Write(&v1.Event{Timestamp: &timestamppb.Timestamp{Seconds: int64(i)}})
   211  	}
   212  
   213  	tests := []struct {
   214  		start       uint64
   215  		count       int
   216  		want        []*v1.Event
   217  		wantTimeout bool
   218  	}{
   219  		{
   220  			start: 0,
   221  			count: 1,
   222  			want: []*v1.Event{
   223  				{Timestamp: &timestamppb.Timestamp{Seconds: 0}},
   224  			},
   225  		}, {
   226  			start: 0,
   227  			count: 2,
   228  			want: []*v1.Event{
   229  				{Timestamp: &timestamppb.Timestamp{Seconds: 0}},
   230  				{Timestamp: &timestamppb.Timestamp{Seconds: 1}},
   231  			},
   232  		}, {
   233  			start: 5,
   234  			count: 5,
   235  			want: []*v1.Event{
   236  				{Timestamp: &timestamppb.Timestamp{Seconds: 5}},
   237  				{Timestamp: &timestamppb.Timestamp{Seconds: 6}},
   238  				{Timestamp: &timestamppb.Timestamp{Seconds: 7}},
   239  				{Timestamp: &timestamppb.Timestamp{Seconds: 8}},
   240  				{Timestamp: &timestamppb.Timestamp{Seconds: 9}},
   241  			},
   242  		}, {
   243  			start: 13,
   244  			count: 1,
   245  			want: []*v1.Event{
   246  				{Timestamp: &timestamppb.Timestamp{Seconds: 13}},
   247  			},
   248  		}, {
   249  			start:       14,
   250  			count:       1,
   251  			want:        []*v1.Event{nil},
   252  			wantTimeout: true,
   253  		},
   254  	}
   255  	for _, tt := range tests {
   256  		name := fmt.Sprintf("read %d, start at position %d, expect timeout=%t", tt.count, tt.start, tt.wantTimeout)
   257  		t.Run(name, func(t *testing.T) {
   258  			reader := NewRingReader(ring, tt.start)
   259  			var timedOut bool
   260  			var got []*v1.Event
   261  			for i := 0; i < tt.count; i++ {
   262  				ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
   263  				got = append(got, reader.NextFollow(ctx))
   264  				select {
   265  				case <-ctx.Done():
   266  					timedOut = true
   267  				default:
   268  					assert.NotNil(t, got[i])
   269  				}
   270  				cancel()
   271  				assert.Nil(t, reader.Close())
   272  			}
   273  			assert.Equal(t, tt.want, got)
   274  			assert.Equal(t, tt.wantTimeout, timedOut)
   275  		})
   276  	}
   277  }
   278  
   279  func TestRingReader_NextFollow_WithEmptyRing(t *testing.T) {
   280  	defer goleak.VerifyNone(
   281  		t,
   282  		// ignore goroutines started by the redirect we do from klog to logrus
   283  		goleak.IgnoreTopFunction("k8s.io/klog.(*loggingT).flushDaemon"),
   284  		goleak.IgnoreTopFunction("k8s.io/klog/v2.(*loggingT).flushDaemon"),
   285  		goleak.IgnoreTopFunction("io.(*pipe).read"))
   286  	ring := NewRing(Capacity15)
   287  	reader := NewRingReader(ring, ring.LastWriteParallel())
   288  	ctx, cancel := context.WithCancel(context.Background())
   289  	c := make(chan *v1.Event)
   290  	done := make(chan struct{})
   291  	go func() {
   292  		select {
   293  		case <-ctx.Done():
   294  		case c <- reader.NextFollow(ctx):
   295  		}
   296  		close(done)
   297  	}()
   298  	select {
   299  	case <-c:
   300  		t.Fail()
   301  	case <-time.After(100 * time.Millisecond):
   302  		// the call blocked, we're good
   303  	}
   304  	cancel()
   305  	<-done
   306  	assert.Nil(t, reader.Close())
   307  }