github.com/cilium/cilium@v1.16.2/pkg/hubble/filters/labels_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Hubble
     3  
     4  package filters
     5  
     6  import (
     7  	"context"
     8  	"reflect"
     9  	"testing"
    10  
    11  	flowpb "github.com/cilium/cilium/api/v1/flow"
    12  	v1 "github.com/cilium/cilium/pkg/hubble/api/v1"
    13  )
    14  
    15  func TestLabelSelectorFilter(t *testing.T) {
    16  	type args struct {
    17  		f  []*flowpb.FlowFilter
    18  		ev []*v1.Event
    19  	}
    20  	tests := []struct {
    21  		name    string
    22  		args    args
    23  		wantErr bool
    24  		want    []bool
    25  	}{
    26  		{
    27  			name: "label filter without value",
    28  			args: args{
    29  				f: []*flowpb.FlowFilter{{SourceLabel: []string{"label1", "label2"}}},
    30  				ev: []*v1.Event{
    31  					{
    32  						Event: &flowpb.Flow{
    33  							Source: &flowpb.Endpoint{
    34  								Labels: []string{"label1"},
    35  							},
    36  						},
    37  					},
    38  					{
    39  						Event: &flowpb.Flow{
    40  							Source: &flowpb.Endpoint{
    41  								Labels: []string{"label1=val1"},
    42  							},
    43  						},
    44  					},
    45  					{
    46  						Event: &flowpb.Flow{
    47  							Source: &flowpb.Endpoint{
    48  								Labels: []string{"label2", "label3", "label4=val4"},
    49  							},
    50  						},
    51  					},
    52  					{
    53  						Event: &flowpb.Flow{
    54  							Source: &flowpb.Endpoint{
    55  								Labels: []string{"label3"},
    56  							},
    57  						},
    58  					},
    59  				},
    60  			},
    61  			want: []bool{
    62  				true,
    63  				true,
    64  				true,
    65  				false,
    66  			},
    67  		},
    68  		{
    69  			name: "label filter with value",
    70  			args: args{
    71  				f: []*flowpb.FlowFilter{{SourceLabel: []string{"label1=val1", "label2=val2"}}},
    72  				ev: []*v1.Event{
    73  					{
    74  						Event: &flowpb.Flow{
    75  							Source: &flowpb.Endpoint{
    76  								Labels: []string{"label1"},
    77  							},
    78  						},
    79  					},
    80  					{
    81  						Event: &flowpb.Flow{
    82  							Source: &flowpb.Endpoint{
    83  								Labels: []string{"label1=val1"},
    84  							},
    85  						},
    86  					},
    87  					{
    88  						Event: &flowpb.Flow{
    89  							Source: &flowpb.Endpoint{
    90  								Labels: []string{"label1=val2", "label2=val1", "label3"},
    91  							},
    92  						},
    93  					},
    94  					{
    95  						Event: &flowpb.Flow{
    96  							Source: &flowpb.Endpoint{
    97  								Labels: []string{"label2=val2", "label3"},
    98  							},
    99  						},
   100  					},
   101  					{
   102  						Event: &flowpb.Flow{
   103  							Source: &flowpb.Endpoint{
   104  								Labels: []string{"label3=val1"},
   105  							},
   106  						},
   107  					},
   108  					{
   109  						Event: &flowpb.Flow{
   110  							Source: &flowpb.Endpoint{
   111  								Labels: []string{""},
   112  							},
   113  						},
   114  					},
   115  					{
   116  						Event: &flowpb.Flow{
   117  							Source: &flowpb.Endpoint{
   118  								Labels: nil,
   119  							},
   120  						},
   121  					},
   122  					{
   123  						Event: &flowpb.Flow{
   124  							Source: &flowpb.Endpoint{
   125  								Labels: []string{"label1=val1=toomuch"},
   126  							},
   127  						},
   128  					},
   129  				},
   130  			},
   131  			want: []bool{
   132  				false,
   133  				true,
   134  				false,
   135  				true,
   136  				false,
   137  				false,
   138  				false,
   139  				false,
   140  			},
   141  		},
   142  		{
   143  			name: "complex label label filter",
   144  			args: args{
   145  				f: []*flowpb.FlowFilter{{SourceLabel: []string{"label1 in (val1, val2), label3 notin ()"}}},
   146  				ev: []*v1.Event{
   147  					{
   148  						Event: &flowpb.Flow{
   149  							Source: &flowpb.Endpoint{
   150  								Labels: []string{"label1"},
   151  							},
   152  						},
   153  					},
   154  					{
   155  						Event: &flowpb.Flow{
   156  							Source: &flowpb.Endpoint{
   157  								Labels: []string{"label1=val1"},
   158  							},
   159  						},
   160  					},
   161  					{
   162  						Event: &flowpb.Flow{
   163  							Source: &flowpb.Endpoint{
   164  								Labels: []string{"label1=val2", "label2=val1", "label3=val3"},
   165  							},
   166  						},
   167  					},
   168  					{
   169  						Event: &flowpb.Flow{
   170  							Source: &flowpb.Endpoint{
   171  								Labels: []string{"label2=val2", "label3"},
   172  							},
   173  						},
   174  					},
   175  					{
   176  						Event: &flowpb.Flow{
   177  							Source: &flowpb.Endpoint{
   178  								Labels: []string{"label1=val1", "label3=val3"},
   179  							},
   180  						},
   181  					},
   182  				},
   183  			},
   184  			want: []bool{
   185  				false,
   186  				true,
   187  				true,
   188  				false,
   189  				true,
   190  			},
   191  		},
   192  		{
   193  			name: "node label filter",
   194  			args: args{
   195  				f: []*flowpb.FlowFilter{
   196  					{
   197  						NodeLabels: []string{"src1, src2=val2"},
   198  					},
   199  				},
   200  				ev: []*v1.Event{
   201  					{
   202  						Event: &flowpb.Flow{
   203  							NodeLabels: []string{"src1", "src2=val2"},
   204  						},
   205  					},
   206  					{
   207  						Event: &flowpb.Flow{
   208  							Source: &flowpb.Endpoint{
   209  								Labels: []string{"src1", "src2=val2"},
   210  							},
   211  						},
   212  					},
   213  					{
   214  						Event: &flowpb.Flow{
   215  							Destination: &flowpb.Endpoint{
   216  								Labels: []string{"src1", "src2=val2"},
   217  							},
   218  						},
   219  					},
   220  				},
   221  			},
   222  			want: []bool{
   223  				true,
   224  				false,
   225  				false,
   226  			},
   227  		},
   228  		{
   229  			name: "source and destination label filter",
   230  			args: args{
   231  				f: []*flowpb.FlowFilter{
   232  					{
   233  						SourceLabel:      []string{"src1, src2=val2"},
   234  						DestinationLabel: []string{"dst1, dst2=val2"},
   235  					},
   236  				},
   237  				ev: []*v1.Event{
   238  					{
   239  						Event: &flowpb.Flow{
   240  							Source: &flowpb.Endpoint{
   241  								Labels: []string{"src1", "src2=val2"},
   242  							},
   243  							Destination: &flowpb.Endpoint{
   244  								Labels: []string{"dst1", "dst2=val2"},
   245  							},
   246  						},
   247  					},
   248  					{
   249  						Event: &flowpb.Flow{
   250  							Source: &flowpb.Endpoint{
   251  								Labels: []string{"label1=val1"},
   252  							},
   253  						},
   254  					},
   255  					{
   256  						Event: &flowpb.Flow{
   257  							Destination: &flowpb.Endpoint{
   258  								Labels: []string{"dst1", "dst2=val2"},
   259  							},
   260  						},
   261  					},
   262  					{
   263  						Event: &flowpb.Flow{
   264  							Source: &flowpb.Endpoint{
   265  								Labels: []string{"dst1", "dst2=val2"},
   266  							},
   267  							Destination: &flowpb.Endpoint{
   268  								Labels: []string{"src1", "src2=val2"},
   269  							},
   270  						},
   271  					},
   272  					{
   273  						Event: &flowpb.Flow{
   274  							Source: &flowpb.Endpoint{
   275  								Labels: []string{"src1"},
   276  							},
   277  							Destination: &flowpb.Endpoint{
   278  								Labels: []string{"dst1"},
   279  							},
   280  						},
   281  					},
   282  				},
   283  			},
   284  			want: []bool{
   285  				true,
   286  				false,
   287  				false,
   288  				false,
   289  				false,
   290  			},
   291  		},
   292  		{
   293  			name: "matchall filter",
   294  			args: args{
   295  				f: []*flowpb.FlowFilter{
   296  					{
   297  						SourceLabel: []string{""},
   298  					},
   299  				},
   300  				ev: []*v1.Event{
   301  					{
   302  						Event: &flowpb.Flow{
   303  							Source: &flowpb.Endpoint{
   304  								Labels: []string{"src1", "src2=val2"},
   305  							},
   306  						},
   307  					},
   308  					{
   309  						Event: &flowpb.Flow{
   310  							Source: &flowpb.Endpoint{
   311  								Labels: nil,
   312  							},
   313  						},
   314  					},
   315  					{
   316  						Event: &flowpb.Flow{
   317  							Source: &flowpb.Endpoint{
   318  								Labels: []string{""},
   319  							},
   320  						},
   321  					},
   322  				},
   323  			},
   324  			want: []bool{
   325  				true,
   326  				true,
   327  				true,
   328  			},
   329  		},
   330  		{
   331  			name: "cilium fixed prefix filters",
   332  			args: args{
   333  				f: []*flowpb.FlowFilter{
   334  					{
   335  						SourceLabel: []string{"k8s:app=bar", "foo", "reserved:host"},
   336  					},
   337  				},
   338  				ev: []*v1.Event{
   339  					{
   340  						Event: &flowpb.Flow{
   341  							Source: &flowpb.Endpoint{
   342  								Labels: []string{"k8s:app=bar"},
   343  							},
   344  						},
   345  					},
   346  					{
   347  						Event: &flowpb.Flow{
   348  							Source: &flowpb.Endpoint{
   349  								Labels: []string{"k8s:foo=baz"},
   350  							},
   351  						},
   352  					},
   353  					{
   354  						Event: &flowpb.Flow{
   355  							Source: &flowpb.Endpoint{
   356  								Labels: []string{"k8s.app=bar"},
   357  							},
   358  						},
   359  					},
   360  					{
   361  						Event: &flowpb.Flow{
   362  							Source: &flowpb.Endpoint{
   363  								Labels: []string{"container:foo=bar", "reserved:host"},
   364  							},
   365  						},
   366  					},
   367  				},
   368  			},
   369  			want: []bool{
   370  				true,
   371  				true,
   372  				false,
   373  				true,
   374  			},
   375  		},
   376  		{
   377  			name: "cilium fixed prefix not filter",
   378  			args: args{
   379  				f: []*flowpb.FlowFilter{
   380  					{
   381  						SourceLabel: []string{"!k8s:app"},
   382  					},
   383  				},
   384  				ev: []*v1.Event{
   385  					{
   386  						Event: &flowpb.Flow{
   387  							Source: &flowpb.Endpoint{
   388  								Labels: []string{"k8s:app=bar"},
   389  							},
   390  						},
   391  					},
   392  					{
   393  						Event: &flowpb.Flow{
   394  							Source: &flowpb.Endpoint{
   395  								Labels: []string{"k8s:app=baz"},
   396  							},
   397  						},
   398  					},
   399  					{
   400  						Event: &flowpb.Flow{
   401  							Source: &flowpb.Endpoint{
   402  								Labels: []string{"k8s.app=bar"},
   403  							},
   404  						},
   405  					},
   406  					{
   407  						Event: &flowpb.Flow{
   408  							Source: &flowpb.Endpoint{
   409  								Labels: []string{"container:foo=bar", "reserved:host"},
   410  							},
   411  						},
   412  					},
   413  				},
   414  			},
   415  			want: []bool{
   416  				false,
   417  				false,
   418  				true,
   419  				true,
   420  			},
   421  		},
   422  		{
   423  			name: "cilium any prefix filters",
   424  			args: args{
   425  				f: []*flowpb.FlowFilter{
   426  					{
   427  						SourceLabel: []string{"any:key"},
   428  					},
   429  				},
   430  				ev: []*v1.Event{
   431  					{
   432  						Event: &flowpb.Flow{
   433  							Source: &flowpb.Endpoint{
   434  								Labels: []string{"key"},
   435  							},
   436  						},
   437  					},
   438  					{
   439  						Event: &flowpb.Flow{
   440  							Source: &flowpb.Endpoint{
   441  								Labels: []string{"reserved:key"},
   442  							},
   443  						},
   444  					},
   445  					{
   446  						Event: &flowpb.Flow{
   447  							Source: &flowpb.Endpoint{
   448  								Labels: []string{"any.key"},
   449  							},
   450  						},
   451  					},
   452  				},
   453  			},
   454  			want: []bool{
   455  				true,
   456  				true,
   457  				false,
   458  			},
   459  		},
   460  		{
   461  			name: "cilium no prefix filters",
   462  			args: args{
   463  				f: []*flowpb.FlowFilter{
   464  					{
   465  						SourceLabel: []string{"key"},
   466  					},
   467  				},
   468  				ev: []*v1.Event{
   469  					{
   470  						Event: &flowpb.Flow{
   471  							Source: &flowpb.Endpoint{
   472  								Labels: []string{"key"},
   473  							},
   474  						},
   475  					},
   476  					{
   477  						Event: &flowpb.Flow{
   478  							Source: &flowpb.Endpoint{
   479  								Labels: []string{"reserved:key"},
   480  							},
   481  						},
   482  					},
   483  					{
   484  						Event: &flowpb.Flow{
   485  							Source: &flowpb.Endpoint{
   486  								Labels: []string{"any.key"},
   487  							},
   488  						},
   489  					},
   490  				},
   491  			},
   492  			want: []bool{
   493  				true,
   494  				true,
   495  				false,
   496  			},
   497  		},
   498  		{
   499  			name: "cilium k8s label with dot",
   500  			args: args{
   501  				f: []*flowpb.FlowFilter{
   502  					{
   503  						SourceLabel: []string{"key.with.dot"},
   504  					},
   505  				},
   506  				ev: []*v1.Event{
   507  					{
   508  						Event: &flowpb.Flow{
   509  							Source: &flowpb.Endpoint{
   510  								Labels: []string{"k8s:key.with.dot"},
   511  							},
   512  						},
   513  					},
   514  					{
   515  						Event: &flowpb.Flow{
   516  							Source: &flowpb.Endpoint{
   517  								Labels: []string{"reserved:key.with.dot"},
   518  							},
   519  						},
   520  					},
   521  					{
   522  						Event: &flowpb.Flow{
   523  							Source: &flowpb.Endpoint{
   524  								Labels: []string{"any.key.with.dot"},
   525  							},
   526  						},
   527  					},
   528  					{
   529  						Event: &flowpb.Flow{
   530  							Source: &flowpb.Endpoint{
   531  								Labels: []string{"key:with.dot"},
   532  							},
   533  						},
   534  					},
   535  				},
   536  			},
   537  			want: []bool{
   538  				true,
   539  				true,
   540  				false,
   541  				false,
   542  			},
   543  		},
   544  		{
   545  			name: "invalid source filter",
   546  			args: args{
   547  				f: []*flowpb.FlowFilter{
   548  					{
   549  						SourceLabel: []string{"()"},
   550  					},
   551  				},
   552  			},
   553  			wantErr: true,
   554  		},
   555  		{
   556  			name: "invalid destination filter",
   557  			args: args{
   558  				f: []*flowpb.FlowFilter{
   559  					{
   560  						DestinationLabel: []string{"="},
   561  					},
   562  				},
   563  			},
   564  			wantErr: true,
   565  		},
   566  		{
   567  			name: "invalid node filter",
   568  			args: args{
   569  				f: []*flowpb.FlowFilter{
   570  					{
   571  						NodeLabels: []string{"!"},
   572  					},
   573  				},
   574  			},
   575  			wantErr: true,
   576  		},
   577  	}
   578  	for _, tt := range tests {
   579  		t.Run(tt.name, func(t *testing.T) {
   580  			fl, err := BuildFilterList(context.Background(), tt.args.f, []OnBuildFilter{&LabelsFilter{}})
   581  			if (err != nil) != tt.wantErr {
   582  				t.Errorf("\"%s\" error = %v, wantErr %v", tt.name, err, tt.wantErr)
   583  				return
   584  			}
   585  			if err != nil {
   586  				return
   587  			}
   588  			for i, ev := range tt.args.ev {
   589  				if got := fl.MatchOne(ev); got != tt.want[i] {
   590  					t.Errorf("\"%s\" got %d = %v, want %v", tt.name, i, got, tt.want[i])
   591  				}
   592  			}
   593  		})
   594  	}
   595  }
   596  
   597  func Test_parseSelector(t *testing.T) {
   598  	type args struct {
   599  		selector string
   600  	}
   601  	tests := []struct {
   602  		name    string
   603  		args    args
   604  		want    string
   605  		wantErr bool
   606  	}{
   607  		{
   608  			name: "simple labels",
   609  			args: args{
   610  				selector: "bar=baz,k8s:app=hubble,reserved:world",
   611  			},
   612  			want: "any.bar=baz,k8s.app=hubble,reserved.world",
   613  		},
   614  		{
   615  			name: "not label",
   616  			args: args{
   617  				selector: "!k8s:app",
   618  			},
   619  			want: "!k8s.app",
   620  		},
   621  		{
   622  			name: "complex labels",
   623  			args: args{
   624  				selector: "any:dash-label.com,k8s:io.cilium in (is-awesome,rocks)",
   625  			},
   626  			want: "any.dash-label.com,k8s.io.cilium in (is-awesome,rocks)",
   627  		},
   628  		{
   629  			name: "more complex labels",
   630  			args: args{
   631  				selector: "any:dash-label.com,k8s:io.cilium in (is-awesome, andsoon, rocks), !foobar, io.cilium notin (), test:label.com/foobar",
   632  			},
   633  			// NOTE: re-ordering and whitespace trimming is due to k8sLabels.Parse()
   634  			want: "any.dash-label.com,!any.foobar,any.io.cilium notin (),k8s.io.cilium in (andsoon,is-awesome,rocks),test.label.com/foobar",
   635  		},
   636  		{
   637  			name: "too many colons",
   638  			args: args{
   639  				selector: "any:k8s:bla",
   640  			},
   641  			wantErr: true,
   642  		},
   643  	}
   644  	for _, tt := range tests {
   645  		t.Run(tt.name, func(t *testing.T) {
   646  			got, err := parseSelector(tt.args.selector)
   647  			if (err != nil) != tt.wantErr {
   648  				t.Errorf("parseSelector() error = %v, wantErr %v", err, tt.wantErr)
   649  				return
   650  			}
   651  			if !tt.wantErr && !reflect.DeepEqual(got.String(), tt.want) {
   652  				t.Errorf("parseSelector() = %q, want %q", got, tt.want)
   653  			}
   654  		})
   655  	}
   656  }