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 }