github.com/containerd/nerdctl@v1.7.7/cmd/nerdctl/container_run_log_driver_syslog_test.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"runtime"
    23  	"strconv"
    24  	"strings"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/containerd/nerdctl/pkg/rootlessutil"
    29  	"github.com/containerd/nerdctl/pkg/testutil"
    30  	"github.com/containerd/nerdctl/pkg/testutil/testca"
    31  	"github.com/containerd/nerdctl/pkg/testutil/testsyslog"
    32  	syslog "github.com/yuchanns/srslog"
    33  )
    34  
    35  func runSyslogTest(t *testing.T, networks []string, syslogFacilities map[string]syslog.Priority, fmtValidFuncs map[string]func(string, string, string, string, syslog.Priority, bool) error) {
    36  	if runtime.GOOS == "windows" {
    37  		t.Skip("syslog container logging is not officially supported on Windows")
    38  	}
    39  
    40  	base := testutil.NewBase(t)
    41  	base.Cmd("pull", testutil.CommonImage).AssertOK()
    42  	hostname, err := os.Hostname()
    43  	if err != nil {
    44  		t.Fatalf("Error retrieving hostname")
    45  	}
    46  	ca := testca.New(base.T)
    47  	cert := ca.NewCert("127.0.0.1")
    48  	t.Cleanup(func() {
    49  		cert.Close()
    50  		ca.Close()
    51  	})
    52  	rI := 0
    53  	for _, network := range networks {
    54  		for rFK, rFV := range syslogFacilities {
    55  			fPriV := rFV
    56  			// test both string and number facility
    57  			for _, fPriK := range []string{rFK, strconv.Itoa(int(fPriV) >> 3)} {
    58  				for fmtK, fmtValidFunc := range fmtValidFuncs {
    59  					fmtKT := "empty"
    60  					if fmtK != "" {
    61  						fmtKT = fmtK
    62  					}
    63  					subTestName := fmt.Sprintf("%s_%s_%s", strings.ReplaceAll(network, "+", "_"), fPriK, fmtKT)
    64  					i := rI
    65  					rI++
    66  					t.Run(subTestName, func(t *testing.T) {
    67  						tID := testutil.Identifier(t)
    68  						tag := tID + "_syslog_driver"
    69  						msg := "hello, " + tID + "_syslog_driver"
    70  						if !testsyslog.TestableNetwork(network) {
    71  							if rootlessutil.IsRootless() {
    72  								t.Skipf("skipping on %s/%s; '%s' for rootless containers are not supported", runtime.GOOS, runtime.GOARCH, network)
    73  							}
    74  							t.Skipf("skipping on %s/%s; '%s' is not supported", runtime.GOOS, runtime.GOARCH, network)
    75  						}
    76  						testContainerName := fmt.Sprintf("%s-%d-%s", tID, i, fPriK)
    77  						done := make(chan string)
    78  						addr, closer := testsyslog.StartServer(network, "", done, cert)
    79  						args := []string{
    80  							"run",
    81  							"-d",
    82  							"--name",
    83  							testContainerName,
    84  							"--restart=no",
    85  							"--log-driver=syslog",
    86  							"--log-opt=syslog-facility=" + fPriK,
    87  							"--log-opt=tag=" + tag,
    88  							"--log-opt=syslog-format=" + fmtK,
    89  							"--log-opt=syslog-address=" + fmt.Sprintf("%s://%s", network, addr),
    90  						}
    91  						if network == "tcp+tls" {
    92  							args = append(args,
    93  								"--log-opt=syslog-tls-cert="+cert.CertPath,
    94  								"--log-opt=syslog-tls-key="+cert.KeyPath,
    95  								"--log-opt=syslog-tls-ca-cert="+ca.CertPath,
    96  							)
    97  						}
    98  						args = append(args, testutil.CommonImage, "echo", msg)
    99  						base.Cmd(args...).AssertOK()
   100  						t.Cleanup(func() {
   101  							base.Cmd("rm", "-f", testContainerName).AssertOK()
   102  						})
   103  						defer closer.Close()
   104  						defer close(done)
   105  						select {
   106  						case rcvd := <-done:
   107  							if err := fmtValidFunc(rcvd, msg, tag, hostname, fPriV, network == "tcp+tls"); err != nil {
   108  								t.Error(err)
   109  							}
   110  						case <-time.Tick(time.Second * 3):
   111  							t.Errorf("timeout with %s", subTestName)
   112  						}
   113  					})
   114  				}
   115  			}
   116  		}
   117  	}
   118  }
   119  
   120  func TestSyslogNetwork(t *testing.T) {
   121  	var syslogFacilities = map[string]syslog.Priority{
   122  		"user": syslog.LOG_USER,
   123  	}
   124  
   125  	networks := []string{
   126  		"udp",
   127  		"tcp",
   128  		"tcp+tls",
   129  		"unix",
   130  		"unixgram",
   131  	}
   132  	fmtValidFuncs := map[string]func(string, string, string, string, syslog.Priority, bool) error{
   133  		"rfc5424": func(rcvd, msg, tag, hostname string, pri syslog.Priority, isTLS bool) error {
   134  			var parsedHostname, timestamp string
   135  			var length, version, pid int
   136  			if !isTLS {
   137  				exp := fmt.Sprintf("<%d>", pri|syslog.LOG_INFO) + "%d %s %s " + tag + " %d " + tag + " - " + msg + "\n"
   138  				if n, err := fmt.Sscanf(rcvd, exp, &version, &timestamp, &parsedHostname, &pid); n != 4 || err != nil || hostname != parsedHostname {
   139  					return fmt.Errorf("s.Info() = '%q', didn't match '%q' (%d %s)", rcvd, exp, n, err)
   140  				}
   141  			} else {
   142  				exp := "%d " + fmt.Sprintf("<%d>", pri|syslog.LOG_INFO) + "%d %s %s " + tag + " %d " + tag + " - " + msg + "\n"
   143  				if n, err := fmt.Sscanf(rcvd, exp, &length, &version, &timestamp, &parsedHostname, &pid); n != 5 || err != nil || hostname != parsedHostname {
   144  					return fmt.Errorf("s.Info() = '%q', didn't match '%q' (%d %s)", rcvd, exp, n, err)
   145  				}
   146  			}
   147  			return nil
   148  		},
   149  	}
   150  	runSyslogTest(t, networks, syslogFacilities, fmtValidFuncs)
   151  }
   152  
   153  func TestSyslogFacilities(t *testing.T) {
   154  	var syslogFacilities = map[string]syslog.Priority{
   155  		"kern":     syslog.LOG_KERN,
   156  		"user":     syslog.LOG_USER,
   157  		"mail":     syslog.LOG_MAIL,
   158  		"daemon":   syslog.LOG_DAEMON,
   159  		"auth":     syslog.LOG_AUTH,
   160  		"syslog":   syslog.LOG_SYSLOG,
   161  		"lpr":      syslog.LOG_LPR,
   162  		"news":     syslog.LOG_NEWS,
   163  		"uucp":     syslog.LOG_UUCP,
   164  		"cron":     syslog.LOG_CRON,
   165  		"authpriv": syslog.LOG_AUTHPRIV,
   166  		"ftp":      syslog.LOG_FTP,
   167  		"local0":   syslog.LOG_LOCAL0,
   168  		"local1":   syslog.LOG_LOCAL1,
   169  		"local2":   syslog.LOG_LOCAL2,
   170  		"local3":   syslog.LOG_LOCAL3,
   171  		"local4":   syslog.LOG_LOCAL4,
   172  		"local5":   syslog.LOG_LOCAL5,
   173  		"local6":   syslog.LOG_LOCAL6,
   174  		"local7":   syslog.LOG_LOCAL7,
   175  	}
   176  
   177  	networks := []string{"unix"}
   178  	fmtValidFuncs := map[string]func(string, string, string, string, syslog.Priority, bool) error{
   179  		"rfc5424": func(rcvd, msg, tag, hostname string, pri syslog.Priority, isTLS bool) error {
   180  			var parsedHostname, timestamp string
   181  			var length, version, pid int
   182  			if !isTLS {
   183  				exp := fmt.Sprintf("<%d>", pri|syslog.LOG_INFO) + "%d %s %s " + tag + " %d " + tag + " - " + msg + "\n"
   184  				if n, err := fmt.Sscanf(rcvd, exp, &version, &timestamp, &parsedHostname, &pid); n != 4 || err != nil || hostname != parsedHostname {
   185  					return fmt.Errorf("s.Info() = '%q', didn't match '%q' (%d %s)", rcvd, exp, n, err)
   186  				}
   187  			} else {
   188  				exp := "%d " + fmt.Sprintf("<%d>", pri|syslog.LOG_INFO) + "%d %s %s " + tag + " %d " + tag + " - " + msg + "\n"
   189  				if n, err := fmt.Sscanf(rcvd, exp, &length, &version, &timestamp, &parsedHostname, &pid); n != 5 || err != nil || hostname != parsedHostname {
   190  					return fmt.Errorf("s.Info() = '%q', didn't match '%q' (%d %s)", rcvd, exp, n, err)
   191  				}
   192  			}
   193  			return nil
   194  		},
   195  	}
   196  	runSyslogTest(t, networks, syslogFacilities, fmtValidFuncs)
   197  }
   198  
   199  func TestSyslogFormat(t *testing.T) {
   200  	var syslogFacilities = map[string]syslog.Priority{
   201  		"user": syslog.LOG_USER,
   202  	}
   203  
   204  	networks := []string{"unix"}
   205  	fmtValidFuncs := map[string]func(string, string, string, string, syslog.Priority, bool) error{
   206  		"": func(rcvd, msg, tag, hostname string, pri syslog.Priority, isSTLS bool) error {
   207  			var mon, day, hrs string
   208  			var pid int
   209  			exp := fmt.Sprintf("<%d>", pri|syslog.LOG_INFO) + "%s %s %s " + tag + "[%d]: " + msg + "\n"
   210  			if n, err := fmt.Sscanf(rcvd, exp, &mon, &day, &hrs, &pid); n != 4 || err != nil {
   211  				return fmt.Errorf("s.Info() = '%q', didn't match '%q' (%d %s)", rcvd, exp, n, err)
   212  			}
   213  			return nil
   214  		},
   215  		"rfc3164": func(rcvd, msg, tag, hostname string, pri syslog.Priority, isTLS bool) error {
   216  			var parsedHostname, mon, day, hrs string
   217  			var pid int
   218  			exp := fmt.Sprintf("<%d>", pri|syslog.LOG_INFO) + "%s %s %s %s " + tag + "[%d]: " + msg + "\n"
   219  			if n, err := fmt.Sscanf(rcvd, exp, &mon, &day, &hrs, &parsedHostname, &pid); n != 5 || err != nil || hostname != parsedHostname {
   220  				return fmt.Errorf("s.Info() = '%q', didn't match '%q' (%d %s)", rcvd, exp, n, err)
   221  			}
   222  			return nil
   223  		},
   224  		"rfc5424": func(rcvd, msg, tag, hostname string, pri syslog.Priority, isTLS bool) error {
   225  			var parsedHostname, timestamp string
   226  			var length, version, pid int
   227  			if !isTLS {
   228  				exp := fmt.Sprintf("<%d>", pri|syslog.LOG_INFO) + "%d %s %s " + tag + " %d " + tag + " - " + msg + "\n"
   229  				if n, err := fmt.Sscanf(rcvd, exp, &version, &timestamp, &parsedHostname, &pid); n != 4 || err != nil || hostname != parsedHostname {
   230  					return fmt.Errorf("s.Info() = '%q', didn't match '%q' (%d %s)", rcvd, exp, n, err)
   231  				}
   232  			} else {
   233  				exp := "%d " + fmt.Sprintf("<%d>", pri|syslog.LOG_INFO) + "%d %s %s " + tag + " %d " + tag + " - " + msg + "\n"
   234  				if n, err := fmt.Sscanf(rcvd, exp, &length, &version, &timestamp, &parsedHostname, &pid); n != 5 || err != nil || hostname != parsedHostname {
   235  					return fmt.Errorf("s.Info() = '%q', didn't match '%q' (%d %s)", rcvd, exp, n, err)
   236  				}
   237  			}
   238  			return nil
   239  		},
   240  		"rfc5424micro": func(rcvd, msg, tag, hostname string, pri syslog.Priority, isTLS bool) error {
   241  			var parsedHostname, timestamp string
   242  			var length, version, pid int
   243  			if !isTLS {
   244  				exp := fmt.Sprintf("<%d>", pri|syslog.LOG_INFO) + "%d %s %s " + tag + " %d " + tag + " - " + msg + "\n"
   245  				if n, err := fmt.Sscanf(rcvd, exp, &version, &timestamp, &parsedHostname, &pid); n != 4 || err != nil || hostname != parsedHostname {
   246  					return fmt.Errorf("s.Info() = '%q', didn't match '%q' (%d %s)", rcvd, exp, n, err)
   247  				}
   248  			} else {
   249  				exp := "%d " + fmt.Sprintf("<%d>", pri|syslog.LOG_INFO) + "%d %s %s " + tag + " %d " + tag + " - " + msg + "\n"
   250  				if n, err := fmt.Sscanf(rcvd, exp, &length, &version, &timestamp, &parsedHostname, &pid); n != 5 || err != nil || hostname != parsedHostname {
   251  					return fmt.Errorf("s.Info() = '%q', didn't match '%q' (%d %s)", rcvd, exp, n, err)
   252  				}
   253  			}
   254  			return nil
   255  		},
   256  	}
   257  	runSyslogTest(t, networks, syslogFacilities, fmtValidFuncs)
   258  }