// ContainerCreate creates a container. func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (types.ContainerCreateResponse, error) { if params.Config == nil { return types.ContainerCreateResponse{}, fmt.Errorf("Config cannot be empty in order to create a container") } warnings, err := daemon.verifyContainerSettings(params.HostConfig, params.Config, false) if err != nil { return types.ContainerCreateResponse{Warnings: warnings}, err } err = daemon.verifyNetworkingConfig(params.NetworkingConfig) if err != nil { return types.ContainerCreateResponse{}, err } if params.HostConfig == nil { params.HostConfig = &containertypes.HostConfig{} } err = daemon.adaptContainerSettings(params.HostConfig, params.AdjustCPUShares) if err != nil { return types.ContainerCreateResponse{Warnings: warnings}, err } container, err := daemon.create(params) if err != nil { return types.ContainerCreateResponse{Warnings: warnings}, daemon.imageNotExistToErrcode(err) } return types.ContainerCreateResponse{ID: container.ID, Warnings: warnings}, nil }
// ContainerCreate creates a container. func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (types.ContainerCreateResponse, error) { if params.Config == nil { return types.ContainerCreateResponse{}, derr.ErrorCodeEmptyConfig } warnings, err := daemon.verifyContainerSettings(params.HostConfig, params.Config) if err != nil { return types.ContainerCreateResponse{Warnings: warnings}, err } if params.HostConfig == nil { params.HostConfig = &containertypes.HostConfig{} } err = daemon.adaptContainerSettings(params.HostConfig, params.AdjustCPUShares) if err != nil { return types.ContainerCreateResponse{Warnings: warnings}, err } container, err := daemon.create(params) if err != nil { return types.ContainerCreateResponse{Warnings: warnings}, daemon.imageNotExistToErrcode(err) } return types.ContainerCreateResponse{ID: container.ID, Warnings: warnings}, nil }
// TestContainerCreateEmptyImageCache() attempts a ContainerCreate() with an empty image // cache func TestContainerCreateEmptyImageCache(t *testing.T) { mockContainerProxy := NewMockContainerProxy() // Create our personality Container backend cb := &Container{ containerProxy: mockContainerProxy, } // mock a container create config var config types.ContainerCreateConfig config.HostConfig = &container.HostConfig{} config.Config = &container.Config{} config.NetworkingConfig = &dnetwork.NetworkingConfig{} config.Config.Image = "busybox" _, err := cb.ContainerCreate(config) assert.Contains(t, err.Error(), "No such image", "Error (%s) should have 'No such image' for an empty image cache", err.Error()) }
// TestContainerAddVolumes() assumes container handle create succeeded and cycles through all // possible input/outputs for committing the handle and calls vicbackends.ContainerCreate() func TestCommitHandle(t *testing.T) { mockContainerProxy := NewMockContainerProxy() // Create our personality Container backend cb := &Container{ containerProxy: mockContainerProxy, } AddMockImageToCache() // mock a container create config var config types.ContainerCreateConfig config.HostConfig = &container.HostConfig{} config.Config = &container.Config{} config.NetworkingConfig = &dnetwork.NetworkingConfig{} mockCommitHandleData := MockCommitData() // Iterate over create handler responses and see what the composite ContainerCreate() // returns. Since the handle is the first operation, we expect to receive a create handle // error. _, _, _, count := mockContainerProxy.GetMockDataCount() for i := 0; i < count; i++ { if i == SUCCESS { //skip success case continue } mockContainerProxy.SetMockDataResponse(0, 0, 0, i) config.Config.Image = mockCommitHandleData[i].createInputID _, err := cb.ContainerCreate(config) assert.Contains(t, err.Error(), mockCommitHandleData[i].createErrSubstr) } }
// CreateContainer creates a new container in the given PodSandbox // Note: docker doesn't use LogPath yet. // TODO: check if the default values returned by the runtime API are ok. func (ds *dockerService) CreateContainer(podSandboxID string, config *runtimeApi.ContainerConfig, sandboxConfig *runtimeApi.PodSandboxConfig) (string, error) { if config == nil { return "", fmt.Errorf("container config is nil") } if sandboxConfig == nil { return "", fmt.Errorf("sandbox config is nil for container %q", config.Metadata.GetName()) } labels := makeLabels(config.GetLabels(), config.GetAnnotations()) // Apply a the container type label. labels[containerTypeLabelKey] = containerTypeLabelContainer // Write the sandbox ID in the labels. labels[sandboxIDLabelKey] = podSandboxID image := "" if iSpec := config.GetImage(); iSpec != nil { image = iSpec.GetImage() } createConfig := dockertypes.ContainerCreateConfig{ Name: makeContainerName(sandboxConfig, config), Config: &dockercontainer.Config{ // TODO: set User. Entrypoint: dockerstrslice.StrSlice(config.GetCommand()), Cmd: dockerstrslice.StrSlice(config.GetArgs()), Env: generateEnvList(config.GetEnvs()), Image: image, WorkingDir: config.GetWorkingDir(), Labels: labels, // Interactive containers: OpenStdin: config.GetStdin(), StdinOnce: config.GetStdinOnce(), Tty: config.GetTty(), }, } // Fill the HostConfig. hc := &dockercontainer.HostConfig{ Binds: generateMountBindings(config.GetMounts()), ReadonlyRootfs: config.GetReadonlyRootfs(), Privileged: config.GetPrivileged(), } // Apply options derived from the sandbox config. if lc := sandboxConfig.GetLinux(); lc != nil { // Apply Cgroup options. // TODO: Check if this works with per-pod cgroups. hc.CgroupParent = lc.GetCgroupParent() // Apply namespace options. sandboxNSMode := fmt.Sprintf("container:%v", podSandboxID) hc.NetworkMode = dockercontainer.NetworkMode(sandboxNSMode) hc.IpcMode = dockercontainer.IpcMode(sandboxNSMode) hc.UTSMode = "" hc.PidMode = "" nsOpts := lc.GetNamespaceOptions() if nsOpts != nil { if nsOpts.GetHostNetwork() { hc.UTSMode = namespaceModeHost } if nsOpts.GetHostPid() { hc.PidMode = namespaceModeHost } } } // Apply Linux-specific options if applicable. if lc := config.GetLinux(); lc != nil { // Apply resource options. // TODO: Check if the units are correct. // TODO: Can we assume the defaults are sane? rOpts := lc.GetResources() if rOpts != nil { hc.Resources = dockercontainer.Resources{ Memory: rOpts.GetMemoryLimitInBytes(), MemorySwap: -1, // Always disable memory swap. CPUShares: rOpts.GetCpuShares(), CPUQuota: rOpts.GetCpuQuota(), CPUPeriod: rOpts.GetCpuPeriod(), // TODO: Need to set devices. } hc.OomScoreAdj = int(rOpts.GetOomScoreAdj()) } // Note: ShmSize is handled in kube_docker_client.go } var err error hc.SecurityOpt, err = getContainerSecurityOpts(config.Metadata.GetName(), sandboxConfig, ds.seccompProfileRoot) if err != nil { return "", fmt.Errorf("failed to generate container security options for container %q: %v", config.Metadata.GetName(), err) } // TODO: Add or drop capabilities. createConfig.HostConfig = hc createResp, err := ds.client.CreateContainer(createConfig) if createResp != nil { return createResp.ID, err } return "", err }
// CreateContainer creates a new container in the given PodSandbox // Note: docker doesn't use LogPath yet. // TODO: check if the default values returned by the runtime API are ok. func (ds *dockerService) CreateContainer(podSandboxID string, config *runtimeApi.ContainerConfig, sandboxConfig *runtimeApi.PodSandboxConfig) (string, error) { if config == nil { return "", fmt.Errorf("container config is nil") } if sandboxConfig == nil { return "", fmt.Errorf("sandbox config is nil for container %q", config.Metadata.GetName()) } labels := makeLabels(config.GetLabels(), config.GetAnnotations()) // Apply a the container type label. labels[containerTypeLabelKey] = containerTypeLabelContainer // Write the container log path in the labels. labels[containerLogPathLabelKey] = filepath.Join(sandboxConfig.GetLogDirectory(), config.GetLogPath()) // Write the sandbox ID in the labels. labels[sandboxIDLabelKey] = podSandboxID image := "" if iSpec := config.GetImage(); iSpec != nil { image = iSpec.GetImage() } createConfig := dockertypes.ContainerCreateConfig{ Name: makeContainerName(sandboxConfig, config), Config: &dockercontainer.Config{ // TODO: set User. Entrypoint: dockerstrslice.StrSlice(config.GetCommand()), Cmd: dockerstrslice.StrSlice(config.GetArgs()), Env: generateEnvList(config.GetEnvs()), Image: image, WorkingDir: config.GetWorkingDir(), Labels: labels, // Interactive containers: OpenStdin: config.GetStdin(), StdinOnce: config.GetStdinOnce(), Tty: config.GetTty(), }, } // Fill the HostConfig. hc := &dockercontainer.HostConfig{ Binds: generateMountBindings(config.GetMounts()), } // Apply cgroupsParent derived from the sandbox config. if lc := sandboxConfig.GetLinux(); lc != nil { // Apply Cgroup options. // TODO: Check if this works with per-pod cgroups. // TODO: we need to pass the cgroup in syntax expected by cgroup driver but shim does not use docker info yet... hc.CgroupParent = lc.GetCgroupParent() } // Apply Linux-specific options if applicable. if lc := config.GetLinux(); lc != nil { // Apply resource options. // TODO: Check if the units are correct. // TODO: Can we assume the defaults are sane? rOpts := lc.GetResources() if rOpts != nil { hc.Resources = dockercontainer.Resources{ Memory: rOpts.GetMemoryLimitInBytes(), MemorySwap: -1, // Always disable memory swap. CPUShares: rOpts.GetCpuShares(), CPUQuota: rOpts.GetCpuQuota(), CPUPeriod: rOpts.GetCpuPeriod(), } hc.OomScoreAdj = int(rOpts.GetOomScoreAdj()) } // Note: ShmSize is handled in kube_docker_client.go // Apply security context. applyContainerSecurityContext(lc, podSandboxID, createConfig.Config, hc) } // Set devices for container. devices := make([]dockercontainer.DeviceMapping, len(config.Devices)) for i, device := range config.Devices { devices[i] = dockercontainer.DeviceMapping{ PathOnHost: device.GetHostPath(), PathInContainer: device.GetContainerPath(), CgroupPermissions: device.GetPermissions(), } } hc.Resources.Devices = devices // Apply appArmor and seccomp options. securityOpts, err := getContainerSecurityOpts(config.Metadata.GetName(), sandboxConfig, ds.seccompProfileRoot) if err != nil { return "", fmt.Errorf("failed to generate container security options for container %q: %v", config.Metadata.GetName(), err) } hc.SecurityOpt = append(hc.SecurityOpt, securityOpts...) createConfig.HostConfig = hc createResp, err := ds.client.CreateContainer(createConfig) if createResp != nil { return createResp.ID, err } return "", err }
// CreateContainer creates a new container in the given PodSandbox // Note: docker doesn't use LogPath yet. // TODO: check if the default values returned by the runtime API are ok. func (ds *dockerService) CreateContainer(podSandboxID string, config *runtimeApi.ContainerConfig, sandboxConfig *runtimeApi.PodSandboxConfig) (string, error) { if config == nil { return "", fmt.Errorf("container config is nil") } if sandboxConfig == nil { return "", fmt.Errorf("sandbox config is nil for container %q", config.GetName()) } // Merge annotations and labels because docker supports only labels. // TODO: add a prefix to annotations so that we can distinguish labels and // annotations when reading back them from the docker container. // TODO: should we apply docker-specific labels? labels := config.GetLabels() for k, v := range config.GetAnnotations() { if _, ok := labels[k]; !ok { // Only write to labels if the key doesn't exist. labels[k] = v } } image := "" if iSpec := config.GetImage(); iSpec != nil { image = iSpec.GetImage() } createConfig := dockertypes.ContainerCreateConfig{ Name: config.GetName(), Config: &dockercontainer.Config{ // TODO: set User. Hostname: sandboxConfig.GetHostname(), Entrypoint: dockerstrslice.StrSlice(config.GetCommand()), Cmd: dockerstrslice.StrSlice(config.GetArgs()), Env: generateEnvList(config.GetEnvs()), Image: image, WorkingDir: config.GetWorkingDir(), Labels: labels, // Interactive containers: OpenStdin: config.GetStdin(), StdinOnce: config.GetStdinOnce(), Tty: config.GetTty(), }, } // Fill the HostConfig. hc := &dockercontainer.HostConfig{ Binds: generateMountBindings(config.GetMounts()), ReadonlyRootfs: config.GetReadonlyRootfs(), Privileged: config.GetPrivileged(), } // Apply options derived from the sandbox config. if lc := sandboxConfig.GetLinux(); lc != nil { // Apply Cgroup options. // TODO: Check if this works with per-pod cgroups. hc.CgroupParent = lc.GetCgroupParent() // Apply namespace options. sandboxNSMode := fmt.Sprintf("container:%v", podSandboxID) hc.NetworkMode = dockercontainer.NetworkMode(sandboxNSMode) hc.IpcMode = dockercontainer.IpcMode(sandboxNSMode) hc.UTSMode = "" hc.PidMode = "" nsOpts := lc.GetNamespaceOptions() if nsOpts != nil { if nsOpts.GetHostNetwork() { hc.UTSMode = namespaceModeHost } if nsOpts.GetHostPid() { hc.PidMode = namespaceModeHost } } } // Apply Linux-specific options if applicable. if lc := config.GetLinux(); lc != nil { // Apply resource options. // TODO: Check if the units are correct. // TODO: Can we assume the defaults are sane? rOpts := lc.GetResources() if rOpts != nil { hc.Resources = dockercontainer.Resources{ Memory: rOpts.GetMemoryLimitInBytes(), MemorySwap: -1, // Always disable memory swap. CPUShares: rOpts.GetCpuShares(), CPUQuota: rOpts.GetCpuQuota(), CPUPeriod: rOpts.GetCpuPeriod(), // TODO: Need to set devices. } hc.OomScoreAdj = int(rOpts.GetOomScoreAdj()) } // Note: ShmSize is handled in kube_docker_client.go } // TODO: Seccomp support. Need to figure out how to pass seccomp options // through the runtime API (annotations?).See dockerManager.getSecurityOpts() // for the details. Always set the default seccomp profile for now. hc.SecurityOpt = []string{fmt.Sprintf("%s=%s", "seccomp", defaultSeccompProfile)} // TODO: Add or drop capabilities. createConfig.HostConfig = hc createResp, err := ds.client.CreateContainer(createConfig) if createResp != nil { return createResp.ID, err } return "", err }
// validateCreateConfig() checks the parameters for ContainerCreate(). // It may "fix up" the config param passed into ConntainerCreate() if needed. func validateCreateConfig(config *types.ContainerCreateConfig) error { defer trace.End(trace.Begin("Container.validateCreateConfig")) // process cpucount here var cpuCount int64 = DefaultCPUs // support windows client if config.HostConfig.CPUCount > 0 { cpuCount = config.HostConfig.CPUCount } else { // we hijack --cpuset-cpus in the non-windows case if config.HostConfig.CpusetCpus != "" { cpus := strings.Split(config.HostConfig.CpusetCpus, ",") if c, err := strconv.Atoi(cpus[0]); err == nil { cpuCount = int64(c) } else { return fmt.Errorf("Error parsing CPU count: %s", err) } } } config.HostConfig.CPUCount = cpuCount // fix-up cpu/memory settings here if cpuCount < MinCPUs { config.HostConfig.CPUCount = MinCPUs } log.Infof("Container CPU count: %d", config.HostConfig.CPUCount) // convert from bytes to MiB for vsphere memoryMB := config.HostConfig.Memory / units.MiB if memoryMB == 0 { memoryMB = MemoryDefaultMB } else if memoryMB < MemoryMinMB { memoryMB = MemoryMinMB } // check that memory is aligned if remainder := memoryMB % MemoryAlignMB; remainder != 0 { log.Warnf("Default container VM memory must be %d aligned for hotadd, rounding up.", MemoryAlignMB) memoryMB += MemoryAlignMB - remainder } config.HostConfig.Memory = memoryMB log.Infof("Container memory: %d MB", config.HostConfig.Memory) if config.NetworkingConfig == nil { config.NetworkingConfig = &dnetwork.NetworkingConfig{} } if config.HostConfig == nil || config.Config == nil { return BadRequestError("invalid config") } // validate port bindings if config.HostConfig != nil { var ips []string if addrs, err := externalIPv4Addrs(); err != nil { log.Warnf("could not get address for external interface: %s", err) } else { ips = make([]string, len(addrs)) for i := range addrs { ips[i] = addrs[i].IP.String() } } for _, pbs := range config.HostConfig.PortBindings { for _, pb := range pbs { if pb.HostIP != "" && pb.HostIP != "0.0.0.0" { // check if specified host ip equals any of the addresses on the "client" interface found := false for _, i := range ips { if i == pb.HostIP { found = true break } } if !found { return InternalServerError("host IP for port bindings is only supported for 0.0.0.0 and the external interface IP address") } } start, end, _ := nat.ParsePortRangeToInt(pb.HostPort) if start != end { return InternalServerError("host port ranges are not supported for port bindings") } } } } // TODO(jzt): users other than root are not currently supported // We should check for USER in config.Config.Env once we support Dockerfiles. if config.Config.User != "" && config.Config.User != "root" { return InternalServerError("Failed to create container - users other than root are not currently supported") } // https://github.com/vmware/vic/issues/1378 if len(config.Config.Entrypoint) == 0 && len(config.Config.Cmd) == 0 { return derr.NewRequestNotFoundError(fmt.Errorf("No command specified")) } // Was a name provided - if not create a friendly name if config.Name == "" { //TODO: Assume we could have a name collison here : need to // provide validation / retry CDG June 9th 2016 config.Name = namesgenerator.GetRandomName(0) } return nil }