Add API version v1.2 with /docker endpoint.
The /docker endpoint lists all Docker containers under one unified namespace.
This commit is contained in:
parent
42e9e50b03
commit
8aa05b0c39
@ -34,14 +34,17 @@ const (
|
||||
containersApi = "containers"
|
||||
subcontainersApi = "subcontainers"
|
||||
machineApi = "machine"
|
||||
dockerApi = "docker"
|
||||
|
||||
version1_0 = "v1.0"
|
||||
version1_1 = "v1.1"
|
||||
version1_2 = "v1.2"
|
||||
)
|
||||
|
||||
var supportedApiVersions map[string]struct{} = map[string]struct{}{
|
||||
version1_0: {},
|
||||
version1_1: {},
|
||||
version1_2: {},
|
||||
}
|
||||
|
||||
func RegisterHandlers(m manager.Manager) error {
|
||||
@ -78,6 +81,9 @@ func handleRequest(m manager.Manager, w http.ResponseWriter, r *http.Request) er
|
||||
requestArgs = requestElements[4:]
|
||||
}
|
||||
|
||||
// The container name is the path after the requestType.
|
||||
containerName := path.Join("/", strings.Join(requestArgs, "/"))
|
||||
|
||||
// Check elements.
|
||||
if len(emptyElement) != 0 {
|
||||
return fmt.Errorf("unexpected API request format %q", request)
|
||||
@ -99,15 +105,11 @@ func handleRequest(m manager.Manager, w http.ResponseWriter, r *http.Request) er
|
||||
return err
|
||||
}
|
||||
|
||||
out, err := json.Marshal(machineInfo)
|
||||
err = writeResult(machineInfo, w)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "Failed to marshall MachineInfo with error: %s", err)
|
||||
return err
|
||||
}
|
||||
w.Write(out)
|
||||
case requestType == containersApi:
|
||||
// The container name is the path after the requestType.
|
||||
containerName := path.Join("/", strings.Join(requestArgs, "/"))
|
||||
|
||||
glog.V(2).Infof("Api - Container(%s)", containerName)
|
||||
|
||||
// Get the query request.
|
||||
@ -123,19 +125,15 @@ func handleRequest(m manager.Manager, w http.ResponseWriter, r *http.Request) er
|
||||
}
|
||||
|
||||
// Only output the container as JSON.
|
||||
out, err := json.Marshal(cont)
|
||||
err = writeResult(cont, w)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshall container %q with error: %s", containerName, err)
|
||||
return err
|
||||
}
|
||||
w.Write(out)
|
||||
case requestType == subcontainersApi:
|
||||
if version == version1_0 {
|
||||
return fmt.Errorf("request type of %q not supported in API version %q", requestType, version)
|
||||
}
|
||||
|
||||
// The container name is the path after the requestType.
|
||||
containerName := path.Join("/", strings.Join(requestArgs, "/"))
|
||||
|
||||
glog.V(2).Infof("Api - Subcontainers(%s)", containerName)
|
||||
|
||||
// Get the query request.
|
||||
@ -151,11 +149,34 @@ func handleRequest(m manager.Manager, w http.ResponseWriter, r *http.Request) er
|
||||
}
|
||||
|
||||
// Only output the containers as JSON.
|
||||
out, err := json.Marshal(containers)
|
||||
err = writeResult(containers, w)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshall container %q with error: %s", containerName, err)
|
||||
return err
|
||||
}
|
||||
case requestType == dockerApi:
|
||||
if version == version1_0 || version == version1_1 {
|
||||
return fmt.Errorf("request type of %q not supported in API version %q", requestType, version)
|
||||
}
|
||||
|
||||
glog.V(2).Infof("Api - Docker(%s)", containerName)
|
||||
|
||||
// Get the query request.
|
||||
query, err := getContainerInfoRequest(r.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the Docker containers.
|
||||
containers, err := m.DockerContainersInfo(containerName, query)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get docker containers for %q with error: %s", containerName, err)
|
||||
}
|
||||
|
||||
// Only output the containers as JSON.
|
||||
err = writeResult(containers, w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.Write(out)
|
||||
default:
|
||||
return fmt.Errorf("unknown API request type %q", requestType)
|
||||
}
|
||||
@ -164,6 +185,16 @@ func handleRequest(m manager.Manager, w http.ResponseWriter, r *http.Request) er
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeResult(res interface{}, w http.ResponseWriter) error {
|
||||
out, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshall response %+v with error: %s", res, err)
|
||||
}
|
||||
|
||||
w.Write(out)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getContainerInfoRequest(body io.ReadCloser) (*info.ContainerInfoRequest, error) {
|
||||
var query info.ContainerInfoRequest
|
||||
|
||||
|
@ -17,6 +17,7 @@ package docker
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -33,12 +34,19 @@ var ArgDockerEndpoint = flag.String("docker", "unix:///var/run/docker.sock", "do
|
||||
// Basepath to all container specific information that libcontainer stores.
|
||||
var dockerRootDir = flag.String("docker_root", "/var/lib/docker", "Absolute path to the Docker state root directory (default: /var/lib/docker)")
|
||||
|
||||
// Whether the system is using Systemd.
|
||||
var useSystemd bool
|
||||
|
||||
func init() {
|
||||
useSystemd = systemd.UseSystemd()
|
||||
if useSystemd {
|
||||
glog.Infof("System is using systemd")
|
||||
}
|
||||
}
|
||||
|
||||
type dockerFactory struct {
|
||||
machineInfoFactory info.MachineInfoFactory
|
||||
|
||||
// Whether this system is using systemd.
|
||||
useSystemd bool
|
||||
|
||||
// Whether docker is running with AUFS storage driver.
|
||||
usesAufsDriver bool
|
||||
|
||||
@ -58,31 +66,48 @@ func (self *dockerFactory) NewContainerHandler(name string) (handler container.C
|
||||
client,
|
||||
name,
|
||||
self.machineInfoFactory,
|
||||
self.useSystemd,
|
||||
*dockerRootDir,
|
||||
self.usesAufsDriver,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Returns whether the specified full container name corresponds to a Docker container.
|
||||
func IsDockerContainerName(name string) bool {
|
||||
if useSystemd {
|
||||
// In systemd systems the containers are: /system.slice/docker-{ID}
|
||||
return strings.HasPrefix(name, "/system.slice/docker-")
|
||||
} else {
|
||||
return strings.HasPrefix(name, "/docker/")
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a list of possible full container names for the specified Docker container name.
|
||||
func FullDockerContainerNames(dockerName string) []string {
|
||||
names := make([]string, 0, 2)
|
||||
|
||||
// Add the full container name.
|
||||
if useSystemd {
|
||||
names = append(names, path.Join("/system.slice", fmt.Sprintf("docker-%s.scope", dockerName)))
|
||||
} else {
|
||||
names = append(names, path.Join("/docker", dockerName))
|
||||
}
|
||||
|
||||
// Add the Docker alias name.
|
||||
return append(names, path.Join("/docker", dockerName))
|
||||
}
|
||||
|
||||
// Docker handles all containers under /docker
|
||||
func (self *dockerFactory) CanHandle(name string) (bool, error) {
|
||||
// In systemd systems the containers are: /system.slice/docker-{ID}
|
||||
if self.useSystemd {
|
||||
if !strings.HasPrefix(name, "/system.slice/docker-") {
|
||||
return false, nil
|
||||
}
|
||||
} else if name == "/" {
|
||||
return false, nil
|
||||
} else if name == "/docker" {
|
||||
if name == "/docker" {
|
||||
// We need the docker driver to handle /docker. Otherwise the aggregation at the API level will break.
|
||||
return true, nil
|
||||
} else if !strings.HasPrefix(name, "/docker/") {
|
||||
} else if !IsDockerContainerName(name) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Check if the container is known to docker and it is active.
|
||||
id := containerNameToDockerId(name, self.useSystemd)
|
||||
id := containerNameToDockerId(name)
|
||||
|
||||
// We assume that if Inspect fails then the container is not known to docker.
|
||||
ctnr, err := self.client.InspectContainer(id)
|
||||
@ -162,13 +187,9 @@ func Register(factory info.MachineInfoFactory) error {
|
||||
|
||||
f := &dockerFactory{
|
||||
machineInfoFactory: factory,
|
||||
useSystemd: systemd.UseSystemd(),
|
||||
client: client,
|
||||
usesAufsDriver: usesAufsDriver,
|
||||
}
|
||||
if f.useSystemd {
|
||||
glog.Infof("System is using systemd")
|
||||
}
|
||||
container.RegisterContainerHandlerFactory(f)
|
||||
return nil
|
||||
}
|
||||
|
@ -51,7 +51,6 @@ type dockerContainerHandler struct {
|
||||
id string
|
||||
aliases []string
|
||||
machineInfoFactory info.MachineInfoFactory
|
||||
useSystemd bool
|
||||
libcontainerStateDir string
|
||||
cgroup cgroups.Cgroup
|
||||
usesAufsDriver bool
|
||||
@ -63,7 +62,6 @@ func newDockerContainerHandler(
|
||||
client *docker.Client,
|
||||
name string,
|
||||
machineInfoFactory info.MachineInfoFactory,
|
||||
useSystemd bool,
|
||||
dockerRootDir string,
|
||||
usesAufsDriver bool,
|
||||
) (container.ContainerHandler, error) {
|
||||
@ -75,7 +73,6 @@ func newDockerContainerHandler(
|
||||
client: client,
|
||||
name: name,
|
||||
machineInfoFactory: machineInfoFactory,
|
||||
useSystemd: useSystemd,
|
||||
libcontainerStateDir: path.Join(dockerRootDir, pathToLibcontainerState),
|
||||
cgroup: cgroups.Cgroup{
|
||||
Parent: "/",
|
||||
@ -88,7 +85,7 @@ func newDockerContainerHandler(
|
||||
if handler.isDockerRoot() {
|
||||
return handler, nil
|
||||
}
|
||||
id := containerNameToDockerId(name, useSystemd)
|
||||
id := containerNameToDockerId(name)
|
||||
handler.id = id
|
||||
ctnr, err := client.InspectContainer(id)
|
||||
// We assume that if Inspect fails then the container is not known to docker.
|
||||
@ -99,7 +96,7 @@ func newDockerContainerHandler(
|
||||
return handler, nil
|
||||
}
|
||||
|
||||
func containerNameToDockerId(name string, useSystemd bool) string {
|
||||
func containerNameToDockerId(name string) string {
|
||||
id := path.Base(name)
|
||||
|
||||
// Turn systemd cgroup name into Docker ID.
|
||||
@ -320,7 +317,7 @@ func (self *dockerContainerHandler) ListContainers(listType container.ListType)
|
||||
|
||||
// On non-systemd systems Docker containers are under /docker.
|
||||
containerPrefix := "/docker"
|
||||
if self.useSystemd {
|
||||
if useSystemd {
|
||||
containerPrefix = "/system.slice"
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@ -26,6 +27,7 @@ import (
|
||||
"github.com/docker/libcontainer/cgroups"
|
||||
"github.com/golang/glog"
|
||||
"github.com/google/cadvisor/container"
|
||||
"github.com/google/cadvisor/container/docker"
|
||||
"github.com/google/cadvisor/info"
|
||||
"github.com/google/cadvisor/storage"
|
||||
)
|
||||
@ -48,6 +50,10 @@ type Manager interface {
|
||||
// Get information about all subcontainers of the specified container (includes self).
|
||||
SubcontainersInfo(containerName string, query *info.ContainerInfoRequest) ([]*info.ContainerInfo, error)
|
||||
|
||||
// Get information for the specified Docker container (or "/" for all Docker containers).
|
||||
// Full container names here are interpreted within the Docker namespace (e.g.: /test -> top-level Docker container named 'test').
|
||||
DockerContainersInfo(containerName string, query *info.ContainerInfoRequest) ([]*info.ContainerInfo, error)
|
||||
|
||||
// Get information about the machine.
|
||||
GetMachineInfo() (*info.MachineInfo, error)
|
||||
|
||||
@ -94,13 +100,14 @@ func New(driver storage.StorageDriver) (Manager, error) {
|
||||
}
|
||||
|
||||
type manager struct {
|
||||
containers map[string]*containerData
|
||||
containersLock sync.RWMutex
|
||||
storageDriver storage.StorageDriver
|
||||
machineInfo info.MachineInfo
|
||||
versionInfo info.VersionInfo
|
||||
quitChannels []chan error
|
||||
cadvisorContainer string
|
||||
containers map[string]*containerData
|
||||
containersLock sync.RWMutex
|
||||
storageDriver storage.StorageDriver
|
||||
machineInfo info.MachineInfo
|
||||
versionInfo info.VersionInfo
|
||||
quitChannels []chan error
|
||||
cadvisorContainer string
|
||||
dockerContainersRegexp *regexp.Regexp
|
||||
}
|
||||
|
||||
// Start the container manager.
|
||||
@ -249,8 +256,54 @@ func (self *manager) SubcontainersInfo(containerName string, query *info.Contain
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return self.containerDataSliceToContainerInfoSlice(containers, query)
|
||||
}
|
||||
|
||||
func (self *manager) DockerContainersInfo(containerName string, query *info.ContainerInfoRequest) ([]*info.ContainerInfo, error) {
|
||||
var containers []*containerData
|
||||
err := func() error {
|
||||
self.containersLock.RLock()
|
||||
defer self.containersLock.RUnlock()
|
||||
containers = make([]*containerData, 0, len(self.containers))
|
||||
|
||||
if containerName == "/" {
|
||||
// Get all the Docker containers.
|
||||
for i := range self.containers {
|
||||
if docker.IsDockerContainerName(self.containers[i].info.Name) {
|
||||
containers = append(containers, self.containers[i])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Strip "/"
|
||||
containerName = strings.Trim(containerName, "/")
|
||||
|
||||
// Get the specified container (check all possible Docker names).
|
||||
possibleNames := docker.FullDockerContainerNames(containerName)
|
||||
found := false
|
||||
for _, fullName := range possibleNames {
|
||||
cont, ok := self.containers[fullName]
|
||||
if ok {
|
||||
containers = append(containers, cont)
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("unable to find Docker container %q with full names %v", containerName, possibleNames)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return self.containerDataSliceToContainerInfoSlice(containers, query)
|
||||
}
|
||||
|
||||
func (self *manager) containerDataSliceToContainerInfoSlice(containers []*containerData, query *info.ContainerInfoRequest) ([]*info.ContainerInfo, error) {
|
||||
if len(containers) == 0 {
|
||||
return nil, fmt.Errorf("unknown container %q", containerName)
|
||||
return nil, fmt.Errorf("no containers found")
|
||||
}
|
||||
|
||||
// Get the info for each container.
|
||||
|
Loading…
Reference in New Issue
Block a user