From 501643c8bde591fcb8e4c42564f1b854128ad2f7 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Fri, 19 Nov 2021 18:15:53 +0100 Subject: Make sure netavark output is logged to the syslog Create a custom writer which logs the netavark output to logrus. This will log to the syslog when it is enabled. Signed-off-by: Paul Holzinger --- libpod/network/netavark/exec.go | 45 ++++++++++++++++++++++++++++++++------ libpod/network/netavark/network.go | 9 ++++++++ libpod/network/netavark/run.go | 4 ++-- libpod/options.go | 8 +++++++ libpod/runtime.go | 6 +++++ pkg/domain/infra/runtime_libpod.go | 5 +++++ 6 files changed, 68 insertions(+), 9 deletions(-) diff --git a/libpod/network/netavark/exec.go b/libpod/network/netavark/exec.go index d6458eeb4..01dea8489 100644 --- a/libpod/network/netavark/exec.go +++ b/libpod/network/netavark/exec.go @@ -3,6 +3,7 @@ package netavark import ( "encoding/json" "errors" + "io" "os" "os/exec" "strconv" @@ -45,6 +46,15 @@ func newNetavarkError(msg string, err error) error { } } +// Type to implement io.Writer interface +// This will write the logrus at info level +type logrusNetavarkWriter struct{} + +func (l *logrusNetavarkWriter) Write(b []byte) (int, error) { + logrus.Info("netavark: ", string(b)) + return len(b), nil +} + // getRustLogEnv returns the RUST_LOG env var based on the current logrus level func getRustLogEnv() string { level := logrus.GetLevel().String() @@ -63,26 +73,43 @@ func getRustLogEnv() string { // used to marshal the netavark output into it. This can be nil. // All errors return by this function should be of the type netavarkError // to provide a helpful error message. -func execNetavark(binary string, args []string, stdin, result interface{}) error { +func (n *netavarkNetwork) execNetavark(args []string, stdin, result interface{}) error { stdinR, stdinW, err := os.Pipe() if err != nil { return newNetavarkError("failed to create stdin pipe", err) } - defer stdinR.Close() + stdinWClosed := false + defer func() { + stdinR.Close() + if !stdinWClosed { + stdinW.Close() + } + }() stdoutR, stdoutW, err := os.Pipe() if err != nil { return newNetavarkError("failed to create stdout pipe", err) } - defer stdoutR.Close() - defer stdoutW.Close() + stdoutWClosed := false + defer func() { + stdoutR.Close() + if !stdoutWClosed { + stdoutW.Close() + } + }() - cmd := exec.Command(binary, args...) + // connect stderr to the podman stderr for logging + var logWriter io.Writer = os.Stderr + if n.syslog { + // connect logrus to stderr as well so that the logs will be written to the syslog as well + logWriter = io.MultiWriter(logWriter, &logrusNetavarkWriter{}) + } + + cmd := exec.Command(n.netavarkBinary, args...) // connect the pipes to stdin and stdout cmd.Stdin = stdinR cmd.Stdout = stdoutW - // connect stderr to the podman stderr for logging - cmd.Stderr = os.Stderr + cmd.Stderr = logWriter // set the netavark log level to the same as the podman cmd.Env = append(os.Environ(), getRustLogEnv()) // if we run with debug log level lets also set RUST_BACKTRACE=1 so we can get the full stack trace in case of panics @@ -95,7 +122,9 @@ func execNetavark(binary string, args []string, stdin, result interface{}) error return newNetavarkError("failed to start process", err) } err = json.NewEncoder(stdinW).Encode(stdin) + // we have to close stdinW so netavark gets the EOF and does not hang forever stdinW.Close() + stdinWClosed = true if err != nil { return newNetavarkError("failed to encode stdin data", err) } @@ -103,7 +132,9 @@ func execNetavark(binary string, args []string, stdin, result interface{}) error dec := json.NewDecoder(stdoutR) err = cmd.Wait() + // we have to close stdoutW so we can decode the json without hanging forever stdoutW.Close() + stdoutWClosed = true if err != nil { exitError := &exec.ExitError{} if errors.As(err, &exitError) { diff --git a/libpod/network/netavark/network.go b/libpod/network/netavark/network.go index cc6fb423c..540d8d6e5 100644 --- a/libpod/network/netavark/network.go +++ b/libpod/network/netavark/network.go @@ -37,6 +37,10 @@ type netavarkNetwork struct { // isMachine describes whenever podman runs in a podman machine environment. isMachine bool + // syslog describes whenever the netavark debbug output should be log to the syslog as well. + // This will use logrus to do so, make sure logrus is set up to log to the syslog. + syslog bool + // lock is a internal lock for critical operations lock lockfile.Locker @@ -68,6 +72,10 @@ type InitConfig struct { // LockFile is the path to lock file. LockFile string + + // Syslog describes whenever the netavark debbug output should be log to the syslog as well. + // This will use logrus to do so, make sure logrus is set up to log to the syslog. + Syslog bool } // NewNetworkInterface creates the ContainerNetwork interface for the netavark backend. @@ -122,6 +130,7 @@ func NewNetworkInterface(conf InitConfig) (types.ContainerNetwork, error) { defaultSubnet: defaultNet, isMachine: conf.IsMachine, lock: lock, + syslog: conf.Syslog, } return n, nil diff --git a/libpod/network/netavark/run.go b/libpod/network/netavark/run.go index 2f839151e..54917a981 100644 --- a/libpod/network/netavark/run.go +++ b/libpod/network/netavark/run.go @@ -54,7 +54,7 @@ func (n *netavarkNetwork) Setup(namespacePath string, options types.SetupOptions } result := map[string]types.StatusBlock{} - err = execNetavark(n.netavarkBinary, []string{"setup", namespacePath}, netavarkOpts, &result) + err = n.execNetavark([]string{"setup", namespacePath}, netavarkOpts, &result) if len(result) != len(options.Networks) { logrus.Errorf("unexpected netavark result: %v", result) @@ -86,7 +86,7 @@ func (n *netavarkNetwork) Teardown(namespacePath string, options types.TeardownO return errors.Wrap(err, "failed to convert net opts") } - retErr := execNetavark(n.netavarkBinary, []string{"teardown", namespacePath}, netavarkOpts, nil) + retErr := n.execNetavark([]string{"teardown", namespacePath}, netavarkOpts, nil) // when netavark returned an error we still free the used ips // otherwise we could end up in a state where block the ips forever diff --git a/libpod/options.go b/libpod/options.go index 3f0f9fbe0..8f2d5cb15 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -578,6 +578,14 @@ func WithEnableSDNotify() RuntimeOption { } } +// WithSyslog sets a runtime option so we know that we have to log to the syslog as well +func WithSyslog() RuntimeOption { + return func(rt *Runtime) error { + rt.syslog = true + return nil + } +} + // WithRuntimeFlags adds the global runtime flags to the container config func WithRuntimeFlags(runtimeFlags []string) RuntimeOption { return func(rt *Runtime) error { diff --git a/libpod/runtime.go b/libpod/runtime.go index c751df79b..1a22cd09a 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -88,6 +88,11 @@ type Runtime struct { libimageEventsShutdown chan bool lockManager lock.Manager + // syslog describes whenever logrus should log to the syslog as well. + // Note that the syslog hook will be enabled early in cmd/podman/syslog_linux.go + // This bool is just needed so that we can set it for netavark interface. + syslog bool + // doRenumber indicates that the runtime should perform a lock renumber // during initialization. // Once the runtime has been initialized and returned, this variable is @@ -517,6 +522,7 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) { DefaultSubnet: runtime.config.Network.DefaultSubnet, IsMachine: runtime.config.Engine.MachineEnabled, LockFile: filepath.Join(runtime.config.Network.NetworkConfigDir, "netavark.lock"), + Syslog: runtime.syslog, }) if err != nil { return errors.Wrapf(err, "could not create network interface") diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go index cfb674b6d..90eb6abeb 100644 --- a/pkg/domain/infra/runtime_libpod.go +++ b/pkg/domain/infra/runtime_libpod.go @@ -236,6 +236,11 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo options = append(options, libpod.WithRegistriesConf(cfg.RegistriesConf)) } + // no need to handle the error, it will return false anyway + if syslog, _ := fs.GetBool("syslog"); syslog { + options = append(options, libpod.WithSyslog()) + } + // TODO flag to set CNI plugins dir? if !opts.withFDS { -- cgit v1.2.3-54-g00ecf From 8198e96f3192c9a96b0568524d5732e34025e09f Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Fri, 19 Nov 2021 18:23:26 +0100 Subject: Force iptables driver for netavark tests Firewalld cannot be used because it can connect to the dbus api but talks to firewalld in the host namespace. This will affact your host badly and also causes tests to fail. Signed-off-by: Paul Holzinger --- libpod/network/netavark/run_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libpod/network/netavark/run_test.go b/libpod/network/netavark/run_test.go index 67dc51c10..f79e6d812 100644 --- a/libpod/network/netavark/run_test.go +++ b/libpod/network/netavark/run_test.go @@ -89,6 +89,10 @@ var _ = Describe("run netavark", func() { if err != nil { Fail("Failed to create netns") } + + // Force iptables driver, firewalld is broken inside the extra + // namespace because it still connects to firewalld on the host. + _ = os.Setenv("NETAVARK_FW", "iptables") }) JustBeforeEach(func() { @@ -109,6 +113,8 @@ var _ = Describe("run netavark", func() { netns.UnmountNS(netNSContainer) netNSContainer.Close() + + _ = os.Unsetenv("NETAVARK_FW") }) It("test basic setup", func() { -- cgit v1.2.3-54-g00ecf