github.com/cilium/cilium@v1.16.2/pkg/k8s/apis/cilium.io/utils/utils_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package utils
     5  
     6  import (
     7  	"fmt"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/require"
    11  	"k8s.io/apimachinery/pkg/types"
    12  
    13  	k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io"
    14  	slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1"
    15  	"github.com/cilium/cilium/pkg/labels"
    16  	"github.com/cilium/cilium/pkg/policy/api"
    17  )
    18  
    19  func Test_namespacesAreValid(t *testing.T) {
    20  	require.Equal(t, true, namespacesAreValid("default", []string{}))
    21  	require.Equal(t, true, namespacesAreValid("default", []string{"default"}))
    22  	require.Equal(t, false, namespacesAreValid("default", []string{"foo"}))
    23  	require.Equal(t, false, namespacesAreValid("default", []string{"default", "foo"}))
    24  }
    25  
    26  func Test_ParseToCiliumRule(t *testing.T) {
    27  	role := fmt.Sprintf("%s.role", labels.LabelSourceAny)
    28  	namespace := fmt.Sprintf("%s.%s", labels.LabelSourceK8s, k8sConst.PodNamespaceLabel)
    29  	uuid := types.UID("11bba160-ddca-11e8-b697-0800273b04ff")
    30  	type args struct {
    31  		namespace string
    32  		rule      *api.Rule
    33  		uid       types.UID
    34  	}
    35  	tests := []struct {
    36  		name string
    37  		args args
    38  		want *api.Rule
    39  	}{
    40  		{
    41  			// When the rule has no namespace match, the namespace
    42  			// is inherited from the namespace where the rule is
    43  			// added.
    44  			name: "parse-in-namespace",
    45  			args: args{
    46  				namespace: slim_metav1.NamespaceDefault,
    47  				uid:       uuid,
    48  				rule: &api.Rule{
    49  					EndpointSelector: api.NewESFromMatchRequirements(
    50  						map[string]string{
    51  							role: "backend",
    52  						},
    53  						nil,
    54  					),
    55  				},
    56  			},
    57  			want: api.NewRule().WithEndpointSelector(
    58  				api.NewESFromMatchRequirements(
    59  					map[string]string{
    60  						role:      "backend",
    61  						namespace: "default",
    62  					},
    63  					nil,
    64  				),
    65  			).WithLabels(
    66  				labels.LabelArray{
    67  					{
    68  						Key:    "io.cilium.k8s.policy.derived-from",
    69  						Value:  "CiliumNetworkPolicy",
    70  						Source: labels.LabelSourceK8s,
    71  					},
    72  					{
    73  						Key:    "io.cilium.k8s.policy.name",
    74  						Value:  "parse-in-namespace",
    75  						Source: labels.LabelSourceK8s,
    76  					},
    77  					{
    78  						Key:    "io.cilium.k8s.policy.namespace",
    79  						Value:  "default",
    80  						Source: labels.LabelSourceK8s,
    81  					},
    82  					{
    83  						Key:    "io.cilium.k8s.policy.uid",
    84  						Value:  string(uuid),
    85  						Source: labels.LabelSourceK8s,
    86  					},
    87  				},
    88  			),
    89  		},
    90  		{
    91  			// When the rule specifies a namespace, it is overridden
    92  			// by the namespace where the rule was inserted.
    93  			name: "parse-in-namespace-with-ns-selector",
    94  			args: args{
    95  				namespace: slim_metav1.NamespaceDefault,
    96  				uid:       uuid,
    97  				rule: &api.Rule{
    98  					EndpointSelector: api.NewESFromMatchRequirements(
    99  						map[string]string{
   100  							role:      "backend",
   101  							namespace: "foo",
   102  						},
   103  						nil,
   104  					),
   105  				},
   106  			},
   107  			want: api.NewRule().WithEndpointSelector(
   108  				api.NewESFromMatchRequirements(
   109  					map[string]string{
   110  						role:      "backend",
   111  						namespace: "default",
   112  					},
   113  					nil,
   114  				),
   115  			).WithLabels(
   116  				labels.LabelArray{
   117  					{
   118  						Key:    "io.cilium.k8s.policy.derived-from",
   119  						Value:  "CiliumNetworkPolicy",
   120  						Source: labels.LabelSourceK8s,
   121  					},
   122  					{
   123  						Key:    "io.cilium.k8s.policy.name",
   124  						Value:  "parse-in-namespace-with-ns-selector",
   125  						Source: labels.LabelSourceK8s,
   126  					},
   127  					{
   128  						Key:    "io.cilium.k8s.policy.namespace",
   129  						Value:  "default",
   130  						Source: labels.LabelSourceK8s,
   131  					},
   132  					{
   133  						Key:    "io.cilium.k8s.policy.uid",
   134  						Value:  string(uuid),
   135  						Source: labels.LabelSourceK8s,
   136  					},
   137  				},
   138  			),
   139  		},
   140  		{
   141  			// Don't insert a namespace selection when the rule
   142  			// is for init policies.
   143  			name: "parse-init-policy",
   144  			args: args{
   145  				uid: uuid,
   146  				rule: &api.Rule{
   147  					EndpointSelector: api.NewESFromMatchRequirements(
   148  						map[string]string{
   149  							role:       "backend",
   150  							podInitLbl: "",
   151  						},
   152  						nil,
   153  					),
   154  				},
   155  			},
   156  			want: api.NewRule().WithEndpointSelector(
   157  				api.NewESFromMatchRequirements(
   158  					map[string]string{
   159  						role:       "backend",
   160  						podInitLbl: "",
   161  						// No namespace because it's init.
   162  						// namespace: "default",
   163  					},
   164  					nil,
   165  				),
   166  			).WithLabels(
   167  				labels.LabelArray{
   168  					{
   169  						Key:    "io.cilium.k8s.policy.derived-from",
   170  						Value:  "CiliumClusterwideNetworkPolicy",
   171  						Source: labels.LabelSourceK8s,
   172  					},
   173  					{
   174  						Key:    "io.cilium.k8s.policy.name",
   175  						Value:  "parse-init-policy",
   176  						Source: labels.LabelSourceK8s,
   177  					},
   178  					{
   179  						Key:    "io.cilium.k8s.policy.uid",
   180  						Value:  string(uuid),
   181  						Source: labels.LabelSourceK8s,
   182  					},
   183  				},
   184  			),
   185  		},
   186  		{
   187  			// CNP with endpoint selectors should always select the
   188  			// current namespace
   189  			name: "parse-init-policy-namespaced",
   190  			args: args{
   191  				namespace: slim_metav1.NamespaceDefault,
   192  				uid:       uuid,
   193  				rule: &api.Rule{
   194  					EndpointSelector: api.NewESFromMatchRequirements(
   195  						nil,
   196  						[]slim_metav1.LabelSelectorRequirement{
   197  							{
   198  								Key:      "reserved.init",
   199  								Operator: slim_metav1.LabelSelectorOpDoesNotExist,
   200  							},
   201  						},
   202  					),
   203  					Ingress: []api.IngressRule{
   204  						{
   205  							IngressCommonRule: api.IngressCommonRule{
   206  								FromEndpoints: []api.EndpointSelector{
   207  									{
   208  										LabelSelector: &slim_metav1.LabelSelector{},
   209  									},
   210  								},
   211  							},
   212  						},
   213  					},
   214  				},
   215  			},
   216  			want: api.NewRule().WithEndpointSelector(
   217  				api.NewESFromMatchRequirements(
   218  					map[string]string{
   219  						namespace: "default",
   220  					},
   221  					[]slim_metav1.LabelSelectorRequirement{
   222  						{
   223  							Key:      "reserved.init",
   224  							Operator: slim_metav1.LabelSelectorOpDoesNotExist,
   225  						},
   226  					},
   227  				),
   228  			).WithIngressRules(
   229  				[]api.IngressRule{
   230  					{
   231  						IngressCommonRule: api.IngressCommonRule{
   232  							FromEndpoints: []api.EndpointSelector{
   233  								api.NewESFromK8sLabelSelector(
   234  									labels.LabelSourceK8sKeyPrefix,
   235  									&slim_metav1.LabelSelector{
   236  										MatchLabels: map[string]string{
   237  											k8sConst.PodNamespaceLabel: "default",
   238  										},
   239  									}),
   240  							},
   241  						},
   242  					},
   243  				},
   244  			).WithLabels(
   245  				labels.LabelArray{
   246  					{
   247  						Key:    "io.cilium.k8s.policy.derived-from",
   248  						Value:  "CiliumNetworkPolicy",
   249  						Source: labels.LabelSourceK8s,
   250  					},
   251  					{
   252  						Key:    "io.cilium.k8s.policy.name",
   253  						Value:  "parse-init-policy-namespaced",
   254  						Source: labels.LabelSourceK8s,
   255  					},
   256  					{
   257  						Key:    "io.cilium.k8s.policy.namespace",
   258  						Value:  "default",
   259  						Source: labels.LabelSourceK8s,
   260  					},
   261  					{
   262  						Key:    "io.cilium.k8s.policy.uid",
   263  						Value:  string(uuid),
   264  						Source: labels.LabelSourceK8s,
   265  					},
   266  				},
   267  			),
   268  		},
   269  		{
   270  			name: "set-any-source-for-namespace",
   271  			args: args{
   272  				namespace: slim_metav1.NamespaceDefault,
   273  				uid:       uuid,
   274  				rule: &api.Rule{
   275  					EndpointSelector: api.NewESFromMatchRequirements(
   276  						map[string]string{
   277  							role: "backend",
   278  						},
   279  						nil,
   280  					),
   281  					Ingress: []api.IngressRule{
   282  						{
   283  							IngressCommonRule: api.IngressCommonRule{
   284  								FromEndpoints: []api.EndpointSelector{
   285  									{
   286  										LabelSelector: &slim_metav1.LabelSelector{
   287  											MatchLabels: map[string]string{
   288  												podAnyPrefixLbl: "ns-2",
   289  											},
   290  										},
   291  									},
   292  								},
   293  							},
   294  						},
   295  					},
   296  				},
   297  			},
   298  			want: api.NewRule().WithEndpointSelector(
   299  				api.NewESFromMatchRequirements(
   300  					map[string]string{
   301  						role:      "backend",
   302  						namespace: "default",
   303  					},
   304  					nil,
   305  				),
   306  			).WithIngressRules(
   307  				[]api.IngressRule{
   308  					{
   309  						IngressCommonRule: api.IngressCommonRule{
   310  							FromEndpoints: []api.EndpointSelector{
   311  								api.NewESFromK8sLabelSelector(
   312  									labels.LabelSourceAnyKeyPrefix,
   313  									&slim_metav1.LabelSelector{
   314  										MatchLabels: map[string]string{
   315  											k8sConst.PodNamespaceLabel: "ns-2",
   316  										},
   317  									}),
   318  							},
   319  						},
   320  					},
   321  				},
   322  			).WithLabels(
   323  				labels.LabelArray{
   324  					{
   325  						Key:    "io.cilium.k8s.policy.derived-from",
   326  						Value:  "CiliumNetworkPolicy",
   327  						Source: labels.LabelSourceK8s,
   328  					},
   329  					{
   330  						Key:    "io.cilium.k8s.policy.name",
   331  						Value:  "set-any-source-for-namespace",
   332  						Source: labels.LabelSourceK8s,
   333  					},
   334  					{
   335  						Key:    "io.cilium.k8s.policy.namespace",
   336  						Value:  "default",
   337  						Source: labels.LabelSourceK8s,
   338  					},
   339  					{
   340  						Key:    "io.cilium.k8s.policy.uid",
   341  						Value:  string(uuid),
   342  						Source: labels.LabelSourceK8s,
   343  					},
   344  				},
   345  			),
   346  		},
   347  		{
   348  			// When the rule specifies namespace labels, namespace label is not added
   349  			// by the namespace where the rule was inserted.
   350  			name: "parse-in-namespace-with-ns-labels-selector",
   351  			args: args{
   352  				namespace: slim_metav1.NamespaceDefault,
   353  				uid:       uuid,
   354  				rule: &api.Rule{
   355  					EndpointSelector: api.NewESFromMatchRequirements(
   356  						map[string]string{
   357  							role: "backend",
   358  						},
   359  						nil,
   360  					),
   361  					Ingress: []api.IngressRule{
   362  						{
   363  							IngressCommonRule: api.IngressCommonRule{
   364  								FromEndpoints: []api.EndpointSelector{
   365  									{
   366  										LabelSelector: &slim_metav1.LabelSelector{
   367  											MatchLabels: map[string]string{
   368  												podAnyNamespaceLabelsPrefix + "team": "team-a",
   369  											},
   370  										},
   371  									},
   372  								},
   373  							},
   374  						},
   375  					},
   376  				},
   377  			},
   378  			want: api.NewRule().WithEndpointSelector(
   379  				api.NewESFromMatchRequirements(
   380  					map[string]string{
   381  						role:      "backend",
   382  						namespace: "default",
   383  					},
   384  					nil,
   385  				),
   386  			).WithIngressRules(
   387  				[]api.IngressRule{
   388  					{
   389  						IngressCommonRule: api.IngressCommonRule{
   390  							FromEndpoints: []api.EndpointSelector{
   391  								api.NewESFromK8sLabelSelector(
   392  									labels.LabelSourceAnyKeyPrefix,
   393  									&slim_metav1.LabelSelector{
   394  										MatchLabels: map[string]string{
   395  											k8sConst.PodNamespaceMetaLabelsPrefix + "team": "team-a",
   396  										},
   397  									}),
   398  							},
   399  						},
   400  					},
   401  				},
   402  			).WithLabels(
   403  				labels.LabelArray{
   404  					{
   405  						Key:    "io.cilium.k8s.policy.derived-from",
   406  						Value:  "CiliumNetworkPolicy",
   407  						Source: labels.LabelSourceK8s,
   408  					},
   409  					{
   410  						Key:    "io.cilium.k8s.policy.name",
   411  						Value:  "parse-in-namespace-with-ns-labels-selector",
   412  						Source: labels.LabelSourceK8s,
   413  					},
   414  					{
   415  						Key:    "io.cilium.k8s.policy.namespace",
   416  						Value:  "default",
   417  						Source: labels.LabelSourceK8s,
   418  					},
   419  					{
   420  						Key:    "io.cilium.k8s.policy.uid",
   421  						Value:  string(uuid),
   422  						Source: labels.LabelSourceK8s,
   423  					},
   424  				},
   425  			),
   426  		},
   427  		{
   428  			// For a clusterwide policy the namespace is empty but when a to/fromEndpoint
   429  			// rule is added that represents a wildcard we add a match expression
   430  			// to account only for endpoints managed by cilium.
   431  			name: "wildcard-to-from-endpoints-with-ccnp",
   432  			args: args{
   433  				// Empty namespace for Clusterwide policy
   434  				namespace: "",
   435  				uid:       uuid,
   436  				rule: &api.Rule{
   437  					EndpointSelector: api.NewESFromMatchRequirements(
   438  						map[string]string{
   439  							role: "backend",
   440  						},
   441  						nil,
   442  					),
   443  					Ingress: []api.IngressRule{
   444  						{
   445  							IngressCommonRule: api.IngressCommonRule{
   446  								FromEndpoints: []api.EndpointSelector{
   447  									{
   448  										LabelSelector: &slim_metav1.LabelSelector{},
   449  									},
   450  								},
   451  							},
   452  						},
   453  					},
   454  				},
   455  			},
   456  			want: api.NewRule().WithEndpointSelector(
   457  				api.NewESFromMatchRequirements(
   458  					map[string]string{
   459  						role: "backend",
   460  					},
   461  					nil,
   462  				),
   463  			).WithIngressRules(
   464  				[]api.IngressRule{
   465  					{
   466  						IngressCommonRule: api.IngressCommonRule{
   467  							FromEndpoints: []api.EndpointSelector{
   468  								api.NewESFromK8sLabelSelector(
   469  									labels.LabelSourceK8sKeyPrefix,
   470  									&slim_metav1.LabelSelector{
   471  										MatchExpressions: []slim_metav1.LabelSelectorRequirement{
   472  											{
   473  												Key:      k8sConst.PodNamespaceLabel,
   474  												Operator: slim_metav1.LabelSelectorOpExists,
   475  												Values:   []string{},
   476  											},
   477  										},
   478  									}),
   479  							},
   480  						},
   481  					},
   482  				},
   483  			).WithLabels(
   484  				labels.LabelArray{
   485  					{
   486  						Key:    "io.cilium.k8s.policy.derived-from",
   487  						Value:  "CiliumClusterwideNetworkPolicy",
   488  						Source: labels.LabelSourceK8s,
   489  					},
   490  					{
   491  						Key:    "io.cilium.k8s.policy.name",
   492  						Value:  "wildcard-to-from-endpoints-with-ccnp",
   493  						Source: labels.LabelSourceK8s,
   494  					},
   495  					{
   496  						Key:    "io.cilium.k8s.policy.uid",
   497  						Value:  string(uuid),
   498  						Source: labels.LabelSourceK8s,
   499  					},
   500  				},
   501  			),
   502  		},
   503  	}
   504  	for _, tt := range tests {
   505  		t.Run(tt.name, func(t *testing.T) {
   506  			tt.args.rule.Sanitize()
   507  			got := ParseToCiliumRule(tt.args.namespace, tt.name, tt.args.uid, tt.args.rule)
   508  
   509  			// Sanitize to set AggregatedSelectors field.
   510  			tt.want.Sanitize()
   511  			require.EqualValues(t, tt.want, got, "Test Name: %s", tt.name)
   512  		})
   513  	}
   514  }
   515  
   516  func TestParseToCiliumLabels(t *testing.T) {
   517  
   518  	uuid := types.UID("11bba160-ddca-11e8-b697-0800273b04ff")
   519  	type args struct {
   520  		namespace string
   521  		name      string
   522  		uid       types.UID
   523  		ruleLbs   labels.LabelArray
   524  	}
   525  	tests := []struct {
   526  		name string
   527  		args args
   528  		want labels.LabelArray
   529  	}{
   530  		{
   531  			name: "parse labels",
   532  			args: args{
   533  				name:      "foo",
   534  				namespace: "bar",
   535  				uid:       uuid,
   536  				ruleLbs: labels.LabelArray{
   537  					{
   538  						Key:    "hello",
   539  						Value:  "world",
   540  						Source: labels.LabelSourceK8s,
   541  					},
   542  				},
   543  			},
   544  			want: labels.LabelArray{
   545  				{
   546  					Key:    "hello",
   547  					Value:  "world",
   548  					Source: labels.LabelSourceK8s,
   549  				},
   550  				{
   551  					Key:    "io.cilium.k8s.policy.derived-from",
   552  					Value:  "CiliumNetworkPolicy",
   553  					Source: labels.LabelSourceK8s,
   554  				},
   555  				{
   556  					Key:    "io.cilium.k8s.policy.name",
   557  					Value:  "foo",
   558  					Source: labels.LabelSourceK8s,
   559  				},
   560  				{
   561  					Key:    "io.cilium.k8s.policy.namespace",
   562  					Value:  "bar",
   563  					Source: labels.LabelSourceK8s,
   564  				},
   565  				{
   566  					Key:    "io.cilium.k8s.policy.uid",
   567  					Value:  string(uuid),
   568  					Source: labels.LabelSourceK8s,
   569  				},
   570  			},
   571  		},
   572  	}
   573  	for _, tt := range tests {
   574  		got := ParseToCiliumLabels(tt.args.namespace, tt.args.name, tt.args.uid, tt.args.ruleLbs)
   575  		require.EqualValuesf(t, tt.want, got, "Test Name: %s", tt.name)
   576  	}
   577  }