Add "Warnings" to /info endpoint, and move detection to the daemon
| ... | ... |
@@ -3904,6 +3904,19 @@ definitions: |
| 3904 | 3904 |
such as number of nodes, and expiration are included. |
| 3905 | 3905 |
type: "string" |
| 3906 | 3906 |
example: "Community Engine" |
| 3907 |
+ Warnings: |
|
| 3908 |
+ description: | |
|
| 3909 |
+ List of warnings / informational messages about missing features, or |
|
| 3910 |
+ issues related to the daemon configuration. |
|
| 3911 |
+ |
|
| 3912 |
+ These messages can be printed by the client as information to the user. |
|
| 3913 |
+ type: "array" |
|
| 3914 |
+ items: |
|
| 3915 |
+ type: "string" |
|
| 3916 |
+ example: |
|
| 3917 |
+ - "WARNING: No memory limit support" |
|
| 3918 |
+ - "WARNING: bridge-nf-call-iptables is disabled" |
|
| 3919 |
+ - "WARNING: bridge-nf-call-ip6tables is disabled" |
|
| 3907 | 3920 |
|
| 3908 | 3921 |
|
| 3909 | 3922 |
# PluginsInfo is a temp struct holding Plugins name |
| ... | ... |
@@ -4,6 +4,7 @@ package daemon // import "github.com/docker/docker/daemon" |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 | 6 |
"context" |
| 7 |
+ "fmt" |
|
| 7 | 8 |
"os/exec" |
| 8 | 9 |
"strings" |
| 9 | 10 |
|
| ... | ... |
@@ -68,6 +69,80 @@ func (daemon *Daemon) fillPlatformInfo(v *types.Info, sysInfo *sysinfo.SysInfo) |
| 68 | 68 |
logrus.Warnf("failed to retrieve %s version: %s", defaultInitBinary, err)
|
| 69 | 69 |
v.InitCommit.ID = "N/A" |
| 70 | 70 |
} |
| 71 |
+ |
|
| 72 |
+ if !v.MemoryLimit {
|
|
| 73 |
+ v.Warnings = append(v.Warnings, "WARNING: No memory limit support") |
|
| 74 |
+ } |
|
| 75 |
+ if !v.SwapLimit {
|
|
| 76 |
+ v.Warnings = append(v.Warnings, "WARNING: No swap limit support") |
|
| 77 |
+ } |
|
| 78 |
+ if !v.KernelMemory {
|
|
| 79 |
+ v.Warnings = append(v.Warnings, "WARNING: No kernel memory limit support") |
|
| 80 |
+ } |
|
| 81 |
+ if !v.OomKillDisable {
|
|
| 82 |
+ v.Warnings = append(v.Warnings, "WARNING: No oom kill disable support") |
|
| 83 |
+ } |
|
| 84 |
+ if !v.CPUCfsQuota {
|
|
| 85 |
+ v.Warnings = append(v.Warnings, "WARNING: No cpu cfs quota support") |
|
| 86 |
+ } |
|
| 87 |
+ if !v.CPUCfsPeriod {
|
|
| 88 |
+ v.Warnings = append(v.Warnings, "WARNING: No cpu cfs period support") |
|
| 89 |
+ } |
|
| 90 |
+ if !v.CPUShares {
|
|
| 91 |
+ v.Warnings = append(v.Warnings, "WARNING: No cpu shares support") |
|
| 92 |
+ } |
|
| 93 |
+ if !v.CPUSet {
|
|
| 94 |
+ v.Warnings = append(v.Warnings, "WARNING: No cpuset support") |
|
| 95 |
+ } |
|
| 96 |
+ if !v.IPv4Forwarding {
|
|
| 97 |
+ v.Warnings = append(v.Warnings, "WARNING: IPv4 forwarding is disabled") |
|
| 98 |
+ } |
|
| 99 |
+ if !v.BridgeNfIptables {
|
|
| 100 |
+ v.Warnings = append(v.Warnings, "WARNING: bridge-nf-call-iptables is disabled") |
|
| 101 |
+ } |
|
| 102 |
+ if !v.BridgeNfIP6tables {
|
|
| 103 |
+ v.Warnings = append(v.Warnings, "WARNING: bridge-nf-call-ip6tables is disabled") |
|
| 104 |
+ } |
|
| 105 |
+} |
|
| 106 |
+ |
|
| 107 |
+func fillDriverWarnings(v *types.Info) {
|
|
| 108 |
+ if v.DriverStatus == nil {
|
|
| 109 |
+ return |
|
| 110 |
+ } |
|
| 111 |
+ for _, pair := range v.DriverStatus {
|
|
| 112 |
+ if pair[0] == "Data loop file" {
|
|
| 113 |
+ msg := fmt.Sprintf("WARNING: %s: usage of loopback devices is "+
|
|
| 114 |
+ "strongly discouraged for production use.\n "+ |
|
| 115 |
+ "Use `--storage-opt dm.thinpooldev` to specify a custom block storage device.", v.Driver) |
|
| 116 |
+ |
|
| 117 |
+ v.Warnings = append(v.Warnings, msg) |
|
| 118 |
+ continue |
|
| 119 |
+ } |
|
| 120 |
+ if pair[0] == "Supports d_type" && pair[1] == "false" {
|
|
| 121 |
+ backingFs := getBackingFs(v) |
|
| 122 |
+ |
|
| 123 |
+ msg := fmt.Sprintf("WARNING: %s: the backing %s filesystem is formatted without d_type support, which leads to incorrect behavior.\n", v.Driver, backingFs)
|
|
| 124 |
+ if backingFs == "xfs" {
|
|
| 125 |
+ msg += " Reformat the filesystem with ftype=1 to enable d_type support.\n" |
|
| 126 |
+ } |
|
| 127 |
+ msg += " Running without d_type support will not be supported in future releases." |
|
| 128 |
+ |
|
| 129 |
+ v.Warnings = append(v.Warnings, msg) |
|
| 130 |
+ continue |
|
| 131 |
+ } |
|
| 132 |
+ } |
|
| 133 |
+} |
|
| 134 |
+ |
|
| 135 |
+func getBackingFs(v *types.Info) string {
|
|
| 136 |
+ if v.DriverStatus == nil {
|
|
| 137 |
+ return "" |
|
| 138 |
+ } |
|
| 139 |
+ for _, pair := range v.DriverStatus {
|
|
| 140 |
+ if pair[0] == "Backing Filesystem" {
|
|
| 141 |
+ return pair[1] |
|
| 142 |
+ } |
|
| 143 |
+ } |
|
| 144 |
+ return "" |
|
| 71 | 145 |
} |
| 72 | 146 |
|
| 73 | 147 |
// parseInitVersion parses a Tini version string, and extracts the version. |
| ... | ... |
@@ -21,6 +21,8 @@ keywords: "API, Docker, rcli, REST, documentation" |
| 21 | 21 |
and `OperatingSystem` if the daemon was unable to obtain this information. |
| 22 | 22 |
* `GET /info` now returns information about the product license, if a license |
| 23 | 23 |
has been applied to the daemon. |
| 24 |
+* `GET /info` now returns a `Warnings` field, containing warnings and informational |
|
| 25 |
+ messages about missing features, or issues related to the daemon configuration. |
|
| 24 | 26 |
* `POST /swarm/init` now accepts a `DefaultAddrPool` property to set global scope default address pool |
| 25 | 27 |
* `POST /swarm/init` now accepts a `SubnetSize` property to set global scope networks by giving the |
| 26 | 28 |
length of the subnet masks for every such network |