github.com/fafucoder/cilium@v1.6.11/pkg/endpoint/endpoint_status_test.go (about)

     1  // Copyright 2019 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // +build !privileged_tests
    16  
    17  package endpoint
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"reflect"
    23  	"time"
    24  
    25  	"github.com/cilium/cilium/api/v1/models"
    26  	"github.com/cilium/cilium/pkg/checker"
    27  	"github.com/cilium/cilium/pkg/controller"
    28  	"github.com/cilium/cilium/pkg/identity"
    29  	"github.com/cilium/cilium/pkg/identity/cache"
    30  	cilium_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
    31  	"github.com/cilium/cilium/pkg/labels"
    32  	"github.com/cilium/cilium/pkg/policy"
    33  	"github.com/cilium/cilium/pkg/policy/trafficdirection"
    34  
    35  	"gopkg.in/check.v1"
    36  )
    37  
    38  var (
    39  	allowAllIdentityList = cilium_v2.AllowedIdentityList{{}}
    40  	denyAllIdentityList  = cilium_v2.AllowedIdentityList(nil)
    41  )
    42  
    43  type endpointGeneratorSpec struct {
    44  	failingControllers       int
    45  	logErrors                int
    46  	allowedIngressIdentities int
    47  	allowedEgressIdentities  int
    48  	numPortsPerIdentity      int
    49  	fakeControllerManager    bool
    50  }
    51  
    52  func (s *EndpointSuite) newEndpoint(c *check.C, spec endpointGeneratorSpec) *Endpoint {
    53  	e, err := NewEndpointFromChangeModel(s, &models.EndpointChangeRequest{
    54  		Addressing: &models.AddressPair{},
    55  		ID:         200,
    56  		Labels: models.Labels{
    57  			"k8s:io.cilium.k8s.policy.cluster=default",
    58  			"k8s:io.cilium.k8s.policy.serviceaccount=default",
    59  			"k8s:io.kubernetes.pod.namespace=default",
    60  			"k8s:name=probe",
    61  		},
    62  		State: models.EndpointState("waiting-for-identity"),
    63  	})
    64  	c.Assert(err, check.IsNil)
    65  
    66  	e.SecurityIdentity = &identity.Identity{
    67  		ID: 100,
    68  		Labels: labels.NewLabelsFromModel([]string{
    69  			"k8s:io.cilium.k8s.policy.cluster=default",
    70  			"k8s:io.cilium.k8s.policy.serviceaccount=default",
    71  			"k8s:io.kubernetes.pod.namespace=default",
    72  			"k8s:name=probe",
    73  		}),
    74  	}
    75  
    76  	if spec.fakeControllerManager {
    77  		e.controllers = controller.FakeManager(spec.failingControllers)
    78  	}
    79  
    80  	for i := 0; i < spec.logErrors; i++ {
    81  		e.Status.addStatusLog(&statusLogMsg{
    82  			Status: Status{Code: Failure, Msg: "Failure", Type: BPF},
    83  		})
    84  	}
    85  
    86  	e.desiredPolicy.PolicyMapState = policy.MapState{}
    87  
    88  	if spec.numPortsPerIdentity == 0 {
    89  		spec.numPortsPerIdentity = 1
    90  	}
    91  
    92  	for i := 0; i < spec.allowedIngressIdentities; i++ {
    93  		for n := 0; n < spec.numPortsPerIdentity; n++ {
    94  			key := policy.Key{
    95  				Identity:         uint32(i),
    96  				DestPort:         uint16(80 + n),
    97  				TrafficDirection: trafficdirection.Ingress.Uint8(),
    98  			}
    99  			e.desiredPolicy.PolicyMapState[key] = policy.MapStateEntry{}
   100  		}
   101  	}
   102  
   103  	for i := 0; i < spec.allowedIngressIdentities; i++ {
   104  		for n := 0; n < spec.numPortsPerIdentity; n++ {
   105  			key := policy.Key{
   106  				Identity:         uint32(i + 30000),
   107  				DestPort:         uint16(80 + n),
   108  				TrafficDirection: trafficdirection.Egress.Uint8(),
   109  			}
   110  			e.desiredPolicy.PolicyMapState[key] = policy.MapStateEntry{}
   111  		}
   112  	}
   113  
   114  	return e
   115  }
   116  
   117  func (s *EndpointSuite) TestGetCiliumEndpointStatusSuccessfulControllers(c *check.C) {
   118  	e := s.newEndpoint(c, endpointGeneratorSpec{})
   119  	cepA := e.GetCiliumEndpointStatus()
   120  
   121  	// Run successful controllers in the background
   122  	for i := 0; i < 50; i++ {
   123  		e.controllers.UpdateController(fmt.Sprintf("controller-%d", i),
   124  			controller.ControllerParams{
   125  				DoFunc: func(ctx context.Context) error {
   126  					return nil
   127  				},
   128  				RunInterval: 10 * time.Millisecond,
   129  			},
   130  		)
   131  	}
   132  	defer e.controllers.RemoveAll()
   133  
   134  	// Generate EndpointStatus in quick interval while controllers are
   135  	// succeeding in the background
   136  	timeout := time.After(1 * time.Second)
   137  	tick := time.Tick(10 * time.Millisecond)
   138  	for {
   139  		select {
   140  		case <-timeout:
   141  			return
   142  		case <-tick:
   143  			cepB := e.GetCiliumEndpointStatus()
   144  			c.Assert(cepA, checker.DeepEquals, cepB)
   145  		}
   146  	}
   147  }
   148  
   149  func (s *EndpointSuite) TestGetCiliumEndpointStatusSuccessfulLog(c *check.C) {
   150  	e := s.newEndpoint(c, endpointGeneratorSpec{})
   151  	cepA := e.GetCiliumEndpointStatus()
   152  
   153  	go func() {
   154  		for i := 0; i < 1000; i++ {
   155  			e.Status.addStatusLog(&statusLogMsg{
   156  				Status: Status{Code: OK, Msg: "Success", Type: BPF},
   157  			})
   158  			time.Sleep(time.Millisecond)
   159  		}
   160  	}()
   161  
   162  	// Generate EndpointStatus in quick interval while state transitions
   163  	// are succeeding in the background
   164  	timeout := time.After(1 * time.Second)
   165  	tick := time.Tick(10 * time.Millisecond)
   166  	for {
   167  		select {
   168  		case <-timeout:
   169  			return
   170  		case <-tick:
   171  			cepB := e.GetCiliumEndpointStatus()
   172  			c.Assert(cepA, checker.DeepEquals, cepB)
   173  		}
   174  	}
   175  }
   176  
   177  func (s *EndpointSuite) TestGetCiliumEndpointStatusDeepEqual(c *check.C) {
   178  	a := s.newEndpoint(c, endpointGeneratorSpec{
   179  		fakeControllerManager:    true,
   180  		failingControllers:       10,
   181  		logErrors:                maxLogs,
   182  		allowedIngressIdentities: 100,
   183  		allowedEgressIdentities:  100,
   184  		numPortsPerIdentity:      10,
   185  	})
   186  
   187  	b := s.newEndpoint(c, endpointGeneratorSpec{
   188  		fakeControllerManager:    true,
   189  		failingControllers:       10,
   190  		logErrors:                maxLogs,
   191  		allowedIngressIdentities: 100,
   192  		allowedEgressIdentities:  100,
   193  		numPortsPerIdentity:      10,
   194  	})
   195  
   196  	cepA := a.GetCiliumEndpointStatus()
   197  	cepB := b.GetCiliumEndpointStatus()
   198  
   199  	c.Assert(cepA, checker.DeepEquals, cepB)
   200  }
   201  
   202  func (s *EndpointSuite) TestGetCiliumEndpointStatusCorrectnes(c *check.C) {
   203  	e := s.newEndpoint(c, endpointGeneratorSpec{
   204  		fakeControllerManager:    true,
   205  		failingControllers:       10,
   206  		logErrors:                maxLogs,
   207  		allowedIngressIdentities: 100,
   208  		allowedEgressIdentities:  100,
   209  		numPortsPerIdentity:      10,
   210  	})
   211  
   212  	cep := e.GetCiliumEndpointStatus()
   213  
   214  	c.Assert(len(cep.Status.Log), check.Equals, cilium_v2.EndpointStatusLogEntries)
   215  }
   216  
   217  // apiResult is an individual desired AllowedIdentityEntry test result entry.
   218  type apiResult struct {
   219  	labels   string
   220  	identity uint64
   221  	dport    uint16
   222  	proto    uint8
   223  }
   224  
   225  func prepareExpectedList(want []apiResult) cilium_v2.AllowedIdentityList {
   226  	expectedList := denyAllIdentityList
   227  	if want != nil {
   228  		expectedList = cilium_v2.AllowedIdentityList{}
   229  		for _, w := range want {
   230  			entry := cilium_v2.AllowedIdentityTuple{
   231  				Identity: w.identity,
   232  				DestPort: w.dport,
   233  				Protocol: w.proto,
   234  			}
   235  			if w.labels != "" {
   236  				entry.IdentityLabels = map[string]string{
   237  					w.labels: "",
   238  				}
   239  			}
   240  			expectedList = append(expectedList, entry)
   241  		}
   242  		expectedList.Sort()
   243  	}
   244  
   245  	return expectedList
   246  }
   247  
   248  func (s *EndpointSuite) TestgetEndpointPolicyMapState(c *check.C) {
   249  	e := s.newEndpoint(c, endpointGeneratorSpec{
   250  		fakeControllerManager:    true,
   251  		failingControllers:       10,
   252  		logErrors:                maxLogs,
   253  		allowedIngressIdentities: 100,
   254  		allowedEgressIdentities:  100,
   255  		numPortsPerIdentity:      10,
   256  	})
   257  	// Policy not enabled; allow all.
   258  	apiPolicy := e.getEndpointPolicy()
   259  	c.Assert(apiPolicy.Ingress.Allowed, checker.DeepEquals, allowAllIdentityList)
   260  	c.Assert(apiPolicy.Egress.Allowed, checker.DeepEquals, allowAllIdentityList)
   261  
   262  	fooLbls := labels.Labels{"": labels.ParseLabel("foo")}
   263  	fooIdentity, _, err := cache.AllocateIdentity(context.Background(), nil, fooLbls)
   264  	c.Assert(err, check.Equals, nil)
   265  	defer cache.Release(context.Background(), nil, fooIdentity)
   266  
   267  	e.desiredPolicy = policy.NewEndpointPolicy(s.repo)
   268  	e.desiredPolicy.IngressPolicyEnabled = true
   269  	e.desiredPolicy.EgressPolicyEnabled = true
   270  
   271  	type args struct {
   272  		identity  uint32
   273  		destPort  uint16
   274  		nexthdr   uint8
   275  		direction trafficdirection.TrafficDirection
   276  	}
   277  
   278  	tests := []struct {
   279  		name          string
   280  		args          []args
   281  		egressResult  []apiResult
   282  		ingressResult []apiResult
   283  	}{
   284  		{
   285  			name: "Deny all",
   286  		},
   287  		{
   288  			name: "Allow all ingress",
   289  			args: []args{
   290  				{0, 0, 0, trafficdirection.Ingress},
   291  			},
   292  			ingressResult: []apiResult{{}},
   293  			egressResult:  nil,
   294  		},
   295  		{
   296  			name: "Allow all egress",
   297  			args: []args{
   298  				{0, 0, 0, trafficdirection.Egress},
   299  			},
   300  			ingressResult: nil,
   301  			egressResult:  []apiResult{{}},
   302  		},
   303  		{
   304  			name: "Allow all both directions",
   305  			args: []args{
   306  				{0, 0, 0, trafficdirection.Ingress},
   307  				{0, 0, 0, trafficdirection.Egress},
   308  			},
   309  			ingressResult: []apiResult{{}},
   310  			egressResult:  []apiResult{{}},
   311  		},
   312  		{
   313  			name: "Allow world ingress",
   314  			args: []args{
   315  				{uint32(identity.ReservedIdentityWorld), 0, 0, trafficdirection.Ingress},
   316  			},
   317  			ingressResult: []apiResult{
   318  				{"reserved:world", uint64(identity.ReservedIdentityWorld), 0, 0},
   319  			},
   320  			egressResult: nil,
   321  		},
   322  		{
   323  			name: "Allow world egress",
   324  			args: []args{
   325  				{uint32(identity.ReservedIdentityWorld), 0, 0, trafficdirection.Egress},
   326  			},
   327  			ingressResult: nil,
   328  			egressResult: []apiResult{
   329  				{"reserved:world", uint64(identity.ReservedIdentityWorld), 0, 0},
   330  			},
   331  		},
   332  		{
   333  			name: "Allow world both directions",
   334  			args: []args{
   335  				{uint32(identity.ReservedIdentityWorld), 0, 0, trafficdirection.Ingress},
   336  				{uint32(identity.ReservedIdentityWorld), 0, 0, trafficdirection.Egress},
   337  			},
   338  			ingressResult: []apiResult{
   339  				{"reserved:world", uint64(identity.ReservedIdentityWorld), 0, 0},
   340  			},
   341  			egressResult: []apiResult{
   342  				{"reserved:world", uint64(identity.ReservedIdentityWorld), 0, 0},
   343  			},
   344  		},
   345  		{
   346  			name: "Ingress mix of L3, L4, L3-dependent L4",
   347  			args: []args{
   348  				{uint32(fooIdentity.ID), 0, 0, trafficdirection.Ingress},  // L3-only map state
   349  				{0, 80, 6, trafficdirection.Ingress},                      // L4-only map state
   350  				{uint32(fooIdentity.ID), 80, 6, trafficdirection.Ingress}, // L3-dependent L4 map state
   351  			},
   352  			ingressResult: []apiResult{
   353  				{"unspec:foo", uint64(fooIdentity.ID), 0, 0},
   354  				{"", 0, 80, 6},
   355  				{"unspec:foo", uint64(fooIdentity.ID), 80, 6},
   356  			},
   357  			egressResult: nil,
   358  		},
   359  		{
   360  			name: "Egress mix of L3, L4, L3-dependent L4",
   361  			args: []args{
   362  				{uint32(fooIdentity.ID), 0, 0, trafficdirection.Egress},  // L3-only map state
   363  				{0, 80, 6, trafficdirection.Egress},                      // L4-only map state
   364  				{uint32(fooIdentity.ID), 80, 6, trafficdirection.Egress}, // L3-dependent L4 map state
   365  			},
   366  			ingressResult: nil,
   367  			egressResult: []apiResult{
   368  				{"unspec:foo", uint64(fooIdentity.ID), 0, 0},
   369  				{"", 0, 80, 6},
   370  				{"unspec:foo", uint64(fooIdentity.ID), 80, 6},
   371  			},
   372  		},
   373  		{
   374  			name: "World shadows CIDR ingress",
   375  			args: []args{
   376  				{uint32(identity.ReservedIdentityWorld), 0, 0, trafficdirection.Ingress},
   377  				{uint32(identity.LocalIdentityFlag), 0, 0, trafficdirection.Ingress},
   378  			},
   379  			ingressResult: []apiResult{
   380  				{"reserved:world", uint64(identity.ReservedIdentityWorld), 0, 0},
   381  			},
   382  			egressResult: nil,
   383  		},
   384  		{
   385  			name: "World shadows CIDR egress",
   386  			args: []args{
   387  				{uint32(identity.ReservedIdentityWorld), 0, 0, trafficdirection.Egress},
   388  				{uint32(identity.LocalIdentityFlag), 0, 0, trafficdirection.Egress},
   389  			},
   390  			ingressResult: nil,
   391  			egressResult: []apiResult{
   392  				{"reserved:world", uint64(identity.ReservedIdentityWorld), 0, 0},
   393  			},
   394  		},
   395  	}
   396  
   397  	for _, tt := range tests {
   398  		e.desiredPolicy.PolicyMapState = policy.MapState{}
   399  		for _, arg := range tt.args {
   400  			t := policy.Key{
   401  				Identity:         arg.identity,
   402  				DestPort:         arg.destPort,
   403  				Nexthdr:          arg.nexthdr,
   404  				TrafficDirection: arg.direction.Uint8(),
   405  			}
   406  			e.desiredPolicy.PolicyMapState[t] = policy.MapStateEntry{}
   407  		}
   408  		expectedIngressList := prepareExpectedList(tt.ingressResult)
   409  		expectedEgressList := prepareExpectedList(tt.egressResult)
   410  
   411  		apiPolicy = e.getEndpointPolicy()
   412  		c.Assert(apiPolicy.Ingress.Allowed, checker.DeepEquals, expectedIngressList)
   413  		c.Assert(apiPolicy.Egress.Allowed, checker.DeepEquals, expectedEgressList)
   414  	}
   415  }
   416  
   417  func (s *EndpointSuite) BenchmarkGetCiliumEndpointStatusDeepEqual(c *check.C) {
   418  	a := s.newEndpoint(c, endpointGeneratorSpec{
   419  		fakeControllerManager:    true,
   420  		failingControllers:       10,
   421  		logErrors:                maxLogs,
   422  		allowedIngressIdentities: 100,
   423  		allowedEgressIdentities:  100,
   424  		numPortsPerIdentity:      10,
   425  	})
   426  
   427  	b := s.newEndpoint(c, endpointGeneratorSpec{
   428  		fakeControllerManager:    true,
   429  		failingControllers:       10,
   430  		logErrors:                maxLogs,
   431  		allowedIngressIdentities: 100,
   432  		allowedEgressIdentities:  100,
   433  		numPortsPerIdentity:      10,
   434  	})
   435  
   436  	c.ResetTimer()
   437  	for i := 0; i < c.N; i++ {
   438  		if !reflect.DeepEqual(a, b) {
   439  			c.Errorf("DeepEqual failed")
   440  		}
   441  	}
   442  	c.StopTimer()
   443  }
   444  
   445  func (s *EndpointSuite) BenchmarkGetCiliumEndpointStatus(c *check.C) {
   446  	e := s.newEndpoint(c, endpointGeneratorSpec{
   447  		failingControllers:       10,
   448  		logErrors:                maxLogs,
   449  		allowedIngressIdentities: 100,
   450  		allowedEgressIdentities:  100,
   451  		numPortsPerIdentity:      10,
   452  	})
   453  
   454  	c.ResetTimer()
   455  	for i := 0; i < c.N; i++ {
   456  		status := e.GetCiliumEndpointStatus()
   457  		c.Assert(status, check.Not(check.IsNil))
   458  	}
   459  	c.StopTimer()
   460  }