diff --git a/api/swagger.yaml b/api/swagger.yaml index 672b598929..3906899050 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -3904,6 +3904,19 @@ definitions: such as number of nodes, and expiration are included. type: "string" example: "Community Engine" + Warnings: + description: | + List of warnings / informational messages about missing features, or + issues related to the daemon configuration. + + These messages can be printed by the client as information to the user. + type: "array" + items: + type: "string" + example: + - "WARNING: No memory limit support" + - "WARNING: bridge-nf-call-iptables is disabled" + - "WARNING: bridge-nf-call-ip6tables is disabled" # PluginsInfo is a temp struct holding Plugins name diff --git a/api/types/types.go b/api/types/types.go index ae9e27dca1..ed62fd41e5 100644 --- a/api/types/types.go +++ b/api/types/types.go @@ -206,6 +206,7 @@ type Info struct { InitCommit Commit SecurityOptions []string ProductLicense string `json:",omitempty"` + Warnings []string } // KeyValue holds a key/value pair diff --git a/daemon/info.go b/daemon/info.go index 3575fdda38..cc9ad8ac61 100644 --- a/daemon/info.go +++ b/daemon/info.go @@ -134,6 +134,8 @@ func (daemon *Daemon) fillDriverInfo(v *types.Info) { v.Driver = drivers v.DriverStatus = ds + + fillDriverWarnings(v) } func (daemon *Daemon) fillPluginsInfo(v *types.Info) { diff --git a/daemon/info_unix.go b/daemon/info_unix.go index 864e8816a9..98935cca70 100644 --- a/daemon/info_unix.go +++ b/daemon/info_unix.go @@ -4,6 +4,7 @@ package daemon // import "github.com/docker/docker/daemon" import ( "context" + "fmt" "os/exec" "strings" @@ -68,6 +69,80 @@ func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) logrus.Warnf("failed to retrieve %s version: %s", defaultInitBinary, err) v.InitCommit.ID = "N/A" } + + if !v.MemoryLimit { + v.Warnings = append(v.Warnings, "WARNING: No memory limit support") + } + if !v.SwapLimit { + v.Warnings = append(v.Warnings, "WARNING: No swap limit support") + } + if !v.KernelMemory { + v.Warnings = append(v.Warnings, "WARNING: No kernel memory limit support") + } + if !v.OomKillDisable { + v.Warnings = append(v.Warnings, "WARNING: No oom kill disable support") + } + if !v.CPUCfsQuota { + v.Warnings = append(v.Warnings, "WARNING: No cpu cfs quota support") + } + if !v.CPUCfsPeriod { + v.Warnings = append(v.Warnings, "WARNING: No cpu cfs period support") + } + if !v.CPUShares { + v.Warnings = append(v.Warnings, "WARNING: No cpu shares support") + } + if !v.CPUSet { + v.Warnings = append(v.Warnings, "WARNING: No cpuset support") + } + if !v.IPv4Forwarding { + v.Warnings = append(v.Warnings, "WARNING: IPv4 forwarding is disabled") + } + if !v.BridgeNfIptables { + v.Warnings = append(v.Warnings, "WARNING: bridge-nf-call-iptables is disabled") + } + if !v.BridgeNfIP6tables { + v.Warnings = append(v.Warnings, "WARNING: bridge-nf-call-ip6tables is disabled") + } +} + +func fillDriverWarnings(v *types.Info) { + if v.DriverStatus == nil { + return + } + for _, pair := range v.DriverStatus { + if pair[0] == "Data loop file" { + msg := fmt.Sprintf("WARNING: %s: usage of loopback devices is "+ + "strongly discouraged for production use.\n "+ + "Use `--storage-opt dm.thinpooldev` to specify a custom block storage device.", v.Driver) + + v.Warnings = append(v.Warnings, msg) + continue + } + if pair[0] == "Supports d_type" && pair[1] == "false" { + backingFs := getBackingFs(v) + + msg := fmt.Sprintf("WARNING: %s: the backing %s filesystem is formatted without d_type support, which leads to incorrect behavior.\n", v.Driver, backingFs) + if backingFs == "xfs" { + msg += " Reformat the filesystem with ftype=1 to enable d_type support.\n" + } + msg += " Running without d_type support will not be supported in future releases." + + v.Warnings = append(v.Warnings, msg) + continue + } + } +} + +func getBackingFs(v *types.Info) string { + if v.DriverStatus == nil { + return "" + } + for _, pair := range v.DriverStatus { + if pair[0] == "Backing Filesystem" { + return pair[1] + } + } + return "" } // parseInitVersion parses a Tini version string, and extracts the version. diff --git a/daemon/info_windows.go b/daemon/info_windows.go index bf97971479..2c1ff460c3 100644 --- a/daemon/info_windows.go +++ b/daemon/info_windows.go @@ -8,3 +8,6 @@ import ( // fillPlatformInfo fills the platform related info. func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) { } + +func fillDriverWarnings(v *types.Info) { +} diff --git a/docs/api/version-history.md b/docs/api/version-history.md index 7c18799fc5..6f0083e90e 100644 --- a/docs/api/version-history.md +++ b/docs/api/version-history.md @@ -21,6 +21,8 @@ keywords: "API, Docker, rcli, REST, documentation" and `OperatingSystem` if the daemon was unable to obtain this information. * `GET /info` now returns information about the product license, if a license has been applied to the daemon. +* `GET /info` now returns a `Warnings` field, containing warnings and informational + messages about missing features, or issues related to the daemon configuration. * `POST /swarm/init` now accepts a `DefaultAddrPool` property to set global scope default address pool * `POST /swarm/init` now accepts a `SubnetSize` property to set global scope networks by giving the length of the subnet masks for every such network