1package daemon // import "github.com/docker/docker/daemon"
2
3import (
4	"errors"
5	"fmt"
6	"time"
7
8	"github.com/docker/docker/api/types"
9	"github.com/docker/docker/api/types/backend"
10	networktypes "github.com/docker/docker/api/types/network"
11	"github.com/docker/docker/api/types/versions"
12	"github.com/docker/docker/api/types/versions/v1p20"
13	"github.com/docker/docker/container"
14	"github.com/docker/docker/daemon/network"
15	"github.com/docker/docker/errdefs"
16	"github.com/docker/go-connections/nat"
17)
18
19// ContainerInspect returns low-level information about a
20// container. Returns an error if the container cannot be found, or if
21// there is an error getting the data.
22func (daemon *Daemon) ContainerInspect(name string, size bool, version string) (interface{}, error) {
23	switch {
24	case versions.LessThan(version, "1.20"):
25		return daemon.containerInspectPre120(name)
26	case versions.Equal(version, "1.20"):
27		return daemon.containerInspect120(name)
28	}
29	return daemon.ContainerInspectCurrent(name, size)
30}
31
32// ContainerInspectCurrent returns low-level information about a
33// container in a most recent api version.
34func (daemon *Daemon) ContainerInspectCurrent(name string, size bool) (*types.ContainerJSON, error) {
35	container, err := daemon.GetContainer(name)
36	if err != nil {
37		return nil, err
38	}
39
40	container.Lock()
41
42	base, err := daemon.getInspectData(container)
43	if err != nil {
44		container.Unlock()
45		return nil, err
46	}
47
48	apiNetworks := make(map[string]*networktypes.EndpointSettings)
49	for name, epConf := range container.NetworkSettings.Networks {
50		if epConf.EndpointSettings != nil {
51			// We must make a copy of this pointer object otherwise it can race with other operations
52			apiNetworks[name] = epConf.EndpointSettings.Copy()
53		}
54	}
55
56	mountPoints := container.GetMountPoints()
57	networkSettings := &types.NetworkSettings{
58		NetworkSettingsBase: types.NetworkSettingsBase{
59			Bridge:                 container.NetworkSettings.Bridge,
60			SandboxID:              container.NetworkSettings.SandboxID,
61			HairpinMode:            container.NetworkSettings.HairpinMode,
62			LinkLocalIPv6Address:   container.NetworkSettings.LinkLocalIPv6Address,
63			LinkLocalIPv6PrefixLen: container.NetworkSettings.LinkLocalIPv6PrefixLen,
64			SandboxKey:             container.NetworkSettings.SandboxKey,
65			SecondaryIPAddresses:   container.NetworkSettings.SecondaryIPAddresses,
66			SecondaryIPv6Addresses: container.NetworkSettings.SecondaryIPv6Addresses,
67		},
68		DefaultNetworkSettings: daemon.getDefaultNetworkSettings(container.NetworkSettings.Networks),
69		Networks:               apiNetworks,
70	}
71
72	ports := make(nat.PortMap, len(container.NetworkSettings.Ports))
73	for k, pm := range container.NetworkSettings.Ports {
74		ports[k] = pm
75	}
76	networkSettings.NetworkSettingsBase.Ports = ports
77
78	container.Unlock()
79
80	if size {
81		sizeRw, sizeRootFs := daemon.imageService.GetContainerLayerSize(base.ID)
82		base.SizeRw = &sizeRw
83		base.SizeRootFs = &sizeRootFs
84	}
85
86	return &types.ContainerJSON{
87		ContainerJSONBase: base,
88		Mounts:            mountPoints,
89		Config:            container.Config,
90		NetworkSettings:   networkSettings,
91	}, nil
92}
93
94// containerInspect120 serializes the master version of a container into a json type.
95func (daemon *Daemon) containerInspect120(name string) (*v1p20.ContainerJSON, error) {
96	container, err := daemon.GetContainer(name)
97	if err != nil {
98		return nil, err
99	}
100
101	container.Lock()
102	defer container.Unlock()
103
104	base, err := daemon.getInspectData(container)
105	if err != nil {
106		return nil, err
107	}
108
109	mountPoints := container.GetMountPoints()
110	config := &v1p20.ContainerConfig{
111		Config:          container.Config,
112		MacAddress:      container.Config.MacAddress,
113		NetworkDisabled: container.Config.NetworkDisabled,
114		ExposedPorts:    container.Config.ExposedPorts,
115		VolumeDriver:    container.HostConfig.VolumeDriver,
116	}
117	networkSettings := daemon.getBackwardsCompatibleNetworkSettings(container.NetworkSettings)
118
119	return &v1p20.ContainerJSON{
120		ContainerJSONBase: base,
121		Mounts:            mountPoints,
122		Config:            config,
123		NetworkSettings:   networkSettings,
124	}, nil
125}
126
127func (daemon *Daemon) getInspectData(container *container.Container) (*types.ContainerJSONBase, error) {
128	// make a copy to play with
129	hostConfig := *container.HostConfig
130
131	children := daemon.children(container)
132	hostConfig.Links = nil // do not expose the internal structure
133	for linkAlias, child := range children {
134		hostConfig.Links = append(hostConfig.Links, fmt.Sprintf("%s:%s", child.Name, linkAlias))
135	}
136
137	// We merge the Ulimits from hostConfig with daemon default
138	daemon.mergeUlimits(&hostConfig)
139
140	var containerHealth *types.Health
141	if container.State.Health != nil {
142		containerHealth = &types.Health{
143			Status:        container.State.Health.Status(),
144			FailingStreak: container.State.Health.FailingStreak,
145			Log:           append([]*types.HealthcheckResult{}, container.State.Health.Log...),
146		}
147	}
148
149	containerState := &types.ContainerState{
150		Status:     container.State.StateString(),
151		Running:    container.State.Running,
152		Paused:     container.State.Paused,
153		Restarting: container.State.Restarting,
154		OOMKilled:  container.State.OOMKilled,
155		Dead:       container.State.Dead,
156		Pid:        container.State.Pid,
157		ExitCode:   container.State.ExitCode(),
158		Error:      container.State.ErrorMsg,
159		StartedAt:  container.State.StartedAt.Format(time.RFC3339Nano),
160		FinishedAt: container.State.FinishedAt.Format(time.RFC3339Nano),
161		Health:     containerHealth,
162	}
163
164	contJSONBase := &types.ContainerJSONBase{
165		ID:           container.ID,
166		Created:      container.Created.Format(time.RFC3339Nano),
167		Path:         container.Path,
168		Args:         container.Args,
169		State:        containerState,
170		Image:        container.ImageID.String(),
171		LogPath:      container.LogPath,
172		Name:         container.Name,
173		RestartCount: container.RestartCount,
174		Driver:       container.Driver,
175		Platform:     container.OS,
176		MountLabel:   container.MountLabel,
177		ProcessLabel: container.ProcessLabel,
178		ExecIDs:      container.GetExecIDs(),
179		HostConfig:   &hostConfig,
180	}
181
182	// Now set any platform-specific fields
183	contJSONBase = setPlatformSpecificContainerFields(container, contJSONBase)
184
185	contJSONBase.GraphDriver.Name = container.Driver
186
187	if container.RWLayer == nil {
188		if container.Dead {
189			return contJSONBase, nil
190		}
191		return nil, errdefs.System(errors.New("RWLayer of container " + container.ID + " is unexpectedly nil"))
192	}
193
194	graphDriverData, err := container.RWLayer.Metadata()
195	// If container is marked as Dead, the container's graphdriver metadata
196	// could have been removed, it will cause error if we try to get the metadata,
197	// we can ignore the error if the container is dead.
198	if err != nil {
199		if !container.Dead {
200			return nil, errdefs.System(err)
201		}
202	} else {
203		contJSONBase.GraphDriver.Data = graphDriverData
204	}
205
206	return contJSONBase, nil
207}
208
209// ContainerExecInspect returns low-level information about the exec
210// command. An error is returned if the exec cannot be found.
211func (daemon *Daemon) ContainerExecInspect(id string) (*backend.ExecInspect, error) {
212	e := daemon.execCommands.Get(id)
213	if e == nil {
214		return nil, errExecNotFound(id)
215	}
216
217	if container := daemon.containers.Get(e.ContainerID); container == nil {
218		return nil, errExecNotFound(id)
219	}
220
221	pc := inspectExecProcessConfig(e)
222
223	return &backend.ExecInspect{
224		ID:            e.ID,
225		Running:       e.Running,
226		ExitCode:      e.ExitCode,
227		ProcessConfig: pc,
228		OpenStdin:     e.OpenStdin,
229		OpenStdout:    e.OpenStdout,
230		OpenStderr:    e.OpenStderr,
231		CanRemove:     e.CanRemove,
232		ContainerID:   e.ContainerID,
233		DetachKeys:    e.DetachKeys,
234		Pid:           e.Pid,
235	}, nil
236}
237
238func (daemon *Daemon) getBackwardsCompatibleNetworkSettings(settings *network.Settings) *v1p20.NetworkSettings {
239	result := &v1p20.NetworkSettings{
240		NetworkSettingsBase: types.NetworkSettingsBase{
241			Bridge:                 settings.Bridge,
242			SandboxID:              settings.SandboxID,
243			HairpinMode:            settings.HairpinMode,
244			LinkLocalIPv6Address:   settings.LinkLocalIPv6Address,
245			LinkLocalIPv6PrefixLen: settings.LinkLocalIPv6PrefixLen,
246			Ports:                  settings.Ports,
247			SandboxKey:             settings.SandboxKey,
248			SecondaryIPAddresses:   settings.SecondaryIPAddresses,
249			SecondaryIPv6Addresses: settings.SecondaryIPv6Addresses,
250		},
251		DefaultNetworkSettings: daemon.getDefaultNetworkSettings(settings.Networks),
252	}
253
254	return result
255}
256
257// getDefaultNetworkSettings creates the deprecated structure that holds the information
258// about the bridge network for a container.
259func (daemon *Daemon) getDefaultNetworkSettings(networks map[string]*network.EndpointSettings) types.DefaultNetworkSettings {
260	var settings types.DefaultNetworkSettings
261
262	if defaultNetwork, ok := networks["bridge"]; ok && defaultNetwork.EndpointSettings != nil {
263		settings.EndpointID = defaultNetwork.EndpointID
264		settings.Gateway = defaultNetwork.Gateway
265		settings.GlobalIPv6Address = defaultNetwork.GlobalIPv6Address
266		settings.GlobalIPv6PrefixLen = defaultNetwork.GlobalIPv6PrefixLen
267		settings.IPAddress = defaultNetwork.IPAddress
268		settings.IPPrefixLen = defaultNetwork.IPPrefixLen
269		settings.IPv6Gateway = defaultNetwork.IPv6Gateway
270		settings.MacAddress = defaultNetwork.MacAddress
271	}
272	return settings
273}
274