diff --git a/api/client/commands.go b/api/client/commands.go index ae1002b563..bc1a4f1a8a 100644 --- a/api/client/commands.go +++ b/api/client/commands.go @@ -493,6 +493,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error { } fmt.Fprintf(cli.out, "Execution Driver: %s\n", remoteInfo.Get("ExecutionDriver")) fmt.Fprintf(cli.out, "Kernel Version: %s\n", remoteInfo.Get("KernelVersion")) + fmt.Fprintf(cli.out, "Operating System: %s\n", remoteInfo.Get("OperatingSystem")) if remoteInfo.GetBool("Debug") || os.Getenv("DEBUG") != "" { fmt.Fprintf(cli.out, "Debug mode (server): %v\n", remoteInfo.GetBool("Debug")) diff --git a/docs/man/docker-info.1.md b/docs/man/docker-info.1.md index 2945d61dfe..bf64a7b543 100644 --- a/docs/man/docker-info.1.md +++ b/docs/man/docker-info.1.md @@ -29,18 +29,14 @@ There are no available options. Here is a sample output: # docker info - Containers: 18 - Images: 95 - Storage Driver: devicemapper - Pool Name: docker-8:1-170408448-pool - Data file: /var/lib/docker/devicemapper/devicemapper/data - Metadata file: /var/lib/docker/devicemapper/devicemapper/metadata - Data Space Used: 9946.3 Mb - Data Space Total: 102400.0 Mb - Metadata Space Used: 9.9 Mb - Metadata Space Total: 2048.0 Mb - Execution Driver: native-0.1 - Kernel Version: 3.10.0-116.el7.x86_64 + Containers: 14 + Images: 52 + Storage Driver: aufs + Root Dir: /var/lib/docker/aufs + Dirs: 80 + Execution Driver: native-0.2 + Kernel Version: 3.13.0-24-generic + Operating System: Ubuntu 14.04 LTS # HISTORY April 2014, Originally compiled by William Henry (whenry at redhat dot com) diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index 9a5ace4716..83590a60e1 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -605,18 +605,19 @@ tar, then the ownerships might not get preserved. For example: $ sudo docker -D info - Containers: 16 - Images: 2138 + Containers: 14 + Images: 52 Storage Driver: btrfs - Execution Driver: native-0.1 - Kernel Version: 3.12.0-1-amd64 + Execution Driver: native-0.2 + Kernel Version: 3.13.0-24-generic + Operating System: Ubuntu 14.04 LTS Debug mode (server): false Debug mode (client): true - Fds: 16 - Goroutines: 104 + Fds: 10 + Goroutines: 9 EventsListeners: 0 Init Path: /usr/bin/docker - Sockets: [unix:///var/run/docker.sock tcp://0.0.0.0:4243] + Sockets: [unix:///var/run/docker.sock] Username: svendowideit Registry: [https://index.docker.io/v1/] diff --git a/pkg/parsers/operatingsystem/operatingsystem.go b/pkg/parsers/operatingsystem/operatingsystem.go new file mode 100644 index 0000000000..af185f9f6b --- /dev/null +++ b/pkg/parsers/operatingsystem/operatingsystem.go @@ -0,0 +1,40 @@ +package operatingsystem + +import ( + "bytes" + "errors" + "io/ioutil" +) + +var ( + // file to use to detect if the daemon is running in a container + proc1Cgroup = "/proc/1/cgroup" + + // file to check to determine Operating System + etcOsRelease = "/etc/os-release" +) + +func GetOperatingSystem() (string, error) { + b, err := ioutil.ReadFile(etcOsRelease) + if err != nil { + return "", err + } + if i := bytes.Index(b, []byte("PRETTY_NAME")); i >= 0 { + b = b[i+13:] + return string(b[:bytes.IndexByte(b, '"')]), nil + } + return "", errors.New("PRETTY_NAME not found") +} + +func IsContainerized() (bool, error) { + b, err := ioutil.ReadFile(proc1Cgroup) + if err != nil { + return false, err + } + for _, line := range bytes.Split(b, []byte{'\n'}) { + if len(line) > 0 && !bytes.HasSuffix(line, []byte{'/'}) { + return true, nil + } + } + return false, nil +} diff --git a/pkg/parsers/operatingsystem/operatingsystem_test.go b/pkg/parsers/operatingsystem/operatingsystem_test.go new file mode 100644 index 0000000000..d264b35f03 --- /dev/null +++ b/pkg/parsers/operatingsystem/operatingsystem_test.go @@ -0,0 +1,123 @@ +package operatingsystem + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" +) + +func TestGetOperatingSystem(t *testing.T) { + var ( + backup = etcOsRelease + ubuntuTrusty = []byte(`NAME="Ubuntu" +VERSION="14.04, Trusty Tahr" +ID=ubuntu +ID_LIKE=debian +PRETTY_NAME="Ubuntu 14.04 LTS" +VERSION_ID="14.04" +HOME_URL="http://www.ubuntu.com/" +SUPPORT_URL="http://help.ubuntu.com/" +BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`) + gentoo = []byte(`NAME=Gentoo +ID=gentoo +PRETTY_NAME="Gentoo/Linux" +ANSI_COLOR="1;32" +HOME_URL="http://www.gentoo.org/" +SUPPORT_URL="http://www.gentoo.org/main/en/support.xml" +BUG_REPORT_URL="https://bugs.gentoo.org/" +`) + noPrettyName = []byte(`NAME="Ubuntu" +VERSION="14.04, Trusty Tahr" +ID=ubuntu +ID_LIKE=debian +VERSION_ID="14.04" +HOME_URL="http://www.ubuntu.com/" +SUPPORT_URL="http://help.ubuntu.com/" +BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`) + ) + + dir := os.TempDir() + defer func() { + etcOsRelease = backup + os.RemoveAll(dir) + }() + + etcOsRelease = filepath.Join(dir, "etcOsRelease") + for expect, osRelease := range map[string][]byte{ + "Ubuntu 14.04 LTS": ubuntuTrusty, + "Gentoo/Linux": gentoo, + "": noPrettyName, + } { + if err := ioutil.WriteFile(etcOsRelease, osRelease, 0600); err != nil { + t.Fatalf("failed to write to %s: %v", etcOsRelease, err) + } + s, err := GetOperatingSystem() + if s != expect { + if expect == "" { + t.Fatalf("Expected error 'PRETTY_NAME not found', but got %v", err) + } else { + t.Fatalf("Expected '%s', but got '%s'. Err=%v", expect, s, err) + } + } + } +} + +func TestIsContainerized(t *testing.T) { + var ( + backup = proc1Cgroup + nonContainerizedProc1Cgroup = []byte(`14:name=systemd:/ +13:hugetlb:/ +12:net_prio:/ +11:perf_event:/ +10:bfqio:/ +9:blkio:/ +8:net_cls:/ +7:freezer:/ +6:devices:/ +5:memory:/ +4:cpuacct:/ +3:cpu:/ +2:cpuset:/ +`) + containerizedProc1Cgroup = []byte(`9:perf_event:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d +8:blkio:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d +7:net_cls:/ +6:freezer:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d +5:devices:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d +4:memory:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d +3:cpuacct:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d +2:cpu:/docker/3cef1b53c50b0fa357d994f8a1a8cd783c76bbf4f5dd08b226e38a8bd331338d +1:cpuset:/`) + ) + + dir := os.TempDir() + defer func() { + proc1Cgroup = backup + os.RemoveAll(dir) + }() + + proc1Cgroup = filepath.Join(dir, "proc1Cgroup") + + if err := ioutil.WriteFile(proc1Cgroup, nonContainerizedProc1Cgroup, 0600); err != nil { + t.Fatalf("failed to write to %s: %v", proc1Cgroup, err) + } + inContainer, err := IsContainerized() + if err != nil { + t.Fatal(err) + } + if inContainer { + t.Fatal("Wrongly assuming containerized") + } + + if err := ioutil.WriteFile(proc1Cgroup, containerizedProc1Cgroup, 0600); err != nil { + t.Fatalf("failed to write to %s: %v", proc1Cgroup, err) + } + inContainer, err = IsContainerized() + if err != nil { + t.Fatal(err) + } + if !inContainer { + t.Fatal("Wrongly assuming non-containerized") + } +} diff --git a/server/server.go b/server/server.go index 9db3d3d1ed..6eddd16fa6 100644 --- a/server/server.go +++ b/server/server.go @@ -57,6 +57,7 @@ import ( "github.com/docker/docker/pkg/parsers" "github.com/docker/docker/pkg/parsers/filters" "github.com/docker/docker/pkg/parsers/kernel" + "github.com/docker/docker/pkg/parsers/operatingsystem" "github.com/docker/docker/pkg/signal" "github.com/docker/docker/pkg/tailfile" "github.com/docker/docker/registry" @@ -795,6 +796,17 @@ func (srv *Server) DockerInfo(job *engine.Job) engine.Status { kernelVersion = kv.String() } + operatingSystem := "" + if s, err := operatingsystem.GetOperatingSystem(); err == nil { + operatingSystem = s + } + if inContainer, err := operatingsystem.IsContainerized(); err != nil { + utils.Errorf("Could not determine if daemon is containerized: %v", err) + operatingSystem += " (error determining if containerized)" + } else if inContainer { + operatingSystem += " (containerized)" + } + // if we still have the original dockerinit binary from before we copied it locally, let's return the path to that, since that's more intuitive (the copied path is trivial to derive by hand given VERSION) initPath := utils.DockerInitPath("") if initPath == "" { @@ -816,6 +828,7 @@ func (srv *Server) DockerInfo(job *engine.Job) engine.Status { v.Set("ExecutionDriver", srv.daemon.ExecutionDriver().Name()) v.SetInt("NEventsListener", srv.eventPublisher.SubscribersCount()) v.Set("KernelVersion", kernelVersion) + v.Set("OperatingSystem", operatingSystem) v.Set("IndexServerAddress", registry.IndexServerAddress()) v.Set("InitSha1", dockerversion.INITSHA1) v.Set("InitPath", initPath)