[lxc-devel] [lxd/master] console: adapt to new liblxc changes

brauner on Github lxc-bot at linuxcontainers.org
Sun Dec 3 21:14:29 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 1471 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20171203/94fe2c4f/attachment.bin>
-------------- next part --------------
From 40393da65205a4ba359307cbdbc33651cae89c23 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sun, 3 Dec 2017 21:53:54 +0100
Subject: [PATCH 1/2] console: adapt to new liblxc changes

Not too long ago we merged

https://github.com/lxc/lxc/pull/1925

which adds the new configuration keys

- lxc.console.buffer.size
- lxc.console.buffer.logfile
- lxc.console.rotate

LXD will always set

lxc.console.buffer = auto

which allocates a 128kB ringbuffer for each container and

lxc.console.buffer.logfile=/var/log/lxd/<container-name>/console.log

which is an on-disk representation of the console ringbuffer. liblxc will
always dump the in-memory ringbuffer when a container stops so users can
request the last contents of the ringbuffer if a container fails to start. When
a container is running and a request is sent to the retrieve the current
in-memory ringbuffer contents liblxc will also automatically dump the current
contents to disk.

As of this commit LXD will ignore lxc.console.logfile i.e. it cannot be
retrieved via the API. We still allow users to set this file via raw.lxc but
the API does not allow interaction with it. Users can either request the
in-memory ringbuffer contents or its on-disk representation so there' really no
point in having this exposed in the API.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 lxd/container.go         |   1 +
 lxd/container_console.go | 109 ++++++++++-------------------------------------
 lxd/container_lxc.go     |  24 +++++++++++
 3 files changed, 47 insertions(+), 87 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index 4515d9c3a..ccf6a4328 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -495,6 +495,7 @@ type container interface {
 	TemplatesPath() string
 	StatePath() string
 	LogFilePath() string
+	ConsoleBufferLogPath() string
 	LogPath() string
 
 	StoragePool() (string, error)
diff --git a/lxd/container_console.go b/lxd/container_console.go
index b8604f6b8..febdc7b16 100644
--- a/lxd/container_console.go
+++ b/lxd/container_console.go
@@ -8,7 +8,6 @@ import (
 	"os"
 	"os/exec"
 	"strconv"
-	"strings"
 	"sync"
 	"syscall"
 
@@ -326,53 +325,25 @@ func containerConsoleLogGet(d *Daemon, r *http.Request) Response {
 		return SmartError(err)
 	}
 
-	expandedConfig := c.ExpandedConfig()
-	rawLxc := expandedConfig["raw.lxc"]
-
-	consoleLogpath := ""
-	for _, line := range strings.Split(rawLxc, "\n") {
-		key, val, err := lxcParseRawLXC(line)
-		if err != nil {
-			return SmartError(err)
-		}
-
-		if key != "lxc.console.logfile" {
-			continue
-		}
-
-		consoleLogpath = val
-		break
-	}
-
-	logContents := ""
-	console := lxc.ConsoleLogOptions{}
-
 	ent := fileResponseEntry{}
 	if !c.IsRunning() {
-		if consoleLogpath == "" {
-			return SmartError(fmt.Errorf("The container does not keep a console log"))
-		}
-
-		// Hand back the contents of the on-disk logfile.
-		ent.path = consoleLogpath
-		ent.filename = consoleLogpath
+		// Hand back the contents of the console ringbuffer logfile.
+		consoleBufferLogPath := c.ConsoleBufferLogPath()
+		ent.path = consoleBufferLogPath
+		ent.filename = consoleBufferLogPath
 		return FileResponse(r, []fileResponseEntry{ent}, nil, false)
 	}
 
-	// Container keeps an on-disk logfile so keep sync it here.
-	if consoleLogpath != "" {
-		console.WriteToLogFile = true
-	} else {
-		console.WriteToLogFile = false
-	}
-
 	// Query the container's console ringbuffer.
-	console.ClearLog = false
-	console.ReadLog = true
-	console.ReadMax = 0
+	console := lxc.ConsoleLogOptions{
+		ClearLog:       false,
+		ReadLog:        true,
+		ReadMax:        0,
+		WriteToLogFile: true,
+	}
 
 	// Send a ringbuffer request to the container.
-	logContents, err = c.ConsoleLog(console)
+	logContents, err := c.ConsoleLog(console)
 	if err != nil {
 		errno, isErrno := shared.GetErrno(err)
 		if !isErrno {
@@ -383,18 +354,10 @@ func containerConsoleLogGet(d *Daemon, r *http.Request) Response {
 			return FileResponse(r, []fileResponseEntry{ent}, nil, false)
 		}
 
-		if errno == syscall.EFAULT && consoleLogpath != "" {
-			// Hand back the contents of the on-disk logfile.
-			ent.path = consoleLogpath
-			ent.filename = consoleLogpath
-			return FileResponse(r, []fileResponseEntry{ent}, nil, false)
-		}
-
 		return SmartError(err)
 	}
 
 	ent.buffer = []byte(logContents)
-
 	return FileResponse(r, []fileResponseEntry{ent}, nil, false)
 }
 
@@ -405,35 +368,10 @@ func containerConsoleLogDelete(d *Daemon, r *http.Request) Response {
 		return SmartError(err)
 	}
 
-	expandedConfig := c.ExpandedConfig()
-	rawLxc := expandedConfig["raw.lxc"]
-
-	consoleLogpath := ""
-	for _, line := range strings.Split(rawLxc, "\n") {
-		key, val, err := lxcParseRawLXC(line)
-		if err != nil {
-			return SmartError(err)
-		}
-
-		if key != "lxc.console.logfile" {
-			continue
-		}
-
-		consoleLogpath = val
-		break
-	}
-
-	console := lxc.ConsoleLogOptions{
-		ClearLog:       true,
-		ReadLog:        false,
-		ReadMax:        0,
-		WriteToLogFile: false,
-	}
-
 	truncateConsoleLogFile := func(path string) error {
 		// Check that this is a regular file. We don't want to try and unlink
 		// /dev/stderr or /dev/null or something.
-		st, err := os.Stat(consoleLogpath)
+		st, err := os.Stat(path)
 		if err != nil {
 			return err
 		}
@@ -442,21 +380,26 @@ func containerConsoleLogDelete(d *Daemon, r *http.Request) Response {
 			return fmt.Errorf("The console log is not a regular file")
 		}
 
-		if consoleLogpath == "" {
+		if path == "" {
 			return fmt.Errorf("Container does not keep a console logfile")
 		}
 
-		return os.Truncate(consoleLogpath, 0)
+		return os.Truncate(path, 0)
 	}
 
 	if !c.IsRunning() {
-		if consoleLogpath == "" {
-			return SmartError(fmt.Errorf("The container does not keep a console log"))
-		}
+		consoleLogpath := c.ConsoleBufferLogPath()
 		return SmartError(truncateConsoleLogFile(consoleLogpath))
 	}
 
 	// Send a ringbuffer request to the container.
+	console := lxc.ConsoleLogOptions{
+		ClearLog:       true,
+		ReadLog:        false,
+		ReadMax:        0,
+		WriteToLogFile: false,
+	}
+
 	_, err = c.ConsoleLog(console)
 	if err != nil {
 		errno, isErrno := shared.GetErrno(err)
@@ -468,14 +411,6 @@ func containerConsoleLogDelete(d *Daemon, r *http.Request) Response {
 			return SmartError(nil)
 		}
 
-		if errno == syscall.EFAULT {
-			if consoleLogpath == "" {
-				return SmartError(fmt.Errorf("The container does not keep a console log"))
-			}
-
-			return SmartError(truncateConsoleLogFile(consoleLogpath))
-		}
-
 		return SmartError(err)
 	}
 
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 5f58821e4..7901a74e9 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -209,6 +209,10 @@ func lxcValidConfig(rawLxc string) error {
 			return fmt.Errorf("Setting lxc.logfile is not allowed")
 		}
 
+		if key == "lxc.console.logfile" {
+			return fmt.Errorf("Setting lxc.console.logfile is not allowed")
+		}
+
 		if key == "lxc.syslog" || key == "lxc.log.syslog" {
 			return fmt.Errorf("Setting lxc.syslog is not allowed")
 		}
@@ -872,6 +876,22 @@ func (c *containerLXC) initLXC(config bool) error {
 		return err
 	}
 
+	if util.RuntimeLiblxcVersionAtLeast(3, 0, 0) {
+		// 128 kB console ringbuffer.
+		err = lxcSetConfigItem(cc, "lxc.console.buffer.size", "auto")
+		if err != nil {
+			return err
+		}
+
+		// File to dump ringbuffer contents to when requested or
+		// container shutdown.
+		consoleBufferLogFile := c.ConsoleBufferLogPath()
+		err = lxcSetConfigItem(cc, "lxc.console.buffer.logfile", consoleBufferLogFile)
+		if err != nil {
+			return err
+		}
+	}
+
 	// Allow for lightweight init
 	c.cConfig = config
 	if !config {
@@ -7256,6 +7276,10 @@ func (c *containerLXC) LogFilePath() string {
 	return filepath.Join(c.LogPath(), "lxc.log")
 }
 
+func (c *containerLXC) ConsoleBufferLogPath() string {
+	return filepath.Join(c.LogPath(), "console.log")
+}
+
 func (c *containerLXC) RootfsPath() string {
 	return filepath.Join(c.Path(), "rootfs")
 }

From b98e54e4ee219990eb0e583782102aa6968cdca7 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Sun, 3 Dec 2017 22:10:24 +0100
Subject: [PATCH 2/2] tests: adapt to changes in console API behavior

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 test/suites/console.sh | 65 +++-----------------------------------------------
 1 file changed, 3 insertions(+), 62 deletions(-)

diff --git a/test/suites/console.sh b/test/suites/console.sh
index 8c1733940..e72451791 100644
--- a/test/suites/console.sh
+++ b/test/suites/console.sh
@@ -13,43 +13,9 @@ test_console() {
 
   lxc init testimage cons1
 
-  # 1. Set a console log file but no ringbuffer.
-
-  # Test the console log file requests.
-  lxc start cons1
-
-  # No console log file set so this should return an error.
-  ! lxc console cons1 --show-log
-  lxc stop --force cons1
-
-  # Set a console log file but no ringbuffer.
-  # shellcheck disable=SC2034
-  CONSOLE_LOGFILE="$(mktemp -p "${LXD_DIR}" XXXXXXXXX)"
-  lxc config set cons1 raw.lxc "lxc.console.logfile=${CONSOLE_LOGFILE}"
   lxc start cons1
 
-  # Let the container come up. Two seconds should be fine.
-  sleep 2
-
-  # Make sure there's something in the console ringbuffer.
-  echo 'some content' | lxc exec cons1 -- tee /dev/console
-
-  # Console log file set so this should return without an error.
-  lxc console cons1 --show-log
-
-  lxc stop --force cons1
-
-  # 2. Set a console ringbuffer but no log file.
-
-  # remove logfile
-  lxc config unset cons1 raw.lxc
-
-  # set console ringbuffer
-  lxc config set cons1 raw.lxc "lxc.console.logsize=auto"
-
-  lxc start cons1
-
-  # Let the container come up. Two seconds should be fine.
+  # Let the init system come up.
   sleep 2
 
   # Make sure there's something in the console ringbuffer.
@@ -59,35 +25,10 @@ test_console() {
   # Retrieve the ringbuffer contents.
   lxc console cons1 --show-log
 
-  # 3. Set a console ringbuffer and a log file.
-
   lxc stop --force cons1
 
-  lxc config unset cons1 raw.lxc
-
-  rm -f "${CONSOLE_LOGFILE}"
-  printf "lxc.console.logsize=auto\nlxc.console.logfile=%s" "${CONSOLE_LOGFILE}" | lxc config set cons1 raw.lxc -
-
-  lxc start cons1
-
-  # Let the container come up. Two seconds should be fine.
-  sleep 2
-
-  # Make sure there's something in the console ringbuffer.
-  echo 'Ringbuffer contents and log file contents must match' | lxc exec cons1 -- tee /dev/console
-
-  # Retrieve the ringbuffer contents.
-  RINGBUFFER_CONTENT=$(lxc console cons1 --show-log)
-  # Strip prefix added by the client tool.
-  RINGBUFFER_CONTENT="${RINGBUFFER_CONTENT#
-Console log:
-
-}"
-  # Give kernel time to sync data to disk
-  sleep 2
-  LOGFILE_CONTENT=$(cat "${CONSOLE_LOGFILE}")
-
-  [ "${RINGBUFFER_CONTENT}" = "${LOGFILE_CONTENT}" ]
+  # Retrieve on-disk representation of the console ringbuffer.
+  lxc console cons1 --show-log
 
   lxc delete --force cons1
 }


More information about the lxc-devel mailing list