Skip to content

Commit

Permalink
Adding OS version info to the nodes' Info struct
Browse files Browse the repository at this point in the history
This is needed so that we can add OS version constraints in Swarmkit, which
does require the engine to report its host's OS version (see
moby/swarmkit#2770).

The OS version is parsed from the `os-release` file on Linux, and from the
`ReleaseId` string value of the `SOFTWARE\Microsoft\Windows NT\CurrentVersion`
registry key on Windows.

Added unit tests when possible, as well as Prometheus metrics.

Signed-off-by: Jean Rouge <[email protected]>
  • Loading branch information
wk8 authored and root committed Jun 6, 2019
1 parent 8d76028 commit d363a18
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 96 deletions.
11 changes: 11 additions & 0 deletions api/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3840,6 +3840,17 @@ definitions:
or "Windows Server 2016 Datacenter"
type: "string"
example: "Alpine Linux v3.5"
OSVersion:
description: |
Version of the host's operating system
<p><br /></p>
> **Note**: The information returned in this field, including its
> very existence, and the formatting of values, should not be considered
> stable, and may change without notice.
type: "string"
example: "16.04"
OSType:
description: |
Generic type of the operating system of the host, as returned by the
Expand Down
1 change: 1 addition & 0 deletions api/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ type Info struct {
NEventsListener int
KernelVersion string
OperatingSystem string
OSVersion string
OSType string
Architecture string
IndexServerAddress string
Expand Down
1 change: 1 addition & 0 deletions daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -1061,6 +1061,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
info.KernelVersion,
info.OperatingSystem,
info.OSType,
info.OSVersion,
info.ID,
).Set(1)
engineCpus.Set(float64(info.NCPU))
Expand Down
23 changes: 21 additions & 2 deletions daemon/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,14 @@ import (
"github.com/docker/docker/pkg/system"
"github.com/docker/docker/registry"
"github.com/docker/go-connections/sockets"
"github.com/docker/go-metrics"
"github.com/sirupsen/logrus"
)

// SystemInfo returns information about the host server the daemon is running on.
func (daemon *Daemon) SystemInfo() (*types.Info, error) {
defer metrics.StartTimer(hostInfoFunctions.WithValues("system_info"))()

sysInfo := sysinfo.New(true)
cRunning, cPaused, cStopped := stateCtr.get()

Expand All @@ -49,6 +52,7 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
NEventsListener: daemon.EventsService.SubscribersCount(),
KernelVersion: kernelVersion(),
OperatingSystem: operatingSystem(),
OSVersion: osVersion(),
IndexServerAddress: registry.IndexServer,
OSType: platform.OSType,
Architecture: platform.Architecture,
Expand Down Expand Up @@ -82,6 +86,8 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {

// SystemVersion returns version information about the daemon.
func (daemon *Daemon) SystemVersion() types.Version {
defer metrics.StartTimer(hostInfoFunctions.WithValues("system_version"))()

kernelVersion := kernelVersion()

v := types.Version{
Expand Down Expand Up @@ -240,8 +246,9 @@ func memInfo() *system.MemInfo {
return memInfo
}

func operatingSystem() string {
var operatingSystem string
func operatingSystem() (operatingSystem string) {
defer metrics.StartTimer(hostInfoFunctions.WithValues("operating_system"))()

if s, err := operatingsystem.GetOperatingSystem(); err != nil {
logrus.Warnf("Could not get operating system name: %v", err)
} else {
Expand All @@ -256,9 +263,21 @@ func operatingSystem() string {
operatingSystem += " (containerized)"
}
}

return operatingSystem
}

func osVersion() (version string) {
defer metrics.StartTimer(hostInfoFunctions.WithValues("os_version"))()

version, err := operatingsystem.GetOperatingSystemVersion()
if err != nil {
logrus.Warnf("Could not get operating system version: %v", err)
}

return version
}

func maskCredentials(rawURL string) string {
parsedURL, err := url.Parse(rawURL)
if err != nil || parsedURL.User == nil {
Expand Down
6 changes: 5 additions & 1 deletion daemon/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const metricsPluginType = "MetricsCollector"
var (
containerActions metrics.LabeledTimer
networkActions metrics.LabeledTimer
hostInfoFunctions metrics.LabeledTimer
engineInfo metrics.LabeledGauge
engineCpus metrics.Gauge
engineMemory metrics.Gauge
Expand All @@ -38,15 +39,18 @@ func init() {
} {
containerActions.WithValues(a).Update(0)
}
hostInfoFunctions = ns.NewLabeledTimer("host_info_functions", "The number of seconds it takes to call functions gathering info about the host", "function")

networkActions = ns.NewLabeledTimer("network_actions", "The number of seconds it takes to process each network action", "action")
engineInfo = ns.NewLabeledGauge("engine", "The information related to the engine and the OS it is running on", metrics.Unit("info"),
"version",
"commit",
"architecture",
"graphdriver",
"kernel", "os",
"kernel",
"os",
"os_type",
"os_version",
"daemon_id", // ID is a randomly generated unique identifier (e.g. UUID4)
)
engineCpus = ns.NewGauge("engine_cpus", "The number of cpus that the host system of the engine has", metrics.Unit("cpus"))
Expand Down
41 changes: 28 additions & 13 deletions pkg/parsers/operatingsystem/operatingsystem_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,24 @@ var (

// GetOperatingSystem gets the name of the current operating system.
func GetOperatingSystem() (string, error) {
if prettyName, err := getValueFromOsRelease("PRETTY_NAME"); err != nil {
return "", err
} else if prettyName != "" {
return prettyName, nil
}

// If not set, defaults to PRETTY_NAME="Linux"
// c.f. http://www.freedesktop.org/software/systemd/man/os-release.html
return "Linux", nil
}

// GetOperatingSystemVersion gets the version of the current operating system, as a string.
func GetOperatingSystemVersion() (string, error) {
return getValueFromOsRelease("VERSION_ID")
}

// parses the os-release file and returns the value associated with `key`
func getValueFromOsRelease(key string) (string, error) {
osReleaseFile, err := os.Open(etcOsRelease)
if err != nil {
if !os.IsNotExist(err) {
Expand All @@ -38,28 +56,25 @@ func GetOperatingSystem() (string, error) {
}
defer osReleaseFile.Close()

var prettyName string
var value string
keyWithTrailingEqual := key + "="
scanner := bufio.NewScanner(osReleaseFile)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "PRETTY_NAME=") {
if strings.HasPrefix(line, keyWithTrailingEqual) {
data := strings.SplitN(line, "=", 2)
prettyNames, err := shellwords.Parse(data[1])
values, err := shellwords.Parse(data[1])
if err != nil {
return "", fmt.Errorf("PRETTY_NAME is invalid: %s", err.Error())
return "", fmt.Errorf("%s is invalid: %s", key, err.Error())
}
if len(prettyNames) != 1 {
return "", fmt.Errorf("PRETTY_NAME needs to be enclosed by quotes if they have spaces: %s", data[1])
if len(values) != 1 {
return "", fmt.Errorf("%s needs to be enclosed by quotes if they have spaces: %s", key, data[1])
}
prettyName = prettyNames[0]
value = values[0]
}
}
if prettyName != "" {
return prettyName, nil
}
// If not set, defaults to PRETTY_NAME="Linux"
// c.f. http://www.freedesktop.org/software/systemd/man/os-release.html
return "Linux", nil

return value, nil
}

// IsContainerized returns true if we are running inside a container.
Expand Down
Loading

0 comments on commit d363a18

Please sign in to comment.