mirror of https://github.com/k3s-io/k3s
306 lines
9.2 KiB
Go
306 lines
9.2 KiB
Go
// Copyright 2014 Google Inc. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Handler for /validate content.
|
|
// Validates cadvisor dependencies - kernel, os, docker setup.
|
|
|
|
package validate
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"path"
|
|
"strings"
|
|
|
|
"github.com/google/cadvisor/container/docker"
|
|
"github.com/google/cadvisor/manager"
|
|
"github.com/google/cadvisor/utils"
|
|
|
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
|
)
|
|
|
|
const (
|
|
ValidatePage = "/validate/"
|
|
Supported = "[Supported, but not recommended]"
|
|
Unsupported = "[Unsupported]"
|
|
Recommended = "[Supported and recommended]"
|
|
Unknown = "[Unknown]"
|
|
VersionFormat = "%d.%d%s"
|
|
OutputFormat = "%s: %s\n\t%s\n\n"
|
|
)
|
|
|
|
func getMajorMinor(version string) (int, int, error) {
|
|
var major, minor int
|
|
var ign string
|
|
n, err := fmt.Sscanf(version, VersionFormat, &major, &minor, &ign)
|
|
if n != 3 || err != nil {
|
|
log.Printf("Failed to parse version for %s", version)
|
|
return -1, -1, err
|
|
}
|
|
return major, minor, nil
|
|
}
|
|
|
|
func validateKernelVersion(version string) (string, string) {
|
|
desc := fmt.Sprintf("Kernel version is %s. Versions >= 2.6 are supported. 3.0+ are recommended.\n", version)
|
|
major, minor, err := getMajorMinor(version)
|
|
if err != nil {
|
|
desc = fmt.Sprintf("Could not parse kernel version. %s", desc)
|
|
return Unknown, desc
|
|
}
|
|
|
|
if major < 2 {
|
|
return Unsupported, desc
|
|
}
|
|
|
|
if major == 2 && minor < 6 {
|
|
return Unsupported, desc
|
|
}
|
|
|
|
if major >= 3 {
|
|
return Recommended, desc
|
|
}
|
|
|
|
return Supported, desc
|
|
}
|
|
|
|
func validateDockerVersion(version string) (string, string) {
|
|
desc := fmt.Sprintf("Docker version is %s. Versions >= 1.0 are supported. 1.2+ are recommended.\n", version)
|
|
major, minor, err := getMajorMinor(version)
|
|
if err != nil {
|
|
desc = fmt.Sprintf("Could not parse docker version. %s\n\t", desc)
|
|
return Unknown, desc
|
|
}
|
|
if major < 1 {
|
|
return Unsupported, desc
|
|
}
|
|
|
|
if major == 1 && minor < 2 {
|
|
return Supported, desc
|
|
}
|
|
|
|
return Recommended, desc
|
|
}
|
|
|
|
func getEnabledCgroups() (map[string]int, error) {
|
|
out, err := ioutil.ReadFile("/proc/cgroups")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cgroups := make(map[string]int)
|
|
for i, line := range strings.Split(string(out), "\n") {
|
|
var cgroup string
|
|
var ign, enabled int
|
|
if i == 0 || line == "" {
|
|
continue
|
|
}
|
|
n, err := fmt.Sscanf(line, "%s %d %d %d", &cgroup, &ign, &ign, &enabled)
|
|
if n != 4 || err != nil {
|
|
if err == nil {
|
|
err = fmt.Errorf("failed to parse /proc/cgroup entry %s", line)
|
|
}
|
|
return nil, err
|
|
}
|
|
cgroups[cgroup] = enabled
|
|
}
|
|
return cgroups, nil
|
|
}
|
|
|
|
func areCgroupsPresent(available map[string]int, desired []string) (bool, string) {
|
|
for _, cgroup := range desired {
|
|
enabled, ok := available[cgroup]
|
|
if !ok {
|
|
reason := fmt.Sprintf("Missing cgroup %s. Available cgroups: %v\n", cgroup, available)
|
|
return false, reason
|
|
}
|
|
if enabled != 1 {
|
|
reason := fmt.Sprintf("Cgroup %s not enabled. Available cgroups: %v\n", cgroup, available)
|
|
return false, reason
|
|
}
|
|
}
|
|
return true, ""
|
|
}
|
|
|
|
func validateMemoryAccounting(available_cgroups map[string]int) string {
|
|
ok, _ := areCgroupsPresent(available_cgroups, []string{"memory"})
|
|
if !ok {
|
|
return "\tHierarchical memory accounting status unknown: memory cgroup not enabled.\n"
|
|
}
|
|
mnt, err := cgroups.FindCgroupMountpoint("memory")
|
|
if err != nil {
|
|
return "\tHierarchical memory accounting status unknown: memory cgroup not mounted.\n"
|
|
}
|
|
hier, err := ioutil.ReadFile(path.Join(mnt, "memory.use_hierarchy"))
|
|
if err != nil {
|
|
return "\tHierarchical memory accounting status unknown: hierarchy interface unavailable.\n"
|
|
}
|
|
var enabled int
|
|
n, err := fmt.Sscanf(string(hier), "%d", &enabled)
|
|
if err != nil || n != 1 {
|
|
return "\tHierarchical memory accounting status unknown: hierarchy interface unreadable.\n"
|
|
}
|
|
if enabled == 1 {
|
|
return "\tHierarchical memory accounting enabled. Reported memory usage includes memory used by child containers.\n"
|
|
}
|
|
return "\tHierarchical memory accounting disabled. Memory usage does not include usage from child containers.\n"
|
|
|
|
}
|
|
|
|
func validateCgroups() (string, string) {
|
|
required_cgroups := []string{"cpu", "cpuacct"}
|
|
recommended_cgroups := []string{"memory", "blkio", "cpuset", "devices", "freezer"}
|
|
available_cgroups, err := getEnabledCgroups()
|
|
desc := fmt.Sprintf("\tFollowing cgroups are required: %v\n\tFollowing other cgroups are recommended: %v\n", required_cgroups, recommended_cgroups)
|
|
if err != nil {
|
|
desc = fmt.Sprintf("Could not parse /proc/cgroups.\n%s", desc)
|
|
return Unknown, desc
|
|
}
|
|
ok, out := areCgroupsPresent(available_cgroups, required_cgroups)
|
|
if !ok {
|
|
out += desc
|
|
return Unsupported, out
|
|
}
|
|
ok, out = areCgroupsPresent(available_cgroups, recommended_cgroups)
|
|
if !ok {
|
|
// supported, but not recommended.
|
|
out += desc
|
|
return Supported, out
|
|
}
|
|
out = fmt.Sprintf("Available cgroups: %v\n", available_cgroups)
|
|
out += desc
|
|
out += validateMemoryAccounting(available_cgroups)
|
|
return Recommended, out
|
|
}
|
|
|
|
func validateDockerInfo() (string, string) {
|
|
info, err := docker.ValidateInfo()
|
|
if err != nil {
|
|
return Unsupported, fmt.Sprintf("Docker setup is invalid: %v", err)
|
|
}
|
|
|
|
desc := fmt.Sprintf("Docker exec driver is %s. Storage driver is %s.\n", info.ExecutionDriver, info.Driver)
|
|
return Recommended, desc
|
|
}
|
|
|
|
func validateCgroupMounts() (string, string) {
|
|
const recommendedMount = "/sys/fs/cgroup"
|
|
desc := fmt.Sprintf("\tAny cgroup mount point that is detectible and accessible is supported. %s is recommended as a standard location.\n", recommendedMount)
|
|
mnt, err := cgroups.FindCgroupMountpoint("cpu")
|
|
if err != nil {
|
|
out := "Could not locate cgroup mount point.\n"
|
|
out += desc
|
|
return Unknown, out
|
|
}
|
|
mnt = path.Dir(mnt)
|
|
if !utils.FileExists(mnt) {
|
|
out := fmt.Sprintf("Cgroup mount directory %s inaccessible.\n", mnt)
|
|
out += desc
|
|
return Unsupported, out
|
|
}
|
|
mounts, err := ioutil.ReadDir(mnt)
|
|
if err != nil {
|
|
out := fmt.Sprintf("Could not read cgroup mount directory %s.\n", mnt)
|
|
out += desc
|
|
return Unsupported, out
|
|
}
|
|
mountNames := "\tCgroup mount directories: "
|
|
for _, mount := range mounts {
|
|
mountNames += mount.Name() + " "
|
|
}
|
|
mountNames += "\n"
|
|
out := fmt.Sprintf("Cgroups are mounted at %s.\n", mnt)
|
|
out += mountNames
|
|
out += desc
|
|
info, err := ioutil.ReadFile("/proc/mounts")
|
|
if err != nil {
|
|
out := fmt.Sprintf("Could not read /proc/mounts.\n")
|
|
out += desc
|
|
return Unsupported, out
|
|
}
|
|
out += "\tCgroup mounts:\n"
|
|
for _, line := range strings.Split(string(info), "\n") {
|
|
if strings.Contains(line, " cgroup ") {
|
|
out += "\t" + line + "\n"
|
|
}
|
|
}
|
|
if mnt == recommendedMount {
|
|
return Recommended, out
|
|
}
|
|
return Supported, out
|
|
}
|
|
|
|
func validateIoScheduler(containerManager manager.Manager) (string, string) {
|
|
var desc string
|
|
mi, err := containerManager.GetMachineInfo()
|
|
if err != nil {
|
|
return Unknown, "Machine info not available\n\t"
|
|
}
|
|
cfq := false
|
|
for _, disk := range mi.DiskMap {
|
|
desc += fmt.Sprintf("\t Disk %q Scheduler type %q.\n", disk.Name, disk.Scheduler)
|
|
if disk.Scheduler == "cfq" {
|
|
cfq = true
|
|
}
|
|
}
|
|
// Since we get lot of random block devices, report recommended if
|
|
// at least one of them is on cfq. Report Supported otherwise.
|
|
if cfq {
|
|
desc = "At least one device supports 'cfq' I/O scheduler. Some disk stats can be reported.\n" + desc
|
|
return Recommended, desc
|
|
}
|
|
desc = "None of the devices support 'cfq' I/O scheduler. No disk stats can be reported.\n" + desc
|
|
return Supported, desc
|
|
}
|
|
|
|
func HandleRequest(w http.ResponseWriter, containerManager manager.Manager) error {
|
|
// Get cAdvisor version Info.
|
|
versionInfo, err := containerManager.GetVersionInfo()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
out := fmt.Sprintf("cAdvisor version: %s\n\n", versionInfo.CadvisorVersion)
|
|
|
|
// No OS is preferred or unsupported as of now.
|
|
out += fmt.Sprintf("OS version: %s\n\n", versionInfo.ContainerOsVersion)
|
|
|
|
kernelValidation, desc := validateKernelVersion(versionInfo.KernelVersion)
|
|
out += fmt.Sprintf(OutputFormat, "Kernel version", kernelValidation, desc)
|
|
|
|
cgroupValidation, desc := validateCgroups()
|
|
out += fmt.Sprintf(OutputFormat, "Cgroup setup", cgroupValidation, desc)
|
|
|
|
mountsValidation, desc := validateCgroupMounts()
|
|
out += fmt.Sprintf(OutputFormat, "Cgroup mount setup", mountsValidation, desc)
|
|
|
|
dockerValidation, desc := validateDockerVersion(versionInfo.DockerVersion)
|
|
out += fmt.Sprintf(OutputFormat, "Docker version", dockerValidation, desc)
|
|
|
|
dockerInfoValidation, desc := validateDockerInfo()
|
|
out += fmt.Sprintf(OutputFormat, "Docker driver setup", dockerInfoValidation, desc)
|
|
|
|
ioSchedulerValidation, desc := validateIoScheduler(containerManager)
|
|
out += fmt.Sprintf(OutputFormat, "Block device setup", ioSchedulerValidation, desc)
|
|
|
|
// Output debug info.
|
|
debugInfo := containerManager.DebugInfo()
|
|
for category, lines := range debugInfo {
|
|
out += fmt.Sprintf(OutputFormat, category, "", strings.Join(lines, "\n\t"))
|
|
}
|
|
|
|
_, err = w.Write([]byte(out))
|
|
return err
|
|
}
|