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

     1  package daemon
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"runtime"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/pkg/errors"
    11  
    12  	"github.com/Sirupsen/logrus"
    13  	apierrors "github.com/docker/docker/api/errors"
    14  	"github.com/docker/docker/api/types"
    15  	containertypes "github.com/docker/docker/api/types/container"
    16  	networktypes "github.com/docker/docker/api/types/network"
    17  	"github.com/docker/docker/container"
    18  	"github.com/docker/docker/image"
    19  	"github.com/docker/docker/layer"
    20  	"github.com/docker/docker/pkg/idtools"
    21  	"github.com/docker/docker/pkg/stringid"
    22  	"github.com/docker/docker/runconfig"
    23  	volumestore "github.com/docker/docker/volume/store"
    24  	"github.com/opencontainers/runc/libcontainer/label"
    25  )
    26  
    27  // CreateManagedContainer creates a container that is managed by a Service
    28  func (daemon *Daemon) CreateManagedContainer(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) {
    29  	return daemon.containerCreate(params, true)
    30  }
    31  
    32  // ContainerCreate creates a regular container
    33  func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) {
    34  	return daemon.containerCreate(params, false)
    35  }
    36  
    37  func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, managed bool) (containertypes.ContainerCreateCreatedBody, error) {
    38  	start := time.Now()
    39  	if params.Config == nil {
    40  		return containertypes.ContainerCreateCreatedBody{}, fmt.Errorf("Config cannot be empty in order to create a container")
    41  	}
    42  
    43  	warnings, err := daemon.verifyContainerSettings(params.HostConfig, params.Config, false)
    44  	if err != nil {
    45  		return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, err
    46  	}
    47  
    48  	err = daemon.verifyNetworkingConfig(params.NetworkingConfig)
    49  	if err != nil {
    50  		return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, err
    51  	}
    52  
    53  	if params.HostConfig == nil {
    54  		params.HostConfig = &containertypes.HostConfig{}
    55  	}
    56  	err = daemon.adaptContainerSettings(params.HostConfig, params.AdjustCPUShares)
    57  	if err != nil {
    58  		return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, err
    59  	}
    60  
    61  	container, err := daemon.create(params, managed)
    62  	if err != nil {
    63  		return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, daemon.imageNotExistToErrcode(err)
    64  	}
    65  	containerActions.WithValues("create").UpdateSince(start)
    66  
    67  	return containertypes.ContainerCreateCreatedBody{ID: container.ID, Warnings: warnings}, nil
    68  }
    69  
    70  // Create creates a new container from the given configuration with a given name.
    71  func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (retC *container.Container, retErr error) {
    72  	var (
    73  		container *container.Container
    74  		img       *image.Image
    75  		imgID     image.ID
    76  		err       error
    77  	)
    78  
    79  	if params.Config.Image != "" {
    80  		img, err = daemon.GetImage(params.Config.Image)
    81  		if err != nil {
    82  			return nil, err
    83  		}
    84  
    85  		if runtime.GOOS == "solaris" && img.OS != "solaris " {
    86  			return nil, errors.New("Platform on which parent image was created is not Solaris")
    87  		}
    88  		imgID = img.ID()
    89  	}
    90  
    91  	if err := daemon.mergeAndVerifyConfig(params.Config, img); err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	if err := daemon.mergeAndVerifyLogConfig(&params.HostConfig.LogConfig); err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	if container, err = daemon.newContainer(params.Name, params.Config, params.HostConfig, imgID, managed); err != nil {
   100  		return nil, err
   101  	}
   102  	defer func() {
   103  		if retErr != nil {
   104  			if err := daemon.cleanupContainer(container, true, true); err != nil {
   105  				logrus.Errorf("failed to cleanup container on create error: %v", err)
   106  			}
   107  		}
   108  	}()
   109  
   110  	if err := daemon.setSecurityOptions(container, params.HostConfig); err != nil {
   111  		return nil, err
   112  	}
   113  
   114  	container.HostConfig.StorageOpt = params.HostConfig.StorageOpt
   115  
   116  	// Set RWLayer for container after mount labels have been set
   117  	if err := daemon.setRWLayer(container); err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	rootUID, rootGID, err := idtools.GetRootUIDGID(daemon.uidMaps, daemon.gidMaps)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  	if err := idtools.MkdirAs(container.Root, 0700, rootUID, rootGID); err != nil {
   126  		return nil, err
   127  	}
   128  	if err := idtools.MkdirAs(container.CheckpointDir(), 0700, rootUID, rootGID); err != nil {
   129  		return nil, err
   130  	}
   131  
   132  	if err := daemon.setHostConfig(container, params.HostConfig); err != nil {
   133  		return nil, err
   134  	}
   135  
   136  	if err := daemon.createContainerPlatformSpecificSettings(container, params.Config, params.HostConfig); err != nil {
   137  		return nil, err
   138  	}
   139  
   140  	var endpointsConfigs map[string]*networktypes.EndpointSettings
   141  	if params.NetworkingConfig != nil {
   142  		endpointsConfigs = params.NetworkingConfig.EndpointsConfig
   143  	}
   144  	// Make sure NetworkMode has an acceptable value. We do this to ensure
   145  	// backwards API compatibility.
   146  	container.HostConfig = runconfig.SetDefaultNetModeIfBlank(container.HostConfig)
   147  
   148  	daemon.updateContainerNetworkSettings(container, endpointsConfigs)
   149  
   150  	if err := container.ToDisk(); err != nil {
   151  		logrus.Errorf("Error saving new container to disk: %v", err)
   152  		return nil, err
   153  	}
   154  	if err := daemon.Register(container); err != nil {
   155  		return nil, err
   156  	}
   157  	daemon.LogContainerEvent(container, "create")
   158  	return container, nil
   159  }
   160  
   161  func (daemon *Daemon) generateSecurityOpt(ipcMode containertypes.IpcMode, pidMode containertypes.PidMode, privileged bool) ([]string, error) {
   162  	if ipcMode.IsHost() || pidMode.IsHost() || privileged {
   163  		return label.DisableSecOpt(), nil
   164  	}
   165  
   166  	var ipcLabel []string
   167  	var pidLabel []string
   168  	ipcContainer := ipcMode.Container()
   169  	pidContainer := pidMode.Container()
   170  	if ipcContainer != "" {
   171  		c, err := daemon.GetContainer(ipcContainer)
   172  		if err != nil {
   173  			return nil, err
   174  		}
   175  		ipcLabel = label.DupSecOpt(c.ProcessLabel)
   176  		if pidContainer == "" {
   177  			return ipcLabel, err
   178  		}
   179  	}
   180  	if pidContainer != "" {
   181  		c, err := daemon.GetContainer(pidContainer)
   182  		if err != nil {
   183  			return nil, err
   184  		}
   185  
   186  		pidLabel = label.DupSecOpt(c.ProcessLabel)
   187  		if ipcContainer == "" {
   188  			return pidLabel, err
   189  		}
   190  	}
   191  
   192  	if pidLabel != nil && ipcLabel != nil {
   193  		for i := 0; i < len(pidLabel); i++ {
   194  			if pidLabel[i] != ipcLabel[i] {
   195  				return nil, fmt.Errorf("--ipc and --pid containers SELinux labels aren't the same")
   196  			}
   197  		}
   198  		return pidLabel, nil
   199  	}
   200  	return nil, nil
   201  }
   202  
   203  func (daemon *Daemon) setRWLayer(container *container.Container) error {
   204  	var layerID layer.ChainID
   205  	if container.ImageID != "" {
   206  		img, err := daemon.imageStore.Get(container.ImageID)
   207  		if err != nil {
   208  			return err
   209  		}
   210  		layerID = img.RootFS.ChainID()
   211  	}
   212  
   213  	rwLayer, err := daemon.layerStore.CreateRWLayer(container.ID, layerID, container.MountLabel, daemon.getLayerInit(), container.HostConfig.StorageOpt)
   214  
   215  	if err != nil {
   216  		return err
   217  	}
   218  	container.RWLayer = rwLayer
   219  
   220  	return nil
   221  }
   222  
   223  // VolumeCreate creates a volume with the specified name, driver, and opts
   224  // This is called directly from the Engine API
   225  func (daemon *Daemon) VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error) {
   226  	if name == "" {
   227  		name = stringid.GenerateNonCryptoID()
   228  	}
   229  
   230  	v, err := daemon.volumes.Create(name, driverName, opts, labels)
   231  	if err != nil {
   232  		if volumestore.IsNameConflict(err) {
   233  			return nil, fmt.Errorf("A volume named %s already exists. Choose a different volume name.", name)
   234  		}
   235  		return nil, err
   236  	}
   237  
   238  	daemon.LogVolumeEvent(v.Name(), "create", map[string]string{"driver": v.DriverName()})
   239  	apiV := volumeToAPIType(v)
   240  	apiV.Mountpoint = v.Path()
   241  	return apiV, nil
   242  }
   243  
   244  func (daemon *Daemon) mergeAndVerifyConfig(config *containertypes.Config, img *image.Image) error {
   245  	if img != nil && img.Config != nil {
   246  		if err := merge(config, img.Config); err != nil {
   247  			return err
   248  		}
   249  	}
   250  	// Reset the Entrypoint if it is [""]
   251  	if len(config.Entrypoint) == 1 && config.Entrypoint[0] == "" {
   252  		config.Entrypoint = nil
   253  	}
   254  	if len(config.Entrypoint) == 0 && len(config.Cmd) == 0 {
   255  		return fmt.Errorf("No command specified")
   256  	}
   257  	return nil
   258  }
   259  
   260  // Checks if the client set configurations for more than one network while creating a container
   261  // Also checks if the IPAMConfig is valid
   262  func (daemon *Daemon) verifyNetworkingConfig(nwConfig *networktypes.NetworkingConfig) error {
   263  	if nwConfig == nil || len(nwConfig.EndpointsConfig) == 0 {
   264  		return nil
   265  	}
   266  	if len(nwConfig.EndpointsConfig) == 1 {
   267  		for _, v := range nwConfig.EndpointsConfig {
   268  			if v != nil && v.IPAMConfig != nil {
   269  				if v.IPAMConfig.IPv4Address != "" && net.ParseIP(v.IPAMConfig.IPv4Address).To4() == nil {
   270  					return apierrors.NewBadRequestError(fmt.Errorf("invalid IPv4 address: %s", v.IPAMConfig.IPv4Address))
   271  				}
   272  				if v.IPAMConfig.IPv6Address != "" {
   273  					n := net.ParseIP(v.IPAMConfig.IPv6Address)
   274  					// if the address is an invalid network address (ParseIP == nil) or if it is
   275  					// an IPv4 address (To4() != nil), then it is an invalid IPv6 address
   276  					if n == nil || n.To4() != nil {
   277  						return apierrors.NewBadRequestError(fmt.Errorf("invalid IPv6 address: %s", v.IPAMConfig.IPv6Address))
   278  					}
   279  				}
   280  			}
   281  		}
   282  		return nil
   283  	}
   284  	l := make([]string, 0, len(nwConfig.EndpointsConfig))
   285  	for k := range nwConfig.EndpointsConfig {
   286  		l = append(l, k)
   287  	}
   288  	err := fmt.Errorf("Container cannot be connected to network endpoints: %s", strings.Join(l, ", "))
   289  	return apierrors.NewBadRequestError(err)
   290  }