github.com/toplink-cn/moby@v0.0.0-20240305205811-460b4aebdf81/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.IsDefault() || nwm.IsBridge() || nwm.IsUserDefined() {
   134  			name := nwm.NetworkName()
   135  			if nwm.IsDefault() {
   136  				name = daemon.netController.Config().DefaultNetwork
   137  			}
   138  			if epConf, ok := container.NetworkSettings.Networks[name]; ok {
   139  				container.Config.MacAddress = epConf.DesiredMacAddress //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44.
   140  			}
   141  		}
   142  	}
   143  
   144  	var containerHealth *types.Health
   145  	if container.State.Health != nil {
   146  		containerHealth = &types.Health{
   147  			Status:        container.State.Health.Status(),
   148  			FailingStreak: container.State.Health.FailingStreak,
   149  			Log:           append([]*types.HealthcheckResult{}, container.State.Health.Log...),
   150  		}
   151  	}
   152  
   153  	containerState := &types.ContainerState{
   154  		Status:     container.State.StateString(),
   155  		Running:    container.State.Running,
   156  		Paused:     container.State.Paused,
   157  		Restarting: container.State.Restarting,
   158  		OOMKilled:  container.State.OOMKilled,
   159  		Dead:       container.State.Dead,
   160  		Pid:        container.State.Pid,
   161  		ExitCode:   container.State.ExitCode(),
   162  		Error:      container.State.ErrorMsg,
   163  		StartedAt:  container.State.StartedAt.Format(time.RFC3339Nano),
   164  		FinishedAt: container.State.FinishedAt.Format(time.RFC3339Nano),
   165  		Health:     containerHealth,
   166  	}
   167  
   168  	contJSONBase := &types.ContainerJSONBase{
   169  		ID:           container.ID,
   170  		Created:      container.Created.Format(time.RFC3339Nano),
   171  		Path:         container.Path,
   172  		Args:         container.Args,
   173  		State:        containerState,
   174  		Image:        container.ImageID.String(),
   175  		LogPath:      container.LogPath,
   176  		Name:         container.Name,
   177  		RestartCount: container.RestartCount,
   178  		Driver:       container.Driver,
   179  		Platform:     container.OS,
   180  		MountLabel:   container.MountLabel,
   181  		ProcessLabel: container.ProcessLabel,
   182  		ExecIDs:      container.GetExecIDs(),
   183  		HostConfig:   &hostConfig,
   184  	}
   185  
   186  	// Now set any platform-specific fields
   187  	contJSONBase = setPlatformSpecificContainerFields(container, contJSONBase)
   188  
   189  	contJSONBase.GraphDriver.Name = container.Driver
   190  
   191  	if daemon.UsesSnapshotter() {
   192  		// Additional information only applies to graphDrivers, so we're done.
   193  		return contJSONBase, nil
   194  	}
   195  
   196  	if container.RWLayer == nil {
   197  		if container.Dead {
   198  			return contJSONBase, nil
   199  		}
   200  		return nil, errdefs.System(errors.New("RWLayer of container " + container.ID + " is unexpectedly nil"))
   201  	}
   202  
   203  	graphDriverData, err := container.RWLayer.Metadata()
   204  	if err != nil {
   205  		if container.Dead {
   206  			// container is marked as Dead, and its graphDriver metadata may
   207  			// have been removed; we can ignore errors.
   208  			return contJSONBase, nil
   209  		}
   210  		return nil, errdefs.System(err)
   211  	}
   212  
   213  	contJSONBase.GraphDriver.Data = graphDriverData
   214  	return contJSONBase, nil
   215  }
   216  
   217  // ContainerExecInspect returns low-level information about the exec
   218  // command. An error is returned if the exec cannot be found.
   219  func (daemon *Daemon) ContainerExecInspect(id string) (*backend.ExecInspect, error) {
   220  	e := daemon.execCommands.Get(id)
   221  	if e == nil {
   222  		return nil, errExecNotFound(id)
   223  	}
   224  
   225  	if ctr := daemon.containers.Get(e.Container.ID); ctr == nil {
   226  		return nil, errExecNotFound(id)
   227  	}
   228  
   229  	e.Lock()
   230  	defer e.Unlock()
   231  	pc := inspectExecProcessConfig(e)
   232  	var pid int
   233  	if e.Process != nil {
   234  		pid = int(e.Process.Pid())
   235  	}
   236  
   237  	return &backend.ExecInspect{
   238  		ID:            e.ID,
   239  		Running:       e.Running,
   240  		ExitCode:      e.ExitCode,
   241  		ProcessConfig: pc,
   242  		OpenStdin:     e.OpenStdin,
   243  		OpenStdout:    e.OpenStdout,
   244  		OpenStderr:    e.OpenStderr,
   245  		CanRemove:     e.CanRemove,
   246  		ContainerID:   e.Container.ID,
   247  		DetachKeys:    e.DetachKeys,
   248  		Pid:           pid,
   249  	}, nil
   250  }
   251  
   252  // getDefaultNetworkSettings creates the deprecated structure that holds the information
   253  // about the bridge network for a container.
   254  func (daemon *Daemon) getDefaultNetworkSettings(networks map[string]*network.EndpointSettings) types.DefaultNetworkSettings {
   255  	var settings types.DefaultNetworkSettings
   256  
   257  	if defaultNetwork, ok := networks[networktypes.NetworkBridge]; ok && defaultNetwork.EndpointSettings != nil {
   258  		settings.EndpointID = defaultNetwork.EndpointID
   259  		settings.Gateway = defaultNetwork.Gateway
   260  		settings.GlobalIPv6Address = defaultNetwork.GlobalIPv6Address
   261  		settings.GlobalIPv6PrefixLen = defaultNetwork.GlobalIPv6PrefixLen
   262  		settings.IPAddress = defaultNetwork.IPAddress
   263  		settings.IPPrefixLen = defaultNetwork.IPPrefixLen
   264  		settings.IPv6Gateway = defaultNetwork.IPv6Gateway
   265  		settings.MacAddress = defaultNetwork.MacAddress
   266  	}
   267  	return settings
   268  }