github.com/moby/docker@v26.1.3+incompatible/daemon/inspect.go (about) 1 // FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16: 2 //go:build go1.19 3 4 package daemon // import "github.com/docker/docker/daemon" 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 "time" 11 12 "github.com/docker/docker/api/types" 13 "github.com/docker/docker/api/types/backend" 14 containertypes "github.com/docker/docker/api/types/container" 15 networktypes "github.com/docker/docker/api/types/network" 16 "github.com/docker/docker/api/types/versions" 17 "github.com/docker/docker/container" 18 "github.com/docker/docker/daemon/config" 19 "github.com/docker/docker/daemon/network" 20 "github.com/docker/docker/errdefs" 21 "github.com/docker/docker/internal/sliceutil" 22 "github.com/docker/docker/pkg/stringid" 23 "github.com/docker/go-connections/nat" 24 ) 25 26 // ContainerInspect returns low-level information about a 27 // container. Returns an error if the container cannot be found, or if 28 // there is an error getting the data. 29 func (daemon *Daemon) ContainerInspect(ctx context.Context, name string, size bool, version string) (interface{}, error) { 30 switch { 31 case versions.LessThan(version, "1.45"): 32 ctr, err := daemon.ContainerInspectCurrent(ctx, name, size) 33 if err != nil { 34 return nil, err 35 } 36 37 shortCID := stringid.TruncateID(ctr.ID) 38 for nwName, ep := range ctr.NetworkSettings.Networks { 39 if containertypes.NetworkMode(nwName).IsUserDefined() { 40 ep.Aliases = sliceutil.Dedup(append(ep.Aliases, shortCID, ctr.Config.Hostname)) 41 } 42 } 43 44 return ctr, nil 45 default: 46 return daemon.ContainerInspectCurrent(ctx, name, size) 47 } 48 } 49 50 // ContainerInspectCurrent returns low-level information about a 51 // container in a most recent api version. 52 func (daemon *Daemon) ContainerInspectCurrent(ctx context.Context, name string, size bool) (*types.ContainerJSON, error) { 53 ctr, err := daemon.GetContainer(name) 54 if err != nil { 55 return nil, err 56 } 57 58 ctr.Lock() 59 60 base, err := daemon.getInspectData(&daemon.config().Config, ctr) 61 if err != nil { 62 ctr.Unlock() 63 return nil, err 64 } 65 66 apiNetworks := make(map[string]*networktypes.EndpointSettings) 67 for nwName, epConf := range ctr.NetworkSettings.Networks { 68 if epConf.EndpointSettings != nil { 69 // We must make a copy of this pointer object otherwise it can race with other operations 70 apiNetworks[nwName] = epConf.EndpointSettings.Copy() 71 } 72 } 73 74 mountPoints := ctr.GetMountPoints() 75 networkSettings := &types.NetworkSettings{ 76 NetworkSettingsBase: types.NetworkSettingsBase{ 77 Bridge: ctr.NetworkSettings.Bridge, 78 SandboxID: ctr.NetworkSettings.SandboxID, 79 SandboxKey: ctr.NetworkSettings.SandboxKey, 80 HairpinMode: ctr.NetworkSettings.HairpinMode, 81 LinkLocalIPv6Address: ctr.NetworkSettings.LinkLocalIPv6Address, 82 LinkLocalIPv6PrefixLen: ctr.NetworkSettings.LinkLocalIPv6PrefixLen, 83 SecondaryIPAddresses: ctr.NetworkSettings.SecondaryIPAddresses, 84 SecondaryIPv6Addresses: ctr.NetworkSettings.SecondaryIPv6Addresses, 85 }, 86 DefaultNetworkSettings: daemon.getDefaultNetworkSettings(ctr.NetworkSettings.Networks), 87 Networks: apiNetworks, 88 } 89 90 ports := make(nat.PortMap, len(ctr.NetworkSettings.Ports)) 91 for k, pm := range ctr.NetworkSettings.Ports { 92 ports[k] = pm 93 } 94 networkSettings.NetworkSettingsBase.Ports = ports 95 96 ctr.Unlock() 97 98 if size { 99 sizeRw, sizeRootFs, err := daemon.imageService.GetContainerLayerSize(ctx, base.ID) 100 if err != nil { 101 return nil, err 102 } 103 base.SizeRw = &sizeRw 104 base.SizeRootFs = &sizeRootFs 105 } 106 107 return &types.ContainerJSON{ 108 ContainerJSONBase: base, 109 Mounts: mountPoints, 110 Config: ctr.Config, 111 NetworkSettings: networkSettings, 112 }, nil 113 } 114 115 func (daemon *Daemon) getInspectData(daemonCfg *config.Config, container *container.Container) (*types.ContainerJSONBase, error) { 116 // make a copy to play with 117 hostConfig := *container.HostConfig 118 119 children := daemon.children(container) 120 hostConfig.Links = nil // do not expose the internal structure 121 for linkAlias, child := range children { 122 hostConfig.Links = append(hostConfig.Links, fmt.Sprintf("%s:%s", child.Name, linkAlias)) 123 } 124 125 // We merge the Ulimits from hostConfig with daemon default 126 daemon.mergeUlimits(&hostConfig, daemonCfg) 127 128 // Migrate the container's default network's MacAddress to the top-level 129 // Config.MacAddress field for older API versions (< 1.44). We set it here 130 // unconditionally, to keep backward compatibility with clients that use 131 // unversioned API endpoints. 132 if container.Config != nil && container.Config.MacAddress == "" { //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44. 133 if nwm := hostConfig.NetworkMode; nwm.IsBridge() || nwm.IsUserDefined() { 134 if epConf, ok := container.NetworkSettings.Networks[nwm.NetworkName()]; ok { 135 container.Config.MacAddress = epConf.DesiredMacAddress //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44. 136 } 137 } 138 } 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 daemon.UsesSnapshotter() { 188 // Additional information only applies to graphDrivers, so we're done. 189 return contJSONBase, nil 190 } 191 192 if container.RWLayer == nil { 193 if container.Dead { 194 return contJSONBase, nil 195 } 196 return nil, errdefs.System(errors.New("RWLayer of container " + container.ID + " is unexpectedly nil")) 197 } 198 199 graphDriverData, err := container.RWLayer.Metadata() 200 if err != nil { 201 if container.Dead { 202 // container is marked as Dead, and its graphDriver metadata may 203 // have been removed; we can ignore errors. 204 return contJSONBase, nil 205 } 206 return nil, errdefs.System(err) 207 } 208 209 contJSONBase.GraphDriver.Data = graphDriverData 210 return contJSONBase, nil 211 } 212 213 // ContainerExecInspect returns low-level information about the exec 214 // command. An error is returned if the exec cannot be found. 215 func (daemon *Daemon) ContainerExecInspect(id string) (*backend.ExecInspect, error) { 216 e := daemon.execCommands.Get(id) 217 if e == nil { 218 return nil, errExecNotFound(id) 219 } 220 221 if ctr := daemon.containers.Get(e.Container.ID); ctr == nil { 222 return nil, errExecNotFound(id) 223 } 224 225 e.Lock() 226 defer e.Unlock() 227 pc := inspectExecProcessConfig(e) 228 var pid int 229 if e.Process != nil { 230 pid = int(e.Process.Pid()) 231 } 232 233 return &backend.ExecInspect{ 234 ID: e.ID, 235 Running: e.Running, 236 ExitCode: e.ExitCode, 237 ProcessConfig: pc, 238 OpenStdin: e.OpenStdin, 239 OpenStdout: e.OpenStdout, 240 OpenStderr: e.OpenStderr, 241 CanRemove: e.CanRemove, 242 ContainerID: e.Container.ID, 243 DetachKeys: e.DetachKeys, 244 Pid: pid, 245 }, nil 246 } 247 248 // getDefaultNetworkSettings creates the deprecated structure that holds the information 249 // about the bridge network for a container. 250 func (daemon *Daemon) getDefaultNetworkSettings(networks map[string]*network.EndpointSettings) types.DefaultNetworkSettings { 251 var settings types.DefaultNetworkSettings 252 253 if defaultNetwork, ok := networks[networktypes.NetworkBridge]; ok && defaultNetwork.EndpointSettings != nil { 254 settings.EndpointID = defaultNetwork.EndpointID 255 settings.Gateway = defaultNetwork.Gateway 256 settings.GlobalIPv6Address = defaultNetwork.GlobalIPv6Address 257 settings.GlobalIPv6PrefixLen = defaultNetwork.GlobalIPv6PrefixLen 258 settings.IPAddress = defaultNetwork.IPAddress 259 settings.IPPrefixLen = defaultNetwork.IPPrefixLen 260 settings.IPv6Gateway = defaultNetwork.IPv6Gateway 261 settings.MacAddress = defaultNetwork.MacAddress 262 } 263 return settings 264 }