github.com/olljanat/moby@v1.13.1/cli/command/system/info.go (about) 1 package system 2 3 import ( 4 "fmt" 5 "sort" 6 "strings" 7 "time" 8 9 "golang.org/x/net/context" 10 11 "github.com/docker/docker/api/types" 12 "github.com/docker/docker/api/types/swarm" 13 "github.com/docker/docker/cli" 14 "github.com/docker/docker/cli/command" 15 "github.com/docker/docker/pkg/ioutils" 16 "github.com/docker/docker/utils" 17 "github.com/docker/docker/utils/templates" 18 "github.com/docker/go-units" 19 "github.com/spf13/cobra" 20 ) 21 22 type infoOptions struct { 23 format string 24 } 25 26 // NewInfoCommand creates a new cobra.Command for `docker info` 27 func NewInfoCommand(dockerCli *command.DockerCli) *cobra.Command { 28 var opts infoOptions 29 30 cmd := &cobra.Command{ 31 Use: "info [OPTIONS]", 32 Short: "Display system-wide information", 33 Args: cli.NoArgs, 34 RunE: func(cmd *cobra.Command, args []string) error { 35 return runInfo(dockerCli, &opts) 36 }, 37 } 38 39 flags := cmd.Flags() 40 41 flags.StringVarP(&opts.format, "format", "f", "", "Format the output using the given Go template") 42 43 return cmd 44 } 45 46 func runInfo(dockerCli *command.DockerCli, opts *infoOptions) error { 47 ctx := context.Background() 48 info, err := dockerCli.Client().Info(ctx) 49 if err != nil { 50 return err 51 } 52 if opts.format == "" { 53 return prettyPrintInfo(dockerCli, info) 54 } 55 return formatInfo(dockerCli, info, opts.format) 56 } 57 58 func prettyPrintInfo(dockerCli *command.DockerCli, info types.Info) error { 59 fmt.Fprintf(dockerCli.Out(), "Containers: %d\n", info.Containers) 60 fmt.Fprintf(dockerCli.Out(), " Running: %d\n", info.ContainersRunning) 61 fmt.Fprintf(dockerCli.Out(), " Paused: %d\n", info.ContainersPaused) 62 fmt.Fprintf(dockerCli.Out(), " Stopped: %d\n", info.ContainersStopped) 63 fmt.Fprintf(dockerCli.Out(), "Images: %d\n", info.Images) 64 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Server Version: %s\n", info.ServerVersion) 65 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Storage Driver: %s\n", info.Driver) 66 if info.DriverStatus != nil { 67 for _, pair := range info.DriverStatus { 68 fmt.Fprintf(dockerCli.Out(), " %s: %s\n", pair[0], pair[1]) 69 70 // print a warning if devicemapper is using a loopback file 71 if pair[0] == "Data loop file" { 72 fmt.Fprintln(dockerCli.Err(), " WARNING: Usage of loopback devices is strongly discouraged for production use. Use `--storage-opt dm.thinpooldev` to specify a custom block storage device.") 73 } 74 } 75 76 } 77 if info.SystemStatus != nil { 78 for _, pair := range info.SystemStatus { 79 fmt.Fprintf(dockerCli.Out(), "%s: %s\n", pair[0], pair[1]) 80 } 81 } 82 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Logging Driver: %s\n", info.LoggingDriver) 83 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Cgroup Driver: %s\n", info.CgroupDriver) 84 85 fmt.Fprintf(dockerCli.Out(), "Plugins: \n") 86 fmt.Fprintf(dockerCli.Out(), " Volume:") 87 fmt.Fprintf(dockerCli.Out(), " %s", strings.Join(info.Plugins.Volume, " ")) 88 fmt.Fprintf(dockerCli.Out(), "\n") 89 fmt.Fprintf(dockerCli.Out(), " Network:") 90 fmt.Fprintf(dockerCli.Out(), " %s", strings.Join(info.Plugins.Network, " ")) 91 fmt.Fprintf(dockerCli.Out(), "\n") 92 93 if len(info.Plugins.Authorization) != 0 { 94 fmt.Fprintf(dockerCli.Out(), " Authorization:") 95 fmt.Fprintf(dockerCli.Out(), " %s", strings.Join(info.Plugins.Authorization, " ")) 96 fmt.Fprintf(dockerCli.Out(), "\n") 97 } 98 99 fmt.Fprintf(dockerCli.Out(), "Swarm: %v\n", info.Swarm.LocalNodeState) 100 if info.Swarm.LocalNodeState != swarm.LocalNodeStateInactive && info.Swarm.LocalNodeState != swarm.LocalNodeStateLocked { 101 fmt.Fprintf(dockerCli.Out(), " NodeID: %s\n", info.Swarm.NodeID) 102 if info.Swarm.Error != "" { 103 fmt.Fprintf(dockerCli.Out(), " Error: %v\n", info.Swarm.Error) 104 } 105 fmt.Fprintf(dockerCli.Out(), " Is Manager: %v\n", info.Swarm.ControlAvailable) 106 if info.Swarm.ControlAvailable { 107 fmt.Fprintf(dockerCli.Out(), " ClusterID: %s\n", info.Swarm.Cluster.ID) 108 fmt.Fprintf(dockerCli.Out(), " Managers: %d\n", info.Swarm.Managers) 109 fmt.Fprintf(dockerCli.Out(), " Nodes: %d\n", info.Swarm.Nodes) 110 fmt.Fprintf(dockerCli.Out(), " Orchestration:\n") 111 taskHistoryRetentionLimit := int64(0) 112 if info.Swarm.Cluster.Spec.Orchestration.TaskHistoryRetentionLimit != nil { 113 taskHistoryRetentionLimit = *info.Swarm.Cluster.Spec.Orchestration.TaskHistoryRetentionLimit 114 } 115 fmt.Fprintf(dockerCli.Out(), " Task History Retention Limit: %d\n", taskHistoryRetentionLimit) 116 fmt.Fprintf(dockerCli.Out(), " Raft:\n") 117 fmt.Fprintf(dockerCli.Out(), " Snapshot Interval: %d\n", info.Swarm.Cluster.Spec.Raft.SnapshotInterval) 118 if info.Swarm.Cluster.Spec.Raft.KeepOldSnapshots != nil { 119 fmt.Fprintf(dockerCli.Out(), " Number of Old Snapshots to Retain: %d\n", *info.Swarm.Cluster.Spec.Raft.KeepOldSnapshots) 120 } 121 fmt.Fprintf(dockerCli.Out(), " Heartbeat Tick: %d\n", info.Swarm.Cluster.Spec.Raft.HeartbeatTick) 122 fmt.Fprintf(dockerCli.Out(), " Election Tick: %d\n", info.Swarm.Cluster.Spec.Raft.ElectionTick) 123 fmt.Fprintf(dockerCli.Out(), " Dispatcher:\n") 124 fmt.Fprintf(dockerCli.Out(), " Heartbeat Period: %s\n", units.HumanDuration(time.Duration(info.Swarm.Cluster.Spec.Dispatcher.HeartbeatPeriod))) 125 fmt.Fprintf(dockerCli.Out(), " CA Configuration:\n") 126 fmt.Fprintf(dockerCli.Out(), " Expiry Duration: %s\n", units.HumanDuration(info.Swarm.Cluster.Spec.CAConfig.NodeCertExpiry)) 127 if len(info.Swarm.Cluster.Spec.CAConfig.ExternalCAs) > 0 { 128 fmt.Fprintf(dockerCli.Out(), " External CAs:\n") 129 for _, entry := range info.Swarm.Cluster.Spec.CAConfig.ExternalCAs { 130 fmt.Fprintf(dockerCli.Out(), " %s: %s\n", entry.Protocol, entry.URL) 131 } 132 } 133 } 134 fmt.Fprintf(dockerCli.Out(), " Node Address: %s\n", info.Swarm.NodeAddr) 135 managers := []string{} 136 for _, entry := range info.Swarm.RemoteManagers { 137 managers = append(managers, entry.Addr) 138 } 139 if len(managers) > 0 { 140 sort.Strings(managers) 141 fmt.Fprintf(dockerCli.Out(), " Manager Addresses:\n") 142 for _, entry := range managers { 143 fmt.Fprintf(dockerCli.Out(), " %s\n", entry) 144 } 145 } 146 } 147 148 if len(info.Runtimes) > 0 { 149 fmt.Fprintf(dockerCli.Out(), "Runtimes:") 150 for name := range info.Runtimes { 151 fmt.Fprintf(dockerCli.Out(), " %s", name) 152 } 153 fmt.Fprint(dockerCli.Out(), "\n") 154 fmt.Fprintf(dockerCli.Out(), "Default Runtime: %s\n", info.DefaultRuntime) 155 } 156 157 if info.OSType == "linux" { 158 fmt.Fprintf(dockerCli.Out(), "Init Binary: %v\n", info.InitBinary) 159 160 for _, ci := range []struct { 161 Name string 162 Commit types.Commit 163 }{ 164 {"containerd", info.ContainerdCommit}, 165 {"runc", info.RuncCommit}, 166 {"init", info.InitCommit}, 167 } { 168 fmt.Fprintf(dockerCli.Out(), "%s version: %s", ci.Name, ci.Commit.ID) 169 if ci.Commit.ID != ci.Commit.Expected { 170 fmt.Fprintf(dockerCli.Out(), " (expected: %s)", ci.Commit.Expected) 171 } 172 fmt.Fprintf(dockerCli.Out(), "\n") 173 } 174 if len(info.SecurityOptions) != 0 { 175 kvs, err := types.DecodeSecurityOptions(info.SecurityOptions) 176 if err != nil { 177 return err 178 } 179 fmt.Fprintf(dockerCli.Out(), "Security Options:\n") 180 for _, so := range kvs { 181 fmt.Fprintf(dockerCli.Out(), " %s\n", so.Name) 182 for _, o := range so.Options { 183 switch o.Key { 184 case "profile": 185 if o.Value != "default" { 186 fmt.Fprintf(dockerCli.Err(), " WARNING: You're not using the default seccomp profile\n") 187 } 188 fmt.Fprintf(dockerCli.Out(), " Profile: %s\n", o.Value) 189 } 190 } 191 } 192 } 193 } 194 195 // Isolation only has meaning on a Windows daemon. 196 if info.OSType == "windows" { 197 fmt.Fprintf(dockerCli.Out(), "Default Isolation: %v\n", info.Isolation) 198 } 199 200 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Kernel Version: %s\n", info.KernelVersion) 201 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Operating System: %s\n", info.OperatingSystem) 202 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "OSType: %s\n", info.OSType) 203 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Architecture: %s\n", info.Architecture) 204 fmt.Fprintf(dockerCli.Out(), "CPUs: %d\n", info.NCPU) 205 fmt.Fprintf(dockerCli.Out(), "Total Memory: %s\n", units.BytesSize(float64(info.MemTotal))) 206 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Name: %s\n", info.Name) 207 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "ID: %s\n", info.ID) 208 fmt.Fprintf(dockerCli.Out(), "Docker Root Dir: %s\n", info.DockerRootDir) 209 fmt.Fprintf(dockerCli.Out(), "Debug Mode (client): %v\n", utils.IsDebugEnabled()) 210 fmt.Fprintf(dockerCli.Out(), "Debug Mode (server): %v\n", info.Debug) 211 212 if info.Debug { 213 fmt.Fprintf(dockerCli.Out(), " File Descriptors: %d\n", info.NFd) 214 fmt.Fprintf(dockerCli.Out(), " Goroutines: %d\n", info.NGoroutines) 215 fmt.Fprintf(dockerCli.Out(), " System Time: %s\n", info.SystemTime) 216 fmt.Fprintf(dockerCli.Out(), " EventsListeners: %d\n", info.NEventsListener) 217 } 218 219 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Http Proxy: %s\n", info.HTTPProxy) 220 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "Https Proxy: %s\n", info.HTTPSProxy) 221 ioutils.FprintfIfNotEmpty(dockerCli.Out(), "No Proxy: %s\n", info.NoProxy) 222 223 if info.IndexServerAddress != "" { 224 u := dockerCli.ConfigFile().AuthConfigs[info.IndexServerAddress].Username 225 if len(u) > 0 { 226 fmt.Fprintf(dockerCli.Out(), "Username: %v\n", u) 227 } 228 fmt.Fprintf(dockerCli.Out(), "Registry: %v\n", info.IndexServerAddress) 229 } 230 231 // Only output these warnings if the server does not support these features 232 if info.OSType != "windows" { 233 if !info.MemoryLimit { 234 fmt.Fprintln(dockerCli.Err(), "WARNING: No memory limit support") 235 } 236 if !info.SwapLimit { 237 fmt.Fprintln(dockerCli.Err(), "WARNING: No swap limit support") 238 } 239 if !info.KernelMemory { 240 fmt.Fprintln(dockerCli.Err(), "WARNING: No kernel memory limit support") 241 } 242 if !info.OomKillDisable { 243 fmt.Fprintln(dockerCli.Err(), "WARNING: No oom kill disable support") 244 } 245 if !info.CPUCfsQuota { 246 fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu cfs quota support") 247 } 248 if !info.CPUCfsPeriod { 249 fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu cfs period support") 250 } 251 if !info.CPUShares { 252 fmt.Fprintln(dockerCli.Err(), "WARNING: No cpu shares support") 253 } 254 if !info.CPUSet { 255 fmt.Fprintln(dockerCli.Err(), "WARNING: No cpuset support") 256 } 257 if !info.IPv4Forwarding { 258 fmt.Fprintln(dockerCli.Err(), "WARNING: IPv4 forwarding is disabled") 259 } 260 if !info.BridgeNfIptables { 261 fmt.Fprintln(dockerCli.Err(), "WARNING: bridge-nf-call-iptables is disabled") 262 } 263 if !info.BridgeNfIP6tables { 264 fmt.Fprintln(dockerCli.Err(), "WARNING: bridge-nf-call-ip6tables is disabled") 265 } 266 } 267 268 if info.Labels != nil { 269 fmt.Fprintln(dockerCli.Out(), "Labels:") 270 for _, attribute := range info.Labels { 271 fmt.Fprintf(dockerCli.Out(), " %s\n", attribute) 272 } 273 // TODO: Engine labels with duplicate keys has been deprecated in 1.13 and will be error out 274 // after 3 release cycles (1.16). For now, a WARNING will be generated. The following will 275 // be removed eventually. 276 labelMap := map[string]string{} 277 for _, label := range info.Labels { 278 stringSlice := strings.SplitN(label, "=", 2) 279 if len(stringSlice) > 1 { 280 // If there is a conflict we will throw out an warning 281 if v, ok := labelMap[stringSlice[0]]; ok && v != stringSlice[1] { 282 fmt.Fprintln(dockerCli.Err(), "WARNING: labels with duplicate keys and conflicting values have been deprecated") 283 break 284 } 285 labelMap[stringSlice[0]] = stringSlice[1] 286 } 287 } 288 } 289 290 fmt.Fprintf(dockerCli.Out(), "Experimental: %v\n", info.ExperimentalBuild) 291 if info.ClusterStore != "" { 292 fmt.Fprintf(dockerCli.Out(), "Cluster Store: %s\n", info.ClusterStore) 293 } 294 295 if info.ClusterAdvertise != "" { 296 fmt.Fprintf(dockerCli.Out(), "Cluster Advertise: %s\n", info.ClusterAdvertise) 297 } 298 299 if info.RegistryConfig != nil && (len(info.RegistryConfig.InsecureRegistryCIDRs) > 0 || len(info.RegistryConfig.IndexConfigs) > 0) { 300 fmt.Fprintln(dockerCli.Out(), "Insecure Registries:") 301 for _, registry := range info.RegistryConfig.IndexConfigs { 302 if registry.Secure == false { 303 fmt.Fprintf(dockerCli.Out(), " %s\n", registry.Name) 304 } 305 } 306 307 for _, registry := range info.RegistryConfig.InsecureRegistryCIDRs { 308 mask, _ := registry.Mask.Size() 309 fmt.Fprintf(dockerCli.Out(), " %s/%d\n", registry.IP.String(), mask) 310 } 311 } 312 313 if info.RegistryConfig != nil && len(info.RegistryConfig.Mirrors) > 0 { 314 fmt.Fprintln(dockerCli.Out(), "Registry Mirrors:") 315 for _, mirror := range info.RegistryConfig.Mirrors { 316 fmt.Fprintf(dockerCli.Out(), " %s\n", mirror) 317 } 318 } 319 320 fmt.Fprintf(dockerCli.Out(), "Live Restore Enabled: %v\n", info.LiveRestoreEnabled) 321 322 return nil 323 } 324 325 func formatInfo(dockerCli *command.DockerCli, info types.Info, format string) error { 326 tmpl, err := templates.Parse(format) 327 if err != nil { 328 return cli.StatusError{StatusCode: 64, 329 Status: "Template parsing error: " + err.Error()} 330 } 331 err = tmpl.Execute(dockerCli.Out(), info) 332 dockerCli.Out().Write([]byte{'\n'}) 333 return err 334 }