Merge pull request #351 from rjnagal/validate
Add a validation handler to check cAdvisor's underlying dependencies.
This commit is contained in:
commit
02e03821f1
@ -34,6 +34,7 @@ import (
|
||||
"github.com/google/cadvisor/manager"
|
||||
"github.com/google/cadvisor/pages"
|
||||
"github.com/google/cadvisor/pages/static"
|
||||
"github.com/google/cadvisor/validate"
|
||||
)
|
||||
|
||||
var argIp = flag.String("listen_ip", "", "IP to listen on, defaults to all IPs")
|
||||
@ -84,6 +85,14 @@ func main() {
|
||||
glog.Fatalf("Failed to register healthz handler: %s", err)
|
||||
}
|
||||
|
||||
// Validation/Debug handler.
|
||||
http.HandleFunc(validate.ValidatePage, func(w http.ResponseWriter, r *http.Request) {
|
||||
err := validate.HandleRequest(w, containerManager)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "%s", err)
|
||||
}
|
||||
})
|
||||
|
||||
// Register API handler.
|
||||
if err := api.RegisterHandlers(containerManager); err != nil {
|
||||
glog.Fatalf("Failed to register API handlers: %s", err)
|
||||
|
229
validate/validate.go
Normal file
229
validate/validate.go
Normal file
@ -0,0 +1,229 @@
|
||||
// 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"
|
||||
"github.com/google/cadvisor/manager"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/libcontainer/cgroups"
|
||||
dclient "github.com/fsouza/go-dockerclient"
|
||||
"github.com/google/cadvisor/container/docker"
|
||||
)
|
||||
|
||||
const ValidatePage = "/validate/"
|
||||
const Supported = "[Supported, but not recommended]"
|
||||
const Unsupported = "[Unsupported]"
|
||||
const Recommended = "[Supported and recommended]"
|
||||
const Unknown = "[Unknown]"
|
||||
const VersionFormat = "%d.%d.%s"
|
||||
const 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 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
|
||||
return Recommended, out
|
||||
}
|
||||
|
||||
func validateDockerInfo() (string, string) {
|
||||
client, err := dclient.NewClient(*docker.ArgDockerEndpoint)
|
||||
if err == nil {
|
||||
info, err := client.Info()
|
||||
if err == nil {
|
||||
execDriver := info.Get("ExecutionDriver")
|
||||
storageDriver := info.Get("Driver")
|
||||
desc := fmt.Sprintf("Docker exec driver is %s. Storage driver is %s.\n", execDriver, storageDriver)
|
||||
if strings.Contains(execDriver, "native") {
|
||||
return Recommended, desc
|
||||
} else if strings.Contains(execDriver, "lxc") {
|
||||
return Supported, desc
|
||||
}
|
||||
return Unknown, desc
|
||||
}
|
||||
}
|
||||
return Unknown, "Docker remote API not reachable\n\t"
|
||||
}
|
||||
|
||||
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 = strings.TrimSuffix(mnt, "/cpu")
|
||||
if _, err := os.Stat(mnt); err != nil {
|
||||
out := fmt.Sprintf("Cgroup mount directory %s inaccessible.\n", mnt)
|
||||
out += desc
|
||||
return Unsupported, out
|
||||
}
|
||||
out := fmt.Sprintf("Cgroups are mounted at %s.\n", mnt)
|
||||
out += desc
|
||||
if mnt == recommendedMount {
|
||||
return Recommended, out
|
||||
}
|
||||
return Supported, out
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
_, err = w.Write([]byte(out))
|
||||
return err
|
||||
}
|
Loading…
Reference in New Issue
Block a user