github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/logql/log/ip_test.go (about)

     1  package log
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/prometheus/prometheus/model/labels"
     7  	"github.com/stretchr/testify/assert"
     8  	"github.com/stretchr/testify/require"
     9  )
    10  
    11  func Test_IPFilter(t *testing.T) {
    12  	cases := []struct {
    13  		name     string
    14  		pat      string
    15  		input    []string
    16  		expected []int // matched line indexes from the input
    17  
    18  		// in case of expecting error
    19  		err  error
    20  		fail bool
    21  	}{
    22  		{
    23  			name: "single IPv4",
    24  			pat:  "192.168.0.1",
    25  			input: []string{
    26  				"vpn 192.168.0.5 connected to vm",
    27  				"vpn 192.168.0.1 connected to vm",
    28  				"x",
    29  				"hello world!",
    30  				"",
    31  			},
    32  			expected: []int{1}, // should match with only line at index `1` from the input
    33  
    34  		},
    35  		{
    36  			name: "single IPv6",
    37  			pat:  "::1", // localhost address
    38  			input: []string{
    39  				"vpn ::1 connected to vm", // still a match
    40  				"vpn 192.168.0.1 connected to vm",
    41  				"x",
    42  				"hello world!",
    43  				"",
    44  			},
    45  			expected: []int{0}, // should match with only line at index `0` from the input
    46  
    47  		},
    48  		{
    49  			name: "IPv4 range",
    50  			pat:  "192.168.0.1-192.189.10.12",
    51  			input: []string{
    52  				"vpn 192.168.0.0 connected to vm",
    53  				"vpn 192.168.0.0 192.168.0.1 connected to vm", // match
    54  				"vpn 192.172.6.1 connected to vm",             // match
    55  				"vpn 192.255.255.255 connected to vm",
    56  				"x",
    57  				"hello world!",
    58  				"",
    59  			},
    60  			expected: []int{1, 2},
    61  		},
    62  		{
    63  			name: "IPv6 range",
    64  			pat:  "2001:db8::1-2001:db8::8",
    65  			input: []string{
    66  				"vpn 192.168.0.0 connected to vm", // not match
    67  				"vpn 2001:db8::2 connected to vm", // match
    68  				"vpn 2001:db8::9 connected to vm", // not match
    69  				"vpn 2001:db8::5 connected to vm", // match
    70  				"x",
    71  				"hello world!",
    72  				"",
    73  			},
    74  			expected: []int{1, 3},
    75  		},
    76  		{
    77  			name: "wrong IP range syntax extra space", // NOTE(kavi): Should we handle this as normal valid range pattern?
    78  			pat:  "192.168.0.1 - 192.189.10.12",
    79  			fail: true,
    80  			err:  ErrIPFilterInvalidPattern,
    81  		},
    82  		{
    83  			name: "CIDR",
    84  			pat:  "192.168.4.5/16",
    85  			input: []string{
    86  				"vpn 192.168.0.0 connected to vm",
    87  				"vpn 192.168.0.1 connected to vm",
    88  				"vpn 192.172.6.1 connected to vm",
    89  				"vpn 192.168.255.255 connected to vm",
    90  				"x",
    91  				"hello world!",
    92  				"",
    93  			},
    94  			expected: []int{0, 1, 3},
    95  		},
    96  		{
    97  			name: "CIDR IPv6",
    98  			pat:  "2001:db8::/32",
    99  			input: []string{
   100  				"vpn 2001:db8::1 connected to vm",                 // match
   101  				"vpn 2001:db9::3 connected to vm",                 // not a match
   102  				"vpn 2001:dc8::1 and 2001:db8::2 connected to vm", // firt not match, but second did match. So overall its a match
   103  				"vpn 192.168.255.255 connected to vm",             // not match
   104  				"x",
   105  				"hello world!",
   106  				"",
   107  			},
   108  			expected: []int{0, 2},
   109  		},
   110  	}
   111  
   112  	for _, c := range cases {
   113  		t.Run(c.name, func(t *testing.T) {
   114  			ip, err := newIPFilter(c.pat)
   115  			if c.fail {
   116  				assert.Error(t, c.err, err)
   117  				return
   118  			}
   119  			assert.NoError(t, err)
   120  
   121  			got := make([]int, 0)
   122  			for i, in := range c.input {
   123  				if ip.filter([]byte(in)) {
   124  					got = append(got, i)
   125  				}
   126  			}
   127  			assert.Equal(t, c.expected, got)
   128  		})
   129  	}
   130  }
   131  
   132  func Test_IPLabelFilterTy(t *testing.T) {
   133  	cases := []struct {
   134  		name          string
   135  		pat           string
   136  		ty            LabelFilterType
   137  		label         string
   138  		val           []byte
   139  		expectedMatch bool
   140  
   141  		fail bool
   142  		err  string // label filter errors are just treated as strings :(
   143  	}{
   144  		{
   145  			name:          "equal operator",
   146  			pat:           "192.168.0.1",
   147  			ty:            LabelFilterEqual,
   148  			label:         "addr",
   149  			val:           []byte("192.168.0.1"),
   150  			expectedMatch: true,
   151  		},
   152  		{
   153  			name:          "not equal operator",
   154  			pat:           "192.168.0.2",
   155  			ty:            LabelFilterNotEqual,
   156  			label:         "addr",
   157  			val:           []byte("192.168.0.1"), // match because !=ip("192.168.0.2")
   158  			expectedMatch: true,
   159  		},
   160  		{
   161  			name:  "pattern-invalid",
   162  			pat:   "192.168.0.2-1.0.0.0",
   163  			ty:    LabelFilterEqual, // not supported
   164  			label: "addr",
   165  			val:   []byte("192.168.0.1"),
   166  			fail:  true,
   167  			err:   ErrIPFilterInvalidPattern.Error(),
   168  		},
   169  	}
   170  
   171  	for _, c := range cases {
   172  		t.Run(c.name, func(t *testing.T) {
   173  			lf := NewIPLabelFilter(c.pat, c.label, c.ty)
   174  
   175  			lbs := labels.Labels{labels.Label{Name: c.label, Value: string(c.val)}}
   176  			lbb := NewBaseLabelsBuilder().ForLabels(lbs, lbs.Hash())
   177  			_, ok := lf.Process(0, []byte("x"), lbb)
   178  			if c.fail {
   179  				assert.Error(t, lf.patError)
   180  				return
   181  			}
   182  			require.Empty(t, lbb.GetErr())
   183  			assert.Equal(t, c.expectedMatch, ok)
   184  		})
   185  	}
   186  }
   187  
   188  func Test_IPLineFilterTy(t *testing.T) {
   189  	cases := []struct {
   190  		name          string
   191  		pat           string
   192  		ty            labels.MatchType
   193  		line          []byte
   194  		expectedMatch bool
   195  
   196  		fail bool
   197  		err  error
   198  	}{
   199  		{
   200  			name:          "equal operator",
   201  			pat:           "192.168.0.1",
   202  			ty:            labels.MatchEqual,
   203  			line:          []byte("192.168.0.1"),
   204  			expectedMatch: true,
   205  		},
   206  		{
   207  			name:          "not equal operator",
   208  			pat:           "192.168.0.2",
   209  			ty:            labels.MatchNotEqual,
   210  			line:          []byte("192.168.0.1"), // match because !=ip("192.168.0.2")
   211  			expectedMatch: true,
   212  		},
   213  		{
   214  			name: "regex not equal",
   215  			pat:  "192.168.0.2",
   216  			ty:   labels.MatchNotRegexp, // not supported
   217  			line: []byte("192.168.0.1"),
   218  			fail: true,
   219  			err:  ErrIPFilterInvalidOperation,
   220  		},
   221  		{
   222  			name: "regex equal",
   223  			pat:  "192.168.0.2",
   224  			ty:   labels.MatchRegexp, // not supported
   225  			line: []byte("192.168.0.1"),
   226  			fail: true,
   227  			err:  ErrIPFilterInvalidOperation,
   228  		},
   229  	}
   230  
   231  	for _, c := range cases {
   232  		t.Run(c.name, func(t *testing.T) {
   233  			lf, err := NewIPLineFilter(c.pat, c.ty)
   234  			if c.fail {
   235  				require.Error(t, err)
   236  				return
   237  			}
   238  			ok := lf.filterTy(c.line, c.ty)
   239  			assert.Equal(t, c.expectedMatch, ok)
   240  		})
   241  	}
   242  }
   243  
   244  func Benchmark_IPFilter(b *testing.B) {
   245  	b.ReportAllocs()
   246  
   247  	line := [][]byte{
   248  		[]byte(`vpn 192.168.0.0 connected to vm`),
   249  		[]byte(`vpn <missing-ip> connected to vm just wanted to make some long line without match`),
   250  		[]byte(`vpn ::1 connected to vm just wanted to make some long line with match at the end 127.0.0.1`),
   251  	}
   252  
   253  	for _, pattern := range []string{
   254  		"127.0.0.1",
   255  		"192.168.0.1-192.189.10.12",
   256  		"192.168.4.5/16",
   257  	} {
   258  		b.Run(pattern, func(b *testing.B) {
   259  			stage, err := newIPFilter(pattern)
   260  			require.NoError(b, err)
   261  			b.ResetTimer()
   262  
   263  			for n := 0; n < b.N; n++ {
   264  				for _, l := range line {
   265  					_ = stage.filter(l)
   266  				}
   267  			}
   268  		})
   269  	}
   270  
   271  }