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  }