func CreateContainer(containerName, image, domain string, portsToMap []Port, environmentVars map[string]string) (*docker.Container, error) { cfg := &docker.Config{} cfg.Image = image cfg.Hostname = containerName cfg.Domainname = domain var e struct{} exposedPorts := make(map[docker.Port]struct{}) for _, p := range portsToMap { prt := strconv.FormatUint(p.Private, 10) + "/" + p.Type exposedPorts[docker.Port(prt)] = e fmt.Printf("Exposing %s\n", prt) } cfg.ExposedPorts = exposedPorts envStrs := make([]string, 0, 10) for k, v := range environmentVars { envStrs = append(envStrs, k+"="+v) } cfg.Env = envStrs hostCfg := &docker.HostConfig{} hostCfg.PublishAllPorts = false hostCfg.Privileged = false hostPorts := make(map[docker.Port][]docker.PortBinding) for _, p := range portsToMap { prt := strconv.FormatUint(p.Private, 10) + "/" + p.Type bindings := make([]docker.PortBinding, 0, 4) bindings = append(bindings, docker.PortBinding{HostIP: "", HostPort: strconv.FormatUint(p.Public, 10)}) fmt.Printf("Binding %s to %s\n", prt, bindings[0]) hostPorts[docker.Port(prt)] = bindings } hostCfg.PortBindings = hostPorts opts := docker.CreateContainerOptions{} opts.Config = cfg opts.Name = containerName opts.HostConfig = hostCfg json, _ := data.PrettyPrint(opts) fmt.Printf("create options: %s\n", string(json)) container, err := client.CreateContainer(opts) if err != nil { fmt.Fprintf(os.Stderr, "Error creating container: %s\n", err.Error()) } else { fmt.Printf("Container created: %s\n", container.ID) } return container, err }
// DockerRun perform a docker run func DockerRun(req *DockerRunRequest) (DockerRunResponse, error) { response := DockerRunResponse{} //logit.Info.Println("DockerRun called") swarmURL := os.Getenv("SWARM_MANAGER_URL") if swarmURL == "" { logit.Error.Println("SWARM_MANAGER_URL not set") return response, errors.New("SWARM_MANAGER_URL not set") } var envvars []string var i = 0 if req.EnvVars != nil { envvars = make([]string, len(req.EnvVars)+1) for k, v := range req.EnvVars { envvars[i] = k + "=" + v i++ } } else { envvars = make([]string, 1) } if req.Profile == "" { return response, errors.New("Profile was empty and should not be") } //typical case is to always add the profile constraint env var //like SM, MED, LG, however in the case of a restore job, we //use a hard constraint of the host ipaddress to pin //the restored container to the same host as where the backup //is stored if req.IPAddress != "" { envvars[i] = "constraint:host==~" + req.IPAddress } else { envvars[i] = "constraint:profile==~" + req.Profile } docker, err := dockerapi.NewClient(swarmURL) if err != nil { logit.Error.Println(err.Error()) return response, err } options := dockerapi.CreateContainerOptions{} config := dockerapi.Config{} config.Hostname = req.ContainerName options.Config = &config hostConfig := dockerapi.HostConfig{} options.HostConfig = &hostConfig options.Name = req.ContainerName options.Config.Env = envvars options.Config.Image = "crunchydata/" + req.Image //logit.Info.Println("swarmapi using " + options.Config.Image + " as the image name") options.Config.Volumes = make(map[string]struct{}) //TODO figure out cpu shares and memory settings, these are different //than what I was using before due to me using the docker api directly //with this swarm implementation...use the defaults for now //options.HostConfig.CPUShares, err = strconv.ParseInt(req.CPU, 0, 64) //if err != nil { //logit.Error.Println(err.Error()) //return response, err //} //options.HostConfig.Memory = req.MEM options.HostConfig.Binds = make([]string, 3) options.HostConfig.Binds[0] = req.PGDataPath + ":/pgdata" options.HostConfig.Binds[1] = "/var/cpm/data/keys:/keys" options.HostConfig.Binds[2] = "/var/cpm/config:/syslogconfig" container, err3 := docker.CreateContainer(options) if err3 != nil { logit.Error.Println(err3.Error()) return response, err3 } var startResponse DockerStartResponse startRequest := DockerStartRequest{} startRequest.ContainerName = req.ContainerName startResponse, err = DockerStart(&startRequest) if err != nil { logit.Error.Println(err.Error()) return response, err } logit.Info.Println(startResponse.Output) //cmd := exec.Command(req.CommandPath, req.PGDataPath, req.ContainerName, //req.Image, req.CPU, req.MEM, allEnvVars) response.ID = container.ID return response, nil }
// RunContainer creates and starts a container using the image specified in the options with the ability // to stream input or output func (d *stiDocker) RunContainer(opts RunContainerOptions) (err error) { // get info about the specified image image := getImageName(opts.Image) var imageMetadata *docker.Image if opts.PullImage { imageMetadata, err = d.CheckAndPullImage(image) } else { imageMetadata, err = d.client.InspectImage(image) } if err != nil { glog.Errorf("Unable to get image metadata for %s: %v", image, err) return err } config := docker.Config{ Image: image, User: opts.User, } config, tarDestination := runContainerTar(opts, config, imageMetadata) if opts.Env != nil { config.Env = opts.Env } if opts.Stdin != nil { config.OpenStdin = true config.StdinOnce = true } if opts.Stdout != nil { config.AttachStdout = true } glog.V(2).Infof("Creating container using config: %+v", config) ccopts := docker.CreateContainerOptions{Name: "", Config: &config} if opts.TargetImage { ccopts.HostConfig = &docker.HostConfig{PublishAllPorts: true, NetworkMode: opts.NetworkMode} } else if opts.NetworkMode != "" { ccopts.HostConfig = &docker.HostConfig{NetworkMode: opts.NetworkMode} } container, err := d.client.CreateContainer(ccopts) if err != nil { return err } defer d.RemoveContainer(container.ID) glog.V(2).Infof("Attaching to container") // creating / piping the channels in runContainerAttach lead to unintended hangs attached := make(chan struct{}) wg := runContainerAttach(attached, container, opts, d) attached <- <-attached glog.V(2).Infof("Starting container") if err = d.client.StartContainer(container.ID, nil); err != nil { return err } if opts.OnStart != nil { if err = opts.OnStart(); err != nil { return err } } if opts.TargetImage { runContainerDockerRun(container, d, image) } else { werr := runContainerWait(wg, d, container) if werr != nil { return werr } } if opts.PostExec != nil { glog.V(2).Infof("Invoking postExecution function") if err = opts.PostExec.PostExecute(container.ID, tarDestination); err != nil { return err } } return nil }
func resourceDockerContainerCreate(d *schema.ResourceData, meta interface{}) error { var err error client := meta.(*dc.Client) var data Data if err := fetchLocalImages(&data, client); err != nil { return err } image := d.Get("image").(string) if _, ok := data.DockerImages[image]; !ok { if _, ok := data.DockerImages[image+":latest"]; !ok { return fmt.Errorf("Unable to find image %s", image) } image = image + ":latest" } // The awesome, wonderful, splendiferous, sensical // Docker API now lets you specify a HostConfig in // CreateContainerOptions, but in my testing it still only // actually applies HostConfig options set in StartContainer. // How cool is that? createOpts := dc.CreateContainerOptions{ Name: d.Get("name").(string), Config: &dc.Config{ Image: image, Hostname: d.Get("hostname").(string), Domainname: d.Get("domainname").(string), }, } if v, ok := d.GetOk("env"); ok { createOpts.Config.Env = stringSetToStringSlice(v.(*schema.Set)) } if v, ok := d.GetOk("command"); ok { createOpts.Config.Cmd = stringListToStringSlice(v.([]interface{})) } if v, ok := d.GetOk("entrypoint"); ok { createOpts.Config.Entrypoint = stringListToStringSlice(v.([]interface{})) } exposedPorts := map[dc.Port]struct{}{} portBindings := map[dc.Port][]dc.PortBinding{} if v, ok := d.GetOk("ports"); ok { exposedPorts, portBindings = portSetToDockerPorts(v.(*schema.Set)) } if len(exposedPorts) != 0 { createOpts.Config.ExposedPorts = exposedPorts } extraHosts := []string{} if v, ok := d.GetOk("host"); ok { extraHosts = extraHostsSetToDockerExtraHosts(v.(*schema.Set)) } volumes := map[string]struct{}{} binds := []string{} volumesFrom := []string{} if v, ok := d.GetOk("volumes"); ok { volumes, binds, volumesFrom, err = volumeSetToDockerVolumes(v.(*schema.Set)) if err != nil { return fmt.Errorf("Unable to parse volumes: %s", err) } } if len(volumes) != 0 { createOpts.Config.Volumes = volumes } if v, ok := d.GetOk("labels"); ok { createOpts.Config.Labels = mapTypeMapValsToString(v.(map[string]interface{})) } hostConfig := &dc.HostConfig{ Privileged: d.Get("privileged").(bool), PublishAllPorts: d.Get("publish_all_ports").(bool), RestartPolicy: dc.RestartPolicy{ Name: d.Get("restart").(string), MaximumRetryCount: d.Get("max_retry_count").(int), }, LogConfig: dc.LogConfig{ Type: d.Get("log_driver").(string), }, } if len(portBindings) != 0 { hostConfig.PortBindings = portBindings } if len(extraHosts) != 0 { hostConfig.ExtraHosts = extraHosts } if len(binds) != 0 { hostConfig.Binds = binds } if len(volumesFrom) != 0 { hostConfig.VolumesFrom = volumesFrom } if v, ok := d.GetOk("dns"); ok { hostConfig.DNS = stringSetToStringSlice(v.(*schema.Set)) } if v, ok := d.GetOk("links"); ok { hostConfig.Links = stringSetToStringSlice(v.(*schema.Set)) } if v, ok := d.GetOk("memory"); ok { hostConfig.Memory = int64(v.(int)) * 1024 * 1024 } if v, ok := d.GetOk("memory_swap"); ok { swap := int64(v.(int)) if swap > 0 { swap = swap * 1024 * 1024 } hostConfig.MemorySwap = swap } if v, ok := d.GetOk("cpu_shares"); ok { hostConfig.CPUShares = int64(v.(int)) } if v, ok := d.GetOk("log_opts"); ok { hostConfig.LogConfig.Config = mapTypeMapValsToString(v.(map[string]interface{})) } if v, ok := d.GetOk("network_mode"); ok { hostConfig.NetworkMode = v.(string) } createOpts.HostConfig = hostConfig var retContainer *dc.Container if retContainer, err = client.CreateContainer(createOpts); err != nil { return fmt.Errorf("Unable to create container: %s", err) } if retContainer == nil { return fmt.Errorf("Returned container is nil") } d.SetId(retContainer.ID) if v, ok := d.GetOk("networks"); ok { connectionOpts := dc.NetworkConnectionOptions{Container: retContainer.ID} for _, network := range v.(*schema.Set).List() { client.ConnectNetwork(network.(string), connectionOpts) } } creationTime = time.Now() if err := client.StartContainer(retContainer.ID, hostConfig); err != nil { return fmt.Errorf("Unable to start container: %s", err) } return resourceDockerContainerRead(d, meta) }
// Build is a helper method to perform a Docker build against the // provided Docker client. It will load the image if not specified, // create a container if one does not already exist, and start a // container if the Dockerfile contains RUN commands. It will cleanup // any containers it creates directly, and set the e.Image.ID field // to the generated image. func (e *ClientExecutor) Build(r io.Reader, args map[string]string) error { b := NewBuilder() b.Args = args if e.Excludes == nil { excludes, err := ParseDockerignore(e.Directory) if err != nil { return err } e.Excludes = append(excludes, ".dockerignore") } // TODO: check the Docker daemon version (1.20 is required for Upload) node, err := parser.Parse(r) if err != nil { return err } // identify the base image from, err := b.From(node) if err != nil { return err } // load the image if e.Image == nil { if from == NoBaseImageSpecifier { if runtime.GOOS == "windows" { return fmt.Errorf("building from scratch images is not supported") } from, err = e.CreateScratchImage() if err != nil { return fmt.Errorf("unable to create a scratch image for this build: %v", err) } defer e.CleanupImage(from) } glog.V(4).Infof("Retrieving image %q", from) e.Image, err = e.LoadImage(from) if err != nil { return err } } // update the builder with any information from the image, including ONBUILD // statements if err := b.FromImage(e.Image, node); err != nil { return err } b.RunConfig.Image = from e.LogFn("FROM %s", from) glog.V(4).Infof("step: FROM %s", from) var sharedMount string // create a container to execute in, if necessary mustStart := b.RequiresStart(node) if e.Container == nil { opts := docker.CreateContainerOptions{ Config: &docker.Config{ Image: from, }, } if mustStart { // Transient mounts only make sense on images that will be running processes if len(e.TransientMounts) > 0 { volumeName, err := randSeq(imageSafeCharacters, 24) if err != nil { return err } v, err := e.Client.CreateVolume(docker.CreateVolumeOptions{Name: volumeName}) if err != nil { return fmt.Errorf("unable to create volume to mount secrets: %v", err) } defer e.cleanupVolume(volumeName) sharedMount = v.Mountpoint opts.HostConfig = &docker.HostConfig{ Binds: []string{sharedMount + ":/tmp/__temporarymount"}, } } // TODO: windows support if len(e.Command) > 0 { opts.Config.Cmd = e.Command opts.Config.Entrypoint = nil } else { // TODO; replace me with a better default command opts.Config.Cmd = []string{"sleep 86400"} opts.Config.Entrypoint = []string{"/bin/sh", "-c"} } } if len(opts.Config.Cmd) == 0 { opts.Config.Entrypoint = []string{"/bin/sh", "-c", "# NOP"} } container, err := e.Client.CreateContainer(opts) if err != nil { return fmt.Errorf("unable to create build container: %v", err) } e.Container = container // if we create the container, take responsibilty for cleaning up defer e.Cleanup() } // copy any source content into the temporary mount path if mustStart && len(e.TransientMounts) > 0 { var copies []Copy for i, mount := range e.TransientMounts { source := mount.SourcePath copies = append(copies, Copy{ Src: source, Dest: []string{path.Join("/tmp/__temporarymount", strconv.Itoa(i))}, }) } if err := e.Copy(copies...); err != nil { return fmt.Errorf("unable to copy build context into container: %v", err) } } // TODO: lazy start if mustStart && !e.Container.State.Running { var hostConfig docker.HostConfig if e.HostConfig != nil { hostConfig = *e.HostConfig } // mount individual items temporarily for i, mount := range e.TransientMounts { if len(sharedMount) == 0 { return fmt.Errorf("no mount point available for temporary mounts") } hostConfig.Binds = append( hostConfig.Binds, fmt.Sprintf("%s:%s:%s", path.Join(sharedMount, strconv.Itoa(i)), mount.DestinationPath, "ro"), ) } if err := e.Client.StartContainer(e.Container.ID, &hostConfig); err != nil { return fmt.Errorf("unable to start build container: %v", err) } // TODO: is this racy? may have to loop wait in the actual run step } for _, child := range node.Children { step := b.Step() if err := step.Resolve(child); err != nil { return err } glog.V(4).Infof("step: %s", step.Original) if e.LogFn != nil { e.LogFn(step.Original) } if err := b.Run(step, e); err != nil { return err } } if mustStart { glog.V(4).Infof("Stopping container %s ...", e.Container.ID) if err := e.Client.StopContainer(e.Container.ID, 0); err != nil { return fmt.Errorf("unable to stop build container: %v", err) } } config := b.Config() var repository, tag string if len(e.Tag) > 0 { repository, tag = docker.ParseRepositoryTag(e.Tag) glog.V(4).Infof("Committing built container %s as image %q: %#v", e.Container.ID, e.Tag, config) if e.LogFn != nil { e.LogFn("Committing changes to %s ...", e.Tag) } } else { glog.V(4).Infof("Committing built container %s: %#v", e.Container.ID, config) if e.LogFn != nil { e.LogFn("Committing changes ...") } } image, err := e.Client.CommitContainer(docker.CommitContainerOptions{ Author: b.Author, Container: e.Container.ID, Run: config, Repository: repository, Tag: tag, }) if err != nil { return fmt.Errorf("unable to commit build container: %v", err) } e.Image = image glog.V(4).Infof("Committed %s to %s", e.Container.ID, e.Image.ID) if e.LogFn != nil { e.LogFn("Done") } return nil }
// RunContainer creates and starts a container using the image specified in the options with the ability // to stream input or output func (d *stiDocker) RunContainer(opts RunContainerOptions) (err error) { // get info about the specified image image := getImageName(opts.Image) var imageMetadata *docker.Image if opts.PullImage { imageMetadata, err = d.CheckAndPullImage(image) } else { imageMetadata, err = d.client.InspectImage(image) } if err != nil { glog.Errorf("Unable to get image metadata for %s: %v", image, err) return err } config := docker.Config{ Image: image, } config, tarDestination := runContainerTar(opts, config, imageMetadata) if opts.Env != nil { config.Env = opts.Env } if opts.Stdin != nil { config.OpenStdin = true config.StdinOnce = true } if opts.Stdout != nil { config.AttachStdout = true } glog.V(2).Infof("Creating container using config: %+v", config) ccopts := docker.CreateContainerOptions{Name: "", Config: &config} if opts.TargetImage { ccopts.HostConfig = &docker.HostConfig{PublishAllPorts: true} } container, err := d.client.CreateContainer(ccopts) if err != nil { return err } defer d.RemoveContainer(container.ID) glog.V(2).Infof("Attaching to container") // creating / piping the channels in runContainerAttachOne lead to unintended hangs attached := make(chan struct{}) wg := runContainerAttachOne(attached, container, opts, d) attached <- <-attached // If attaching both stdin and stdout or stderr, attach stdout and stderr in // a second goroutine // TODO remove this goroutine when docker 1.4 will be in broad usage, // see: https://github.com/docker/docker/commit/f936a10d8048f471d115978472006e1b58a7c67d if opts.Stdin != nil && opts.Stdout != nil { // creating / piping the channels in runContainerAttachTwo lead to unintended hangs attached2 := make(chan struct{}) runContainerAttachTwo(attached2, container, opts, d, wg) attached2 <- <-attached2 } glog.V(2).Infof("Starting container") if err = d.client.StartContainer(container.ID, nil); err != nil { return err } if opts.OnStart != nil { if err = opts.OnStart(); err != nil { return err } } if opts.TargetImage { runContainerDockerRun(container, d, image) } else { werr := runContainerWait(wg, d, container) if werr != nil { return werr } } if opts.PostExec != nil { glog.V(2).Infof("Invoking postExecution function") if err = opts.PostExec.PostExecute(container.ID, tarDestination); err != nil { return err } } return nil }