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"
|
containersApi = "containers"
|
||||||
subcontainersApi = "subcontainers"
|
subcontainersApi = "subcontainers"
|
||||||
machineApi = "machine"
|
machineApi = "machine"
|
||||||
|
dockerApi = "docker"
|
||||||
|
|
||||||
version1_0 = "v1.0"
|
version1_0 = "v1.0"
|
||||||
version1_1 = "v1.1"
|
version1_1 = "v1.1"
|
||||||
|
version1_2 = "v1.2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var supportedApiVersions map[string]struct{} = map[string]struct{}{
|
var supportedApiVersions map[string]struct{} = map[string]struct{}{
|
||||||
version1_0: {},
|
version1_0: {},
|
||||||
version1_1: {},
|
version1_1: {},
|
||||||
|
version1_2: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterHandlers(m manager.Manager) error {
|
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:]
|
requestArgs = requestElements[4:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The container name is the path after the requestType.
|
||||||
|
containerName := path.Join("/", strings.Join(requestArgs, "/"))
|
||||||
|
|
||||||
// Check elements.
|
// Check elements.
|
||||||
if len(emptyElement) != 0 {
|
if len(emptyElement) != 0 {
|
||||||
return fmt.Errorf("unexpected API request format %q", request)
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := json.Marshal(machineInfo)
|
err = writeResult(machineInfo, w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(w, "Failed to marshall MachineInfo with error: %s", err)
|
return err
|
||||||
}
|
}
|
||||||
w.Write(out)
|
|
||||||
case requestType == containersApi:
|
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)
|
glog.V(2).Infof("Api - Container(%s)", containerName)
|
||||||
|
|
||||||
// Get the query request.
|
// 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.
|
// Only output the container as JSON.
|
||||||
out, err := json.Marshal(cont)
|
err = writeResult(cont, w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to marshall container %q with error: %s", containerName, err)
|
return err
|
||||||
}
|
}
|
||||||
w.Write(out)
|
|
||||||
case requestType == subcontainersApi:
|
case requestType == subcontainersApi:
|
||||||
if version == version1_0 {
|
if version == version1_0 {
|
||||||
return fmt.Errorf("request type of %q not supported in API version %q", requestType, version)
|
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)
|
glog.V(2).Infof("Api - Subcontainers(%s)", containerName)
|
||||||
|
|
||||||
// Get the query request.
|
// 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.
|
// Only output the containers as JSON.
|
||||||
out, err := json.Marshal(containers)
|
err = writeResult(containers, w)
|
||||||
if err != nil {
|
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:
|
default:
|
||||||
return fmt.Errorf("unknown API request type %q", requestType)
|
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
|
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) {
|
func getContainerInfoRequest(body io.ReadCloser) (*info.ContainerInfoRequest, error) {
|
||||||
var query info.ContainerInfoRequest
|
var query info.ContainerInfoRequest
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ package docker
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"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.
|
// 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)")
|
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 {
|
type dockerFactory struct {
|
||||||
machineInfoFactory info.MachineInfoFactory
|
machineInfoFactory info.MachineInfoFactory
|
||||||
|
|
||||||
// Whether this system is using systemd.
|
|
||||||
useSystemd bool
|
|
||||||
|
|
||||||
// Whether docker is running with AUFS storage driver.
|
// Whether docker is running with AUFS storage driver.
|
||||||
usesAufsDriver bool
|
usesAufsDriver bool
|
||||||
|
|
||||||
@ -58,31 +66,48 @@ func (self *dockerFactory) NewContainerHandler(name string) (handler container.C
|
|||||||
client,
|
client,
|
||||||
name,
|
name,
|
||||||
self.machineInfoFactory,
|
self.machineInfoFactory,
|
||||||
self.useSystemd,
|
|
||||||
*dockerRootDir,
|
*dockerRootDir,
|
||||||
self.usesAufsDriver,
|
self.usesAufsDriver,
|
||||||
)
|
)
|
||||||
return
|
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
|
// Docker handles all containers under /docker
|
||||||
func (self *dockerFactory) CanHandle(name string) (bool, error) {
|
func (self *dockerFactory) CanHandle(name string) (bool, error) {
|
||||||
// In systemd systems the containers are: /system.slice/docker-{ID}
|
if name == "/docker" {
|
||||||
if self.useSystemd {
|
|
||||||
if !strings.HasPrefix(name, "/system.slice/docker-") {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
} else if name == "/" {
|
|
||||||
return false, nil
|
|
||||||
} else if name == "/docker" {
|
|
||||||
// We need the docker driver to handle /docker. Otherwise the aggregation at the API level will break.
|
// We need the docker driver to handle /docker. Otherwise the aggregation at the API level will break.
|
||||||
return true, nil
|
return true, nil
|
||||||
} else if !strings.HasPrefix(name, "/docker/") {
|
} else if !IsDockerContainerName(name) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the container is known to docker and it is active.
|
// 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.
|
// We assume that if Inspect fails then the container is not known to docker.
|
||||||
ctnr, err := self.client.InspectContainer(id)
|
ctnr, err := self.client.InspectContainer(id)
|
||||||
@ -162,13 +187,9 @@ func Register(factory info.MachineInfoFactory) error {
|
|||||||
|
|
||||||
f := &dockerFactory{
|
f := &dockerFactory{
|
||||||
machineInfoFactory: factory,
|
machineInfoFactory: factory,
|
||||||
useSystemd: systemd.UseSystemd(),
|
|
||||||
client: client,
|
client: client,
|
||||||
usesAufsDriver: usesAufsDriver,
|
usesAufsDriver: usesAufsDriver,
|
||||||
}
|
}
|
||||||
if f.useSystemd {
|
|
||||||
glog.Infof("System is using systemd")
|
|
||||||
}
|
|
||||||
container.RegisterContainerHandlerFactory(f)
|
container.RegisterContainerHandlerFactory(f)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,6 @@ type dockerContainerHandler struct {
|
|||||||
id string
|
id string
|
||||||
aliases []string
|
aliases []string
|
||||||
machineInfoFactory info.MachineInfoFactory
|
machineInfoFactory info.MachineInfoFactory
|
||||||
useSystemd bool
|
|
||||||
libcontainerStateDir string
|
libcontainerStateDir string
|
||||||
cgroup cgroups.Cgroup
|
cgroup cgroups.Cgroup
|
||||||
usesAufsDriver bool
|
usesAufsDriver bool
|
||||||
@ -63,7 +62,6 @@ func newDockerContainerHandler(
|
|||||||
client *docker.Client,
|
client *docker.Client,
|
||||||
name string,
|
name string,
|
||||||
machineInfoFactory info.MachineInfoFactory,
|
machineInfoFactory info.MachineInfoFactory,
|
||||||
useSystemd bool,
|
|
||||||
dockerRootDir string,
|
dockerRootDir string,
|
||||||
usesAufsDriver bool,
|
usesAufsDriver bool,
|
||||||
) (container.ContainerHandler, error) {
|
) (container.ContainerHandler, error) {
|
||||||
@ -75,7 +73,6 @@ func newDockerContainerHandler(
|
|||||||
client: client,
|
client: client,
|
||||||
name: name,
|
name: name,
|
||||||
machineInfoFactory: machineInfoFactory,
|
machineInfoFactory: machineInfoFactory,
|
||||||
useSystemd: useSystemd,
|
|
||||||
libcontainerStateDir: path.Join(dockerRootDir, pathToLibcontainerState),
|
libcontainerStateDir: path.Join(dockerRootDir, pathToLibcontainerState),
|
||||||
cgroup: cgroups.Cgroup{
|
cgroup: cgroups.Cgroup{
|
||||||
Parent: "/",
|
Parent: "/",
|
||||||
@ -88,7 +85,7 @@ func newDockerContainerHandler(
|
|||||||
if handler.isDockerRoot() {
|
if handler.isDockerRoot() {
|
||||||
return handler, nil
|
return handler, nil
|
||||||
}
|
}
|
||||||
id := containerNameToDockerId(name, useSystemd)
|
id := containerNameToDockerId(name)
|
||||||
handler.id = id
|
handler.id = id
|
||||||
ctnr, err := client.InspectContainer(id)
|
ctnr, err := client.InspectContainer(id)
|
||||||
// We assume that if Inspect fails then the container is not known to docker.
|
// We assume that if Inspect fails then the container is not known to docker.
|
||||||
@ -99,7 +96,7 @@ func newDockerContainerHandler(
|
|||||||
return handler, nil
|
return handler, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func containerNameToDockerId(name string, useSystemd bool) string {
|
func containerNameToDockerId(name string) string {
|
||||||
id := path.Base(name)
|
id := path.Base(name)
|
||||||
|
|
||||||
// Turn systemd cgroup name into Docker ID.
|
// 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.
|
// On non-systemd systems Docker containers are under /docker.
|
||||||
containerPrefix := "/docker"
|
containerPrefix := "/docker"
|
||||||
if self.useSystemd {
|
if useSystemd {
|
||||||
containerPrefix = "/system.slice"
|
containerPrefix = "/system.slice"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -26,6 +27,7 @@ import (
|
|||||||
"github.com/docker/libcontainer/cgroups"
|
"github.com/docker/libcontainer/cgroups"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/google/cadvisor/container"
|
"github.com/google/cadvisor/container"
|
||||||
|
"github.com/google/cadvisor/container/docker"
|
||||||
"github.com/google/cadvisor/info"
|
"github.com/google/cadvisor/info"
|
||||||
"github.com/google/cadvisor/storage"
|
"github.com/google/cadvisor/storage"
|
||||||
)
|
)
|
||||||
@ -48,6 +50,10 @@ type Manager interface {
|
|||||||
// Get information about all subcontainers of the specified container (includes self).
|
// Get information about all subcontainers of the specified container (includes self).
|
||||||
SubcontainersInfo(containerName string, query *info.ContainerInfoRequest) ([]*info.ContainerInfo, error)
|
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.
|
// Get information about the machine.
|
||||||
GetMachineInfo() (*info.MachineInfo, error)
|
GetMachineInfo() (*info.MachineInfo, error)
|
||||||
|
|
||||||
@ -94,13 +100,14 @@ func New(driver storage.StorageDriver) (Manager, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type manager struct {
|
type manager struct {
|
||||||
containers map[string]*containerData
|
containers map[string]*containerData
|
||||||
containersLock sync.RWMutex
|
containersLock sync.RWMutex
|
||||||
storageDriver storage.StorageDriver
|
storageDriver storage.StorageDriver
|
||||||
machineInfo info.MachineInfo
|
machineInfo info.MachineInfo
|
||||||
versionInfo info.VersionInfo
|
versionInfo info.VersionInfo
|
||||||
quitChannels []chan error
|
quitChannels []chan error
|
||||||
cadvisorContainer string
|
cadvisorContainer string
|
||||||
|
dockerContainersRegexp *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the container manager.
|
// 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 {
|
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.
|
// Get the info for each container.
|
||||||
|
Loading…
Reference in New Issue
Block a user