github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/daemon/inspect.go (about)

     1  package daemon
     2  
     3  import (
     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  	volumestore "github.com/docker/docker/volume/store"
    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.
    22  func (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.
    34  func (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.getSize(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.
    95  func (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  
   127  func (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, systemError{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, systemError{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.
   211  func (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  
   238  // VolumeInspect looks up a volume by name. An error is returned if
   239  // the volume cannot be found.
   240  func (daemon *Daemon) VolumeInspect(name string) (*types.Volume, error) {
   241  	v, err := daemon.volumes.Get(name)
   242  	if err != nil {
   243  		if volumestore.IsNotExist(err) {
   244  			return nil, volumeNotFound(name)
   245  		}
   246  		return nil, systemError{err}
   247  	}
   248  	apiV := volumeToAPIType(v)
   249  	apiV.Mountpoint = v.Path()
   250  	apiV.Status = v.Status()
   251  	return apiV, nil
   252  }
   253  
   254  func (daemon *Daemon) getBackwardsCompatibleNetworkSettings(settings *network.Settings) *v1p20.NetworkSettings {
   255  	result := &v1p20.NetworkSettings{
   256  		NetworkSettingsBase: types.NetworkSettingsBase{
   257  			Bridge:                 settings.Bridge,
   258  			SandboxID:              settings.SandboxID,
   259  			HairpinMode:            settings.HairpinMode,
   260  			LinkLocalIPv6Address:   settings.LinkLocalIPv6Address,
   261  			LinkLocalIPv6PrefixLen: settings.LinkLocalIPv6PrefixLen,
   262  			Ports:                  settings.Ports,
   263  			SandboxKey:             settings.SandboxKey,
   264  			SecondaryIPAddresses:   settings.SecondaryIPAddresses,
   265  			SecondaryIPv6Addresses: settings.SecondaryIPv6Addresses,
   266  		},
   267  		DefaultNetworkSettings: daemon.getDefaultNetworkSettings(settings.Networks),
   268  	}
   269  
   270  	return result
   271  }
   272  
   273  // getDefaultNetworkSettings creates the deprecated structure that holds the information
   274  // about the bridge network for a container.
   275  func (daemon *Daemon) getDefaultNetworkSettings(networks map[string]*network.EndpointSettings) types.DefaultNetworkSettings {
   276  	var settings types.DefaultNetworkSettings
   277  
   278  	if defaultNetwork, ok := networks["bridge"]; ok && defaultNetwork.EndpointSettings != nil {
   279  		settings.EndpointID = defaultNetwork.EndpointID
   280  		settings.Gateway = defaultNetwork.Gateway
   281  		settings.GlobalIPv6Address = defaultNetwork.GlobalIPv6Address
   282  		settings.GlobalIPv6PrefixLen = defaultNetwork.GlobalIPv6PrefixLen
   283  		settings.IPAddress = defaultNetwork.IPAddress
   284  		settings.IPPrefixLen = defaultNetwork.IPPrefixLen
   285  		settings.IPv6Gateway = defaultNetwork.IPv6Gateway
   286  		settings.MacAddress = defaultNetwork.MacAddress
   287  	}
   288  	return settings
   289  }