github.com/gravitational/moby@v1.13.1/daemon/inspect.go (about)

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