api/server/router/container/container_routes.go#containerRouter.postContainersCreate
func (s *containerRouter) postContainersCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if err := httputils.ParseForm(r); err != nil { return err } if err := httputils.CheckForJSON(r); err != nil { return err } name := r.Form.Get("name") config, hostConfig, networkingConfig, err := s.decoder.DecodeConfig(r.Body) if err != nil { return err } version := httputils.VersionFromContext(ctx) adjustCPUShares := versions.LessThan(version, "1.19") // When using API 1.24 and under, the client is responsible for removing the container if hostConfig != nil && versions.LessThan(version, "1.25") { hostConfig.AutoRemove = false } if hostConfig != nil && versions.LessThan(version, "1.40") { // Ignore BindOptions.NonRecursive because it was added in API 1.40. for _, m := range hostConfig.Mounts { if bo := m.BindOptions; bo != nil { bo.NonRecursive = false } } // Ignore KernelMemoryTCP because it was added in API 1.40. hostConfig.KernelMemoryTCP = 0 } // Ignore Capabilities because it was added in API 1.40. if hostConfig != nil && versions.LessThan(version, "1.40") { hostConfig.Capabilities = nil } // ********************************** NOTICE ********************************** // ccr, err := s.backend.ContainerCreate(types.ContainerCreateConfig{ Name: name, Config: config, HostConfig: hostConfig, NetworkingConfig: networkingConfig, AdjustCPUShares: adjustCPUShares, }) // ********************************** NOTICE ********************************** // if err != nil { return err } return httputils.WriteJSON(w, http.StatusCreated, ccr)}
1) daemon/create.go#Daemon.ContainerCreate
// ContainerCreate creates a regular containerfunc (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) { return daemon.containerCreate(params, false)}func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, managed bool) (containertypes.ContainerCreateCreatedBody, error) { start := time.Now() if params.Config == nil { return containertypes.ContainerCreateCreatedBody{}, errdefs.InvalidParameter(errors.New("Config cannot be empty in order to create a container")) } os := runtime.GOOS if params.Config.Image != "" { // ********************************** NOTICE ********************************** // img, err := daemon.imageService.GetImage(params.Config.Image) // ********************************** NOTICE ********************************** // if err == nil { os = img.OS } } else { // This mean scratch. On Windows, we can safely assume that this is a linux // container. On other platforms, it's the host OS (which it already is) if runtime.GOOS == "windows" && system.LCOWSupported() { os = "linux" } } warnings, err := daemon.verifyContainerSettings(os, params.HostConfig, params.Config, false) if err != nil { return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, errdefs.InvalidParameter(err) } err = verifyNetworkingConfig(params.NetworkingConfig) if err != nil { return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, errdefs.InvalidParameter(err) } if params.HostConfig == nil { params.HostConfig = &containertypes.HostConfig{} } err = daemon.adaptContainerSettings(params.HostConfig, params.AdjustCPUShares) if err != nil { return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, errdefs.InvalidParameter(err) } // ********************************** NOTICE ********************************** // container, err := daemon.create(params, managed) // ********************************** NOTICE ********************************** // if err != nil { return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, err } containerActions.WithValues("create").UpdateSince(start) return containertypes.ContainerCreateCreatedBody{ID: container.ID, Warnings: warnings}, nil}
1.1) daemon/images/image.go#ImageService.GetImage
// GetImage returns an image corresponding to the image referred to by refOrID.func (i *ImageService) GetImage(refOrID string) (*image.Image, error) { ref, err := reference.ParseAnyReference(refOrID) if err != nil { return nil, errdefs.InvalidParameter(err) } namedRef, ok := ref.(reference.Named) if !ok { digested, ok := ref.(reference.Digested) if !ok { return nil, ErrImageDoesNotExist{ref} } id := image.IDFromDigest(digested.Digest()) if img, err := i.imageStore.Get(id); err == nil { return img, nil } return nil, ErrImageDoesNotExist{ref} } if digest, err := i.referenceStore.Get(namedRef); err == nil { // Search the image stores to get the operating system, defaulting to host OS. id := image.IDFromDigest(digest) if img, err := i.imageStore.Get(id); err == nil { return img, nil } } // Search based on ID if id, err := i.imageStore.Search(refOrID); err == nil { img, err := i.imageStore.Get(id) if err != nil { return nil, ErrImageDoesNotExist{ref} } return img, nil } return nil, ErrImageDoesNotExist{ref}}
1.2) daemon/create.go#Daemon.create
// Create creates a new container from the given configuration with a given name.func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (retC *container.Container, retErr error) { var ( container *container.Container img *image.Image imgID image.ID err error ) os := runtime.GOOS if params.Config.Image != "" { img, err = daemon.imageService.GetImage(params.Config.Image) if err != nil { return nil, err } if img.OS != "" { os = img.OS } else { // default to the host OS except on Windows with LCOW if runtime.GOOS == "windows" && system.LCOWSupported() { os = "linux" } } imgID = img.ID() if runtime.GOOS == "windows" && img.OS == "linux" && !system.LCOWSupported() { return nil, errors.New("operating system on which parent image was created is not Windows") } } else { if runtime.GOOS == "windows" { os = "linux" // 'scratch' case. } } if err := daemon.mergeAndVerifyConfig(params.Config, img); err != nil { return nil, errdefs.InvalidParameter(err) } if err := daemon.mergeAndVerifyLogConfig(¶ms.HostConfig.LogConfig); err != nil { return nil, errdefs.InvalidParameter(err) } // ********************************** NOTICE ********************************** // if container, err = daemon.newContainer(params.Name, os, params.Config, params.HostConfig, imgID, managed); err != nil { // ********************************** NOTICE ********************************** // return nil, err } defer func() { if retErr != nil { if err := daemon.cleanupContainer(container, true, true); err != nil { logrus.Errorf("failed to cleanup container on create error: %v", err) } } }() if err := daemon.setSecurityOptions(container, params.HostConfig); err != nil { return nil, err } container.HostConfig.StorageOpt = params.HostConfig.StorageOpt // Fixes: https://github.com/moby/moby/issues/34074 and // https://github.com/docker/for-win/issues/999. // Merge the daemon's storage options if they aren't already present. We only // do this on Windows as there's no effective sandbox size limit other than // physical on Linux. if runtime.GOOS == "windows" { if container.HostConfig.StorageOpt == nil { container.HostConfig.StorageOpt = make(map[string]string) } for _, v := range daemon.configStore.GraphOptions { opt := strings.SplitN(v, "=", 2) if _, ok := container.HostConfig.StorageOpt[opt[0]]; !ok { container.HostConfig.StorageOpt[opt[0]] = opt[1] } } } // Set RWLayer for container after mount labels have been set rwLayer, err := daemon.imageService.CreateLayer(container, setupInitLayer(daemon.idMapping)) if err != nil { return nil, errdefs.System(err) } container.RWLayer = rwLayer rootIDs := daemon.idMapping.RootPair() if err := idtools.MkdirAndChown(container.Root, 0700, rootIDs); err != nil { return nil, err } if err := idtools.MkdirAndChown(container.CheckpointDir(), 0700, rootIDs); err != nil { return nil, err } if err := daemon.setHostConfig(container, params.HostConfig); err != nil { return nil, err } if err := daemon.createContainerOSSpecificSettings(container, params.Config, params.HostConfig); err != nil { return nil, err } var endpointsConfigs map[string]*networktypes.EndpointSettings if params.NetworkingConfig != nil { endpointsConfigs = params.NetworkingConfig.EndpointsConfig } // Make sure NetworkMode has an acceptable value. We do this to ensure // backwards API compatibility. runconfig.SetDefaultNetModeIfBlank(container.HostConfig) daemon.updateContainerNetworkSettings(container, endpointsConfigs) // ********************************** NOTICE ********************************** // if err := daemon.Register(container); err != nil { // ********************************** NOTICE ********************************** // return nil, err } stateCtr.set(container.ID, "stopped") daemon.LogContainerEvent(container, "create") return container, nil}
1.2.1) daemon/container.go#Daemon.newContainer
func (daemon *Daemon) newContainer(name string, operatingSystem string, config *containertypes.Config, hostConfig *containertypes.HostConfig, imgID image.ID, managed bool) (*container.Container, error) { var ( id string err error noExplicitName = name == "" ) id, name, err = daemon.generateIDAndName(name) if err != nil { return nil, err } if hostConfig.NetworkMode.IsHost() { if config.Hostname == "" { config.Hostname, err = os.Hostname() if err != nil { return nil, errdefs.System(err) } } } else { daemon.generateHostname(id, config) } entrypoint, args := daemon.getEntrypointAndArgs(config.Entrypoint, config.Cmd) // ********************************** NOTICE ********************************** // base := daemon.newBaseContainer(id) // ********************************** NOTICE ********************************** // base.Created = time.Now().UTC() base.Managed = managed base.Path = entrypoint base.Args = args //FIXME: de-duplicate from config base.Config = config base.HostConfig = &containertypes.HostConfig{} base.ImageID = imgID base.NetworkSettings = &network.Settings{IsAnonymousEndpoint: noExplicitName} base.Name = name base.Driver = daemon.imageService.GraphDriverForOS(operatingSystem) base.OS = operatingSystem return base, err}// newBaseContainer creates a new container with its initial// configuration based on the root storage from the daemon.func (daemon *Daemon) newBaseContainer(id string) *container.Container { // ********************************** NOTICE ********************************** // return container.NewBaseContainer(id, daemon.containerRoot(id)) // ********************************** NOTICE ********************************** //}// NewBaseContainer creates a new container with its// basic configuration.func NewBaseContainer(id, root string) *Container { return &Container{ ID: id, State: NewState(), ExecCommands: exec.NewStore(), Root: root, MountPoints: make(map[string]*volumemounts.MountPoint), StreamConfig: stream.NewConfig(), attachContext: &attachContext{}, }}
1.2.2) daemon/container.go#Daemon.Register
// Register makes a container object usable by the daemon as <container.ID>func (daemon *Daemon) Register(c *container.Container) error { // Attach to stdout and stderr if c.Config.OpenStdin { c.StreamConfig.NewInputPipes() } else { c.StreamConfig.NewNopInputPipe() } // once in the memory store it is visible to other goroutines // grab a Lock until it has been checkpointed to avoid races c.Lock() defer c.Unlock() // ********************************** NOTICE ********************************** // daemon.containers.Add(c.ID, c) daemon.idIndex.Add(c.ID) // ********************************** NOTICE ********************************** // return c.CheckpointTo(daemon.containersReplica)}
1.2.2.1) container/memory_store.go#memoryStore.Add
// Store defines an interface that// any container store must implement.type Store interface { // Add appends a new container to the store. Add(string, *Container) // Get returns a container from the store by the identifier it was stored with. Get(string) *Container // Delete removes a container from the store by the identifier it was stored with. Delete(string) // List returns a list of containers from the store. List() []*Container // Size returns the number of containers in the store. Size() int // First returns the first container found in the store by a given filter. First(StoreFilter) *Container // ApplyAll calls the reducer function with every container in the store. ApplyAll(StoreReducer)}
// memoryStore implements a Store in memory.type memoryStore struct { s map[string]*Container sync.RWMutex}// Add appends a new container to the memory store.// It overrides the id if it existed before.func (c *memoryStore) Add(id string, cont *Container) { c.Lock() c.s[id] = cont c.Unlock()}