
The containerd client is very chatty at the best of times. Because the libcontained API is stateless and references containers and processes by string ID for every method call, the implementation is essentially forced to use the containerd client in a way which amplifies the number of redundant RPCs invoked to perform any operation. The libcontainerd remote implementation has to reload the containerd container, task and/or process metadata for nearly every operation. This in turn amplifies the number of context switches between dockerd and containerd to perform any container operation or handle a containerd event, increasing the load on the system which could otherwise be allocated to workloads. Overhaul the libcontainerd interface to reduce the impedance mismatch with the containerd client so that the containerd client can be used more efficiently. Split the API out into container, task and process interfaces which the consumer is expected to retain so that libcontainerd can retain state---especially the analogous containerd client objects---without having to manage any state-store inside the libcontainerd client. Signed-off-by: Cory Snider <csnider@mirantis.com>
277 lines
9 KiB
Go
277 lines
9 KiB
Go
package daemon // import "github.com/docker/docker/daemon"
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/api/types/backend"
|
|
networktypes "github.com/docker/docker/api/types/network"
|
|
"github.com/docker/docker/api/types/versions"
|
|
"github.com/docker/docker/api/types/versions/v1p20"
|
|
"github.com/docker/docker/container"
|
|
"github.com/docker/docker/daemon/network"
|
|
"github.com/docker/docker/errdefs"
|
|
"github.com/docker/go-connections/nat"
|
|
)
|
|
|
|
// ContainerInspect returns low-level information about a
|
|
// container. Returns an error if the container cannot be found, or if
|
|
// there is an error getting the data.
|
|
func (daemon *Daemon) ContainerInspect(name string, size bool, version string) (interface{}, error) {
|
|
switch {
|
|
case versions.LessThan(version, "1.20"):
|
|
return daemon.containerInspectPre120(name)
|
|
case versions.Equal(version, "1.20"):
|
|
return daemon.containerInspect120(name)
|
|
}
|
|
return daemon.ContainerInspectCurrent(name, size)
|
|
}
|
|
|
|
// ContainerInspectCurrent returns low-level information about a
|
|
// container in a most recent api version.
|
|
func (daemon *Daemon) ContainerInspectCurrent(name string, size bool) (*types.ContainerJSON, error) {
|
|
ctr, err := daemon.GetContainer(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ctr.Lock()
|
|
|
|
base, err := daemon.getInspectData(ctr)
|
|
if err != nil {
|
|
ctr.Unlock()
|
|
return nil, err
|
|
}
|
|
|
|
apiNetworks := make(map[string]*networktypes.EndpointSettings)
|
|
for name, epConf := range ctr.NetworkSettings.Networks {
|
|
if epConf.EndpointSettings != nil {
|
|
// We must make a copy of this pointer object otherwise it can race with other operations
|
|
apiNetworks[name] = epConf.EndpointSettings.Copy()
|
|
}
|
|
}
|
|
|
|
mountPoints := ctr.GetMountPoints()
|
|
networkSettings := &types.NetworkSettings{
|
|
NetworkSettingsBase: types.NetworkSettingsBase{
|
|
Bridge: ctr.NetworkSettings.Bridge,
|
|
SandboxID: ctr.NetworkSettings.SandboxID,
|
|
HairpinMode: ctr.NetworkSettings.HairpinMode,
|
|
LinkLocalIPv6Address: ctr.NetworkSettings.LinkLocalIPv6Address,
|
|
LinkLocalIPv6PrefixLen: ctr.NetworkSettings.LinkLocalIPv6PrefixLen,
|
|
SandboxKey: ctr.NetworkSettings.SandboxKey,
|
|
SecondaryIPAddresses: ctr.NetworkSettings.SecondaryIPAddresses,
|
|
SecondaryIPv6Addresses: ctr.NetworkSettings.SecondaryIPv6Addresses,
|
|
},
|
|
DefaultNetworkSettings: daemon.getDefaultNetworkSettings(ctr.NetworkSettings.Networks),
|
|
Networks: apiNetworks,
|
|
}
|
|
|
|
ports := make(nat.PortMap, len(ctr.NetworkSettings.Ports))
|
|
for k, pm := range ctr.NetworkSettings.Ports {
|
|
ports[k] = pm
|
|
}
|
|
networkSettings.NetworkSettingsBase.Ports = ports
|
|
|
|
ctr.Unlock()
|
|
|
|
if size {
|
|
sizeRw, sizeRootFs := daemon.imageService.GetContainerLayerSize(base.ID)
|
|
base.SizeRw = &sizeRw
|
|
base.SizeRootFs = &sizeRootFs
|
|
}
|
|
|
|
return &types.ContainerJSON{
|
|
ContainerJSONBase: base,
|
|
Mounts: mountPoints,
|
|
Config: ctr.Config,
|
|
NetworkSettings: networkSettings,
|
|
}, nil
|
|
}
|
|
|
|
// containerInspect120 serializes the master version of a container into a json type.
|
|
func (daemon *Daemon) containerInspect120(name string) (*v1p20.ContainerJSON, error) {
|
|
container, err := daemon.GetContainer(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
container.Lock()
|
|
defer container.Unlock()
|
|
|
|
base, err := daemon.getInspectData(container)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
mountPoints := container.GetMountPoints()
|
|
config := &v1p20.ContainerConfig{
|
|
Config: container.Config,
|
|
MacAddress: container.Config.MacAddress,
|
|
NetworkDisabled: container.Config.NetworkDisabled,
|
|
ExposedPorts: container.Config.ExposedPorts,
|
|
VolumeDriver: container.HostConfig.VolumeDriver,
|
|
}
|
|
networkSettings := daemon.getBackwardsCompatibleNetworkSettings(container.NetworkSettings)
|
|
|
|
return &v1p20.ContainerJSON{
|
|
ContainerJSONBase: base,
|
|
Mounts: mountPoints,
|
|
Config: config,
|
|
NetworkSettings: networkSettings,
|
|
}, nil
|
|
}
|
|
|
|
func (daemon *Daemon) getInspectData(container *container.Container) (*types.ContainerJSONBase, error) {
|
|
// make a copy to play with
|
|
hostConfig := *container.HostConfig
|
|
|
|
children := daemon.children(container)
|
|
hostConfig.Links = nil // do not expose the internal structure
|
|
for linkAlias, child := range children {
|
|
hostConfig.Links = append(hostConfig.Links, fmt.Sprintf("%s:%s", child.Name, linkAlias))
|
|
}
|
|
|
|
// We merge the Ulimits from hostConfig with daemon default
|
|
daemon.mergeUlimits(&hostConfig)
|
|
|
|
var containerHealth *types.Health
|
|
if container.State.Health != nil {
|
|
containerHealth = &types.Health{
|
|
Status: container.State.Health.Status(),
|
|
FailingStreak: container.State.Health.FailingStreak,
|
|
Log: append([]*types.HealthcheckResult{}, container.State.Health.Log...),
|
|
}
|
|
}
|
|
|
|
containerState := &types.ContainerState{
|
|
Status: container.State.StateString(),
|
|
Running: container.State.Running,
|
|
Paused: container.State.Paused,
|
|
Restarting: container.State.Restarting,
|
|
OOMKilled: container.State.OOMKilled,
|
|
Dead: container.State.Dead,
|
|
Pid: container.State.Pid,
|
|
ExitCode: container.State.ExitCode(),
|
|
Error: container.State.ErrorMsg,
|
|
StartedAt: container.State.StartedAt.Format(time.RFC3339Nano),
|
|
FinishedAt: container.State.FinishedAt.Format(time.RFC3339Nano),
|
|
Health: containerHealth,
|
|
}
|
|
|
|
contJSONBase := &types.ContainerJSONBase{
|
|
ID: container.ID,
|
|
Created: container.Created.Format(time.RFC3339Nano),
|
|
Path: container.Path,
|
|
Args: container.Args,
|
|
State: containerState,
|
|
Image: container.ImageID.String(),
|
|
LogPath: container.LogPath,
|
|
Name: container.Name,
|
|
RestartCount: container.RestartCount,
|
|
Driver: container.Driver,
|
|
Platform: container.OS,
|
|
MountLabel: container.MountLabel,
|
|
ProcessLabel: container.ProcessLabel,
|
|
ExecIDs: container.GetExecIDs(),
|
|
HostConfig: &hostConfig,
|
|
}
|
|
|
|
// Now set any platform-specific fields
|
|
contJSONBase = setPlatformSpecificContainerFields(container, contJSONBase)
|
|
|
|
contJSONBase.GraphDriver.Name = container.Driver
|
|
|
|
if container.RWLayer == nil {
|
|
if container.Dead {
|
|
return contJSONBase, nil
|
|
}
|
|
return nil, errdefs.System(errors.New("RWLayer of container " + container.ID + " is unexpectedly nil"))
|
|
}
|
|
|
|
graphDriverData, err := container.RWLayer.Metadata()
|
|
// If container is marked as Dead, the container's graphdriver metadata
|
|
// could have been removed, it will cause error if we try to get the metadata,
|
|
// we can ignore the error if the container is dead.
|
|
if err != nil {
|
|
if !container.Dead {
|
|
return nil, errdefs.System(err)
|
|
}
|
|
} else {
|
|
contJSONBase.GraphDriver.Data = graphDriverData
|
|
}
|
|
|
|
return contJSONBase, nil
|
|
}
|
|
|
|
// ContainerExecInspect returns low-level information about the exec
|
|
// command. An error is returned if the exec cannot be found.
|
|
func (daemon *Daemon) ContainerExecInspect(id string) (*backend.ExecInspect, error) {
|
|
e := daemon.execCommands.Get(id)
|
|
if e == nil {
|
|
return nil, errExecNotFound(id)
|
|
}
|
|
|
|
if ctr := daemon.containers.Get(e.Container.ID); ctr == nil {
|
|
return nil, errExecNotFound(id)
|
|
}
|
|
|
|
pc := inspectExecProcessConfig(e)
|
|
var pid int
|
|
if e.Process != nil {
|
|
pid = int(e.Process.Pid())
|
|
}
|
|
|
|
return &backend.ExecInspect{
|
|
ID: e.ID,
|
|
Running: e.Running,
|
|
ExitCode: e.ExitCode,
|
|
ProcessConfig: pc,
|
|
OpenStdin: e.OpenStdin,
|
|
OpenStdout: e.OpenStdout,
|
|
OpenStderr: e.OpenStderr,
|
|
CanRemove: e.CanRemove,
|
|
ContainerID: e.Container.ID,
|
|
DetachKeys: e.DetachKeys,
|
|
Pid: pid,
|
|
}, nil
|
|
}
|
|
|
|
func (daemon *Daemon) getBackwardsCompatibleNetworkSettings(settings *network.Settings) *v1p20.NetworkSettings {
|
|
result := &v1p20.NetworkSettings{
|
|
NetworkSettingsBase: types.NetworkSettingsBase{
|
|
Bridge: settings.Bridge,
|
|
SandboxID: settings.SandboxID,
|
|
HairpinMode: settings.HairpinMode,
|
|
LinkLocalIPv6Address: settings.LinkLocalIPv6Address,
|
|
LinkLocalIPv6PrefixLen: settings.LinkLocalIPv6PrefixLen,
|
|
Ports: settings.Ports,
|
|
SandboxKey: settings.SandboxKey,
|
|
SecondaryIPAddresses: settings.SecondaryIPAddresses,
|
|
SecondaryIPv6Addresses: settings.SecondaryIPv6Addresses,
|
|
},
|
|
DefaultNetworkSettings: daemon.getDefaultNetworkSettings(settings.Networks),
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// getDefaultNetworkSettings creates the deprecated structure that holds the information
|
|
// about the bridge network for a container.
|
|
func (daemon *Daemon) getDefaultNetworkSettings(networks map[string]*network.EndpointSettings) types.DefaultNetworkSettings {
|
|
var settings types.DefaultNetworkSettings
|
|
|
|
if defaultNetwork, ok := networks["bridge"]; ok && defaultNetwork.EndpointSettings != nil {
|
|
settings.EndpointID = defaultNetwork.EndpointID
|
|
settings.Gateway = defaultNetwork.Gateway
|
|
settings.GlobalIPv6Address = defaultNetwork.GlobalIPv6Address
|
|
settings.GlobalIPv6PrefixLen = defaultNetwork.GlobalIPv6PrefixLen
|
|
settings.IPAddress = defaultNetwork.IPAddress
|
|
settings.IPPrefixLen = defaultNetwork.IPPrefixLen
|
|
settings.IPv6Gateway = defaultNetwork.IPv6Gateway
|
|
settings.MacAddress = defaultNetwork.MacAddress
|
|
}
|
|
return settings
|
|
}
|