github.com/looshlee/cilium@v1.6.12/test/runtime/monitor.go (about)

     1  // Copyright 2017 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  package RuntimeTest
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"math/rand"
    21  	"strings"
    22  	"time"
    23  
    24  	. "github.com/cilium/cilium/test/ginkgo-ext"
    25  	"github.com/cilium/cilium/test/helpers"
    26  
    27  	. "github.com/onsi/gomega"
    28  )
    29  
    30  func init() {
    31  	// ensure that our random numbers are seeded differently on each run
    32  	rand.Seed(time.Now().UnixNano())
    33  }
    34  
    35  const (
    36  	// MonitorDropNotification represents the DropNotification configuration
    37  	// value for the Cilium monitor
    38  	MonitorDropNotification = "DropNotification"
    39  
    40  	// MonitorTraceNotification represents the TraceNotification configuration
    41  	// value for the Cilium monitor
    42  	MonitorTraceNotification = "TraceNotification"
    43  )
    44  
    45  var _ = Describe("RuntimeMonitorTest", func() {
    46  
    47  	var vm *helpers.SSHMeta
    48  
    49  	BeforeAll(func() {
    50  		vm = helpers.InitRuntimeHelper(helpers.Runtime, logger)
    51  		ExpectCiliumReady(vm)
    52  
    53  		areEndpointsReady := vm.WaitEndpointsReady()
    54  		Expect(areEndpointsReady).Should(BeTrue())
    55  	})
    56  
    57  	JustAfterEach(func() {
    58  		vm.ValidateNoErrorsInLogs(CurrentGinkgoTestDescription().Duration)
    59  	})
    60  
    61  	AfterFailed(func() {
    62  		vm.ReportFailed()
    63  	})
    64  
    65  	BeforeEach(func() {
    66  		ExpectPolicyEnforcementUpdated(vm, helpers.PolicyEnforcementDefault)
    67  	})
    68  
    69  	AfterAll(func() {
    70  		vm.CloseSSHClient()
    71  	})
    72  
    73  	Context("With Sample Containers", func() {
    74  
    75  		BeforeAll(func() {
    76  			vm.SampleContainersActions(helpers.Create, helpers.CiliumDockerNetwork)
    77  		})
    78  
    79  		AfterEach(func() {
    80  			_ = vm.PolicyDelAll()
    81  		})
    82  
    83  		AfterAll(func() {
    84  			vm.SampleContainersActions(helpers.Delete, helpers.CiliumDockerNetwork)
    85  		})
    86  
    87  		monitorConfig := func() {
    88  			res := vm.ExecCilium(fmt.Sprintf("config %s=true %s=true",
    89  				MonitorDropNotification, MonitorTraceNotification))
    90  			ExpectWithOffset(1, res.WasSuccessful()).To(BeTrue(), "cannot update monitor config")
    91  		}
    92  
    93  		It("Cilium monitor verbose mode", func() {
    94  			monitorConfig()
    95  
    96  			ctx, cancel := context.WithCancel(context.Background())
    97  			res := vm.ExecInBackground(ctx, "cilium monitor -v")
    98  			defer cancel()
    99  
   100  			areEndpointsReady := vm.WaitEndpointsReady()
   101  			Expect(areEndpointsReady).Should(BeTrue())
   102  
   103  			endpoints, err := vm.GetEndpointsIds()
   104  			Expect(err).Should(BeNil())
   105  
   106  			for k, v := range endpoints {
   107  				filter := fmt.Sprintf("FROM %s DEBUG:", v)
   108  				vm.ContainerExec(k, helpers.Ping(helpers.Httpd1))
   109  				Expect(res.WaitUntilMatch(filter)).To(BeNil(),
   110  					"%q is not in the output after timeout", filter)
   111  				Expect(res.Output().String()).Should(ContainSubstring(filter))
   112  			}
   113  		})
   114  
   115  		It("Cilium monitor event types", func() {
   116  			monitorConfig()
   117  
   118  			_, err := vm.PolicyImportAndWait(vm.GetFullPath(policiesL3JSON), helpers.HelperTimeout)
   119  			Expect(err).Should(BeNil())
   120  
   121  			areEndpointsReady := vm.WaitEndpointsReady()
   122  			Expect(areEndpointsReady).Should(BeTrue(), "Endpoints are not ready after timeout")
   123  
   124  			eventTypes := map[string]string{
   125  				"drop":    "DROP:",
   126  				"debug":   "DEBUG:",
   127  				"capture": "DEBUG:",
   128  			}
   129  
   130  			for k, v := range eventTypes {
   131  				By("Type %s", k)
   132  
   133  				ctx, cancel := context.WithCancel(context.Background())
   134  				defer cancel()
   135  				res := vm.ExecInBackground(ctx, fmt.Sprintf("cilium monitor --type %s -v", k))
   136  
   137  				vm.ContainerExec(helpers.App1, helpers.Ping(helpers.Httpd1))
   138  				vm.ContainerExec(helpers.App3, helpers.Ping(helpers.Httpd1))
   139  
   140  				Expect(res.WaitUntilMatch(v)).To(BeNil(),
   141  					"%q is not in the output after timeout", v)
   142  				Expect(res.CountLines()).Should(BeNumerically(">", 3))
   143  				Expect(res.Output().String()).Should(ContainSubstring(v))
   144  				cancel()
   145  			}
   146  
   147  			By("all types together")
   148  			command := "cilium monitor -v"
   149  			for k := range eventTypes {
   150  				command = command + " --type " + k
   151  			}
   152  
   153  			ctx, cancel := context.WithCancel(context.Background())
   154  			defer cancel()
   155  
   156  			By(command)
   157  			res := vm.ExecInBackground(ctx, command)
   158  
   159  			areEndpointsReady = vm.WaitEndpointsReady()
   160  			Expect(areEndpointsReady).Should(BeTrue())
   161  
   162  			vm.ContainerExec(helpers.App3, helpers.Ping(helpers.Httpd1))
   163  			vm.ContainerExec(helpers.App1, helpers.Ping(helpers.Httpd1))
   164  
   165  			for _, v := range eventTypes {
   166  				Expect(res.WaitUntilMatch(v)).To(BeNil(),
   167  					"%q is not in the output after timeout", v)
   168  				Expect(res.Output().String()).Should(ContainSubstring(v))
   169  			}
   170  
   171  			Expect(res.CountLines()).Should(BeNumerically(">", 3))
   172  		})
   173  
   174  		It("cilium monitor check --from", func() {
   175  			monitorConfig()
   176  
   177  			areEndpointsReady := vm.WaitEndpointsReady()
   178  			Expect(areEndpointsReady).Should(BeTrue())
   179  
   180  			endpoints, err := vm.GetEndpointsIds()
   181  			Expect(err).Should(BeNil())
   182  
   183  			ctx, cancel := context.WithCancel(context.Background())
   184  			defer cancel()
   185  
   186  			res := vm.ExecInBackground(ctx, fmt.Sprintf(
   187  				"cilium monitor --type debug --from %s -v", endpoints[helpers.App1]))
   188  			vm.ContainerExec(helpers.App1, helpers.Ping(helpers.Httpd1))
   189  
   190  			filter := fmt.Sprintf("FROM %s DEBUG:", endpoints[helpers.App1])
   191  			Expect(res.WaitUntilMatch(filter)).To(BeNil(),
   192  				"%q is not in the output after timeout", filter)
   193  			Expect(res.CountLines()).Should(BeNumerically(">", 3))
   194  			Expect(res.Output().String()).Should(ContainSubstring(filter))
   195  
   196  			//MonitorDebug mode shouldn't have DROP lines
   197  			Expect(res.Output().String()).ShouldNot(ContainSubstring("DROP"))
   198  		})
   199  
   200  		It("cilium monitor check --to", func() {
   201  			monitorConfig()
   202  
   203  			areEndpointsReady := vm.WaitEndpointsReady()
   204  			Expect(areEndpointsReady).Should(BeTrue())
   205  
   206  			endpoints, err := vm.GetEndpointsIds()
   207  			Expect(err).Should(BeNil())
   208  
   209  			ctx, cancel := context.WithCancel(context.Background())
   210  			defer cancel()
   211  			res := vm.ExecInBackground(ctx, fmt.Sprintf(
   212  				"cilium monitor -v --to %s", endpoints[helpers.Httpd1]))
   213  
   214  			vm.ContainerExec(helpers.App1, helpers.Ping(helpers.Httpd1))
   215  			vm.ContainerExec(helpers.App2, helpers.Ping(helpers.Httpd1))
   216  
   217  			filter := fmt.Sprintf("to endpoint %s", endpoints[helpers.Httpd1])
   218  			Expect(res.WaitUntilMatch(filter)).To(BeNil(),
   219  				"%q is not in the output after timeout", filter)
   220  			Expect(res.CountLines()).Should(BeNumerically(">=", 3))
   221  			Expect(res.Output().String()).Should(ContainSubstring(filter))
   222  		})
   223  
   224  		It("cilium monitor check --related-to", func() {
   225  			monitorConfig()
   226  
   227  			areEndpointsReady := vm.WaitEndpointsReady()
   228  			Expect(areEndpointsReady).Should(BeTrue())
   229  
   230  			endpoints, err := vm.GetEndpointsIds()
   231  			Expect(err).Should(BeNil())
   232  
   233  			ctx, cancel := context.WithCancel(context.Background())
   234  			defer cancel()
   235  			res := vm.ExecInBackground(ctx, fmt.Sprintf(
   236  				"cilium monitor -v --related-to %s", endpoints[helpers.Httpd1]))
   237  
   238  			vm.WaitEndpointsReady()
   239  			vm.ContainerExec(helpers.App1, helpers.CurlFail("http://httpd1/public"))
   240  
   241  			filter := fmt.Sprintf("FROM %s DEBUG:", endpoints[helpers.Httpd1])
   242  			Expect(res.WaitUntilMatch(filter)).To(BeNil(),
   243  				"%q is not in the output after timeout", filter)
   244  			Expect(res.CountLines()).Should(BeNumerically(">=", 3))
   245  			Expect(res.Output().String()).Should(ContainSubstring(filter))
   246  		})
   247  
   248  		It("delivers the same information to multiple monitors", func() {
   249  			monitorConfig()
   250  
   251  			areEndpointsReady := vm.WaitEndpointsReady()
   252  			Expect(areEndpointsReady).Should(BeTrue(), "Endpoints are not ready after timeout")
   253  
   254  			var monitorRes []*helpers.CmdRes
   255  			ctx, cancelfn := context.WithCancel(context.Background())
   256  
   257  			for i := 1; i <= 3; i++ {
   258  				monitorRes = append(monitorRes, vm.ExecInBackground(ctx, "cilium monitor"))
   259  			}
   260  
   261  			vm.ContainerExec(helpers.App1, helpers.Ping(helpers.Httpd1))
   262  			cancelfn()
   263  
   264  			for _, res := range monitorRes {
   265  				res.WaitUntilFinish()
   266  			}
   267  
   268  			Expect(monitorRes[0].CountLines()).Should(BeNumerically(">", 2))
   269  
   270  			// Some monitor instances may see more data than others due to timing. We
   271  			// want to find a run of lines that matches between all monitor
   272  			// instances, ignoring earlier or later lines that may have not been seen
   273  			// by an instance. We find the shortest sample and check that those lines
   274  			// occur in all monitor responses.
   275  			var (
   276  				// shortestResult is the smallest set of monitor output lines, after we
   277  				// trim leading init lines.
   278  				shortestResult string
   279  
   280  				// Output lines from each monitor, trimmed to ignore init messages.
   281  				// The order matches monitorRes.
   282  				trimmedResults []string
   283  			)
   284  
   285  			// Trim the result lines to ignore startup/shutdown messages like
   286  			//    "level=info msg="Initializing dissection cache..." subsys=monitor"
   287  			//    "Received an interrupt, disconnecting from monitor..."
   288  			// and note the shortest
   289  			for _, result := range monitorRes {
   290  				lines := result.ByLines()
   291  				trimmedResult := make([]string, 0, len(lines))
   292  				for _, line := range lines {
   293  					if strings.HasPrefix(line, " <- endpoint") {
   294  						trimmedResult = append(trimmedResult, line)
   295  					}
   296  				}
   297  				trimmedResults = append(trimmedResults, strings.Join(trimmedResult, "\n"))
   298  
   299  				if len(trimmedResult) < len(shortestResult) {
   300  					shortestResult = strings.Join(trimmedResult, "\n")
   301  				}
   302  			}
   303  
   304  			// The shortest output must occur in whole within the other outputs
   305  			for _, trimmedResult := range trimmedResults {
   306  				Expect(strings.Contains(trimmedResult, shortestResult)).Should(Equal(true),
   307  					"Inconsistent monitor output between 2 monitor instances during the same time period\nExpected:\n%s\nFound:\n%s\n", shortestResult, trimmedResult)
   308  			}
   309  		})
   310  
   311  		It("checks container ids match monitor output", func() {
   312  			ExpectPolicyEnforcementUpdated(vm, helpers.PolicyEnforcementAlways)
   313  
   314  			ctx, cancel := context.WithCancel(context.Background())
   315  			res := vm.ExecInBackground(ctx, "cilium monitor -v")
   316  
   317  			vm.ContainerExec(helpers.App1, helpers.Ping(helpers.Httpd1))
   318  			vm.ContainerExec(helpers.Httpd1, helpers.Ping(helpers.App1))
   319  
   320  			endpoints, err := vm.GetEndpointsIDMap()
   321  			Expect(err).Should(BeNil())
   322  
   323  			helpers.Sleep(10)
   324  			cancel()
   325  
   326  			// Expected full example output:
   327  			// CPU 01: MARK 0x3de3947b FROM 48896 DEBUG: Attempting local delivery for container id 29381 from seclabel 263
   328  			//                              ^                                                       ^
   329  			for _, line := range res.ByLines() {
   330  				var toID, fromID string
   331  
   332  				fields := strings.Split(line, " ")
   333  				for i := range fields {
   334  					switch fields[i] {
   335  					case "FROM":
   336  						fromID = fields[i+1]
   337  						break
   338  					case "id":
   339  						toID = fields[i+1]
   340  						break
   341  					}
   342  				}
   343  				if fromID == "" || toID == "" {
   344  					continue
   345  				}
   346  				By("checking endpoints in monitor line:\n%q", line)
   347  
   348  				Expect(toID).Should(Not(Equal(fromID)))
   349  				Expect(endpoints[toID]).Should(Not(BeNil()))
   350  				Expect(endpoints[fromID]).Should(Not(BeNil()))
   351  			}
   352  		})
   353  	})
   354  })