func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.StreamFormatter) (string, error) { out = utils.NewWriteFlusher(out) img, err := srv.runtime.repositories.LookupImage(name) if err != nil { return "", err } file, err := utils.Download(url, out) if err != nil { return "", err } defer file.Body.Close() config, _, _, err := ParseRun([]string{img.ID, "echo", "insert", url, path}, srv.runtime.capabilities) if err != nil { return "", err } b := NewBuilder(srv.runtime) c, err := b.Create(config) if err != nil { return "", err } if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, sf.FormatProgress("Downloading", "%v/%v (%v)"), sf), path); err != nil { return "", err } // FIXME: Handle custom repo, tag comment, author img, err = b.Commit(c, "", "", img.Comment, img.Author, nil) if err != nil { return "", err } out.Write(sf.FormatStatus(img.ID)) return img.ShortID(), nil }
func (b *buildFile) addRemote(container *Container, orig, dest string) error { file, err := utils.Download(orig, ioutil.Discard) if err != nil { return err } defer file.Body.Close() // If the destination is a directory, figure out the filename. if strings.HasSuffix(dest, "/") { u, err := url.Parse(orig) if err != nil { return err } path := u.Path if strings.HasSuffix(path, "/") { path = path[:len(path)-1] } parts := strings.Split(path, "/") filename := parts[len(parts)-1] if filename == "" { return fmt.Errorf("cannot determine filename from url: %s", u) } dest = dest + filename } return container.Inject(file.Body, dest) }
func (b *buildFile) CmdInsert(args string) error { if b.image == "" { return fmt.Errorf("Please provide a source image with `from` prior to insert") } tmp := strings.SplitN(args, " ", 2) if len(tmp) != 2 { return fmt.Errorf("Invalid INSERT format") } sourceUrl := strings.Trim(tmp[0], " ") destPath := strings.Trim(tmp[1], " ") file, err := utils.Download(sourceUrl, b.out) if err != nil { return err } defer file.Body.Close() b.config.Cmd = []string{"echo", "INSERT", sourceUrl, "in", destPath} cid, err := b.run() if err != nil { return err } container := b.runtime.Get(cid) if container == nil { return fmt.Errorf("An error occured while creating the container") } if err := container.Inject(file.Body, destPath); err != nil { return err } return b.commit(cid) }
func (srv *Server) ImageInsert(name, url, path string, out io.Writer) error { out = utils.NewWriteFlusher(out) img, err := srv.runtime.repositories.LookupImage(name) if err != nil { return err } file, err := utils.Download(url, out) if err != nil { return err } defer file.Body.Close() config, _, err := ParseRun([]string{img.Id, "echo", "insert", url, path}, srv.runtime.capabilities) if err != nil { return err } b := NewBuilder(srv.runtime) c, err := b.Create(config) if err != nil { return err } if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, "Downloading %v/%v (%v)\r", false), path); err != nil { return err } // FIXME: Handle custom repo, tag comment, author img, err = b.Commit(c, "", "", img.Comment, img.Author, nil) if err != nil { return err } fmt.Fprintf(out, "%s\n", img.Id) return nil }
func (b *buildFile) addRemote(container *Container, orig, dest string) error { file, err := utils.Download(orig, ioutil.Discard) if err != nil { return err } defer file.Body.Close() return container.Inject(file.Body, dest) }
func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Writer, sf *utils.StreamFormatter) error { var archive io.Reader var resp *http.Response if src == "-" { archive = in } else { u, err := url.Parse(src) if err != nil { return err } if u.Scheme == "" { u.Scheme = "http" u.Host = src u.Path = "" } out.Write(sf.FormatStatus("Downloading from %s", u)) // Download with curl (pretty progress bar) // If curl is not available, fallback to http.Get() resp, err = utils.Download(u.String(), out) if err != nil { return err } archive = utils.ProgressReader(resp.Body, int(resp.ContentLength), out, sf.FormatProgress("Importing", "%v/%v (%v)"), sf) } img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil) if err != nil { return err } // Optionally register the image at REPO/TAG if repo != "" { if err := srv.runtime.repositories.Set(repo, tag, img.ID, true); err != nil { return err } } out.Write(sf.FormatStatus(img.ShortID())) return nil }
func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if version < 1.3 { return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.") } remoteURL := r.FormValue("remote") repoName := r.FormValue("t") rawSuppressOutput := r.FormValue("q") rawNoCache := r.FormValue("nocache") rawRm := r.FormValue("rm") repoName, tag := utils.ParseRepositoryTag(repoName) var context io.Reader if remoteURL == "" { context = r.Body } else if utils.IsGIT(remoteURL) { if !strings.HasPrefix(remoteURL, "git://") { remoteURL = "https://" + remoteURL } root, err := ioutil.TempDir("", "docker-build-git") if err != nil { return err } defer os.RemoveAll(root) if output, err := exec.Command("git", "clone", remoteURL, root).CombinedOutput(); err != nil { return fmt.Errorf("Error trying to use git: %s (%s)", err, output) } c, err := archive.Tar(root, archive.Bzip2) if err != nil { return err } context = c } else if utils.IsURL(remoteURL) { f, err := utils.Download(remoteURL, ioutil.Discard) if err != nil { return err } defer f.Body.Close() dockerFile, err := ioutil.ReadAll(f.Body) if err != nil { return err } c, err := MkBuildContext(string(dockerFile), nil) if err != nil { return err } context = c } suppressOutput, err := getBoolParam(rawSuppressOutput) if err != nil { return err } noCache, err := getBoolParam(rawNoCache) if err != nil { return err } rm, err := getBoolParam(rawRm) if err != nil { return err } b := NewBuildFile(srv, utils.NewWriteFlusher(w), !suppressOutput, !noCache, rm) id, err := b.Build(context) if err != nil { return fmt.Errorf("Error build: %s", err) } if repoName != "" { srv.runtime.repositories.Set(repoName, tag, id, false) } return nil }
func (b *buildFile) CmdAdd(args string) error { if b.context == nil { return fmt.Errorf("No context given. Impossible to use ADD") } tmp := strings.SplitN(args, " ", 2) if len(tmp) != 2 { return fmt.Errorf("Invalid ADD format") } orig, err := b.ReplaceEnvMatches(strings.Trim(tmp[0], " \t")) if err != nil { return err } dest, err := b.ReplaceEnvMatches(strings.Trim(tmp[1], " \t")) if err != nil { return err } cmd := b.config.Cmd b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) ADD %s in %s", orig, dest)} b.config.Image = b.image var ( origPath = orig destPath = dest remoteHash string isRemote bool ) if utils.IsURL(orig) { // Initiate the download isRemote = true resp, err := utils.Download(orig) if err != nil { return err } // Create a tmp dir tmpDirName, err := ioutil.TempDir(b.contextPath, "docker-remote") if err != nil { return err } // Create a tmp file within our tmp dir tmpFileName := path.Join(tmpDirName, "tmp") tmpFile, err := os.OpenFile(tmpFileName, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) if err != nil { return err } defer os.RemoveAll(tmpDirName) // Download and dump result to tmp file if _, err := io.Copy(tmpFile, resp.Body); err != nil { tmpFile.Close() return err } tmpFile.Close() origPath = path.Join(filepath.Base(tmpDirName), filepath.Base(tmpFileName)) // Process the checksum r, err := archive.Tar(tmpFileName, archive.Uncompressed) if err != nil { return err } tarSum := utils.TarSum{Reader: r, DisableCompression: true} remoteHash = tarSum.Sum(nil) r.Close() // If the destination is a directory, figure out the filename. if strings.HasSuffix(dest, "/") { u, err := url.Parse(orig) if err != nil { return err } path := u.Path if strings.HasSuffix(path, "/") { path = path[:len(path)-1] } parts := strings.Split(path, "/") filename := parts[len(parts)-1] if filename == "" { return fmt.Errorf("cannot determine filename from url: %s", u) } destPath = dest + filename } } if err := b.checkPathForAddition(origPath); err != nil { return err } // Hash path and check the cache if b.utilizeCache { var ( hash string sums = b.context.GetSums() ) if remoteHash != "" { hash = remoteHash } else if fi, err := os.Stat(path.Join(b.contextPath, origPath)); err != nil { return err } else if fi.IsDir() { var subfiles []string for file, sum := range sums { absFile := path.Join(b.contextPath, file) absOrigPath := path.Join(b.contextPath, origPath) if strings.HasPrefix(absFile, absOrigPath) { subfiles = append(subfiles, sum) } } sort.Strings(subfiles) hasher := sha256.New() hasher.Write([]byte(strings.Join(subfiles, ","))) hash = "dir:" + hex.EncodeToString(hasher.Sum(nil)) } else { if origPath[0] == '/' && len(origPath) > 1 { origPath = origPath[1:] } origPath = strings.TrimPrefix(origPath, "./") if h, ok := sums[origPath]; ok { hash = "file:" + h } } b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) ADD %s in %s", hash, dest)} hit, err := b.probeCache() if err != nil { return err } // If we do not have a hash, never use the cache if hit && hash != "" { return nil } } // Create the container and start it container, _, err := b.runtime.Create(b.config, "") if err != nil { return err } b.tmpContainers[container.ID] = struct{}{} if err := container.Mount(); err != nil { return err } defer container.Unmount() if err := b.addContext(container, origPath, destPath, isRemote); err != nil { return err } if err := b.commit(container.ID, cmd, fmt.Sprintf("ADD %s in %s", orig, dest)); err != nil { return err } b.config.Cmd = cmd return nil }
func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if version < 1.3 { return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.") } remoteURL := r.FormValue("remote") repoName := r.FormValue("t") tag := "" if strings.Contains(repoName, ":") { remoteParts := strings.Split(repoName, ":") tag = remoteParts[1] repoName = remoteParts[0] } var context io.Reader if remoteURL == "" { context = r.Body } else if utils.IsGIT(remoteURL) { if !strings.HasPrefix(remoteURL, "git://") { remoteURL = "https://" + remoteURL } root, err := ioutil.TempDir("", "docker-build-git") if err != nil { return err } defer os.RemoveAll(root) if output, err := exec.Command("git", "clone", remoteURL, root).CombinedOutput(); err != nil { return fmt.Errorf("Error trying to use git: %s (%s)", err, output) } c, err := Tar(root, Bzip2) if err != nil { return err } context = c } else if utils.IsURL(remoteURL) { f, err := utils.Download(remoteURL, ioutil.Discard) if err != nil { return err } defer f.Body.Close() dockerFile, err := ioutil.ReadAll(f.Body) if err != nil { return err } c, err := mkBuildContext(string(dockerFile), nil) if err != nil { return err } context = c } b := NewBuildFile(srv, utils.NewWriteFlusher(w)) id, err := b.Build(context) if err != nil { fmt.Fprintf(w, "Error build: %s\n", err) return err } if repoName != "" { srv.runtime.repositories.Set(repoName, tag, id, false) } return nil }
func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if version < 1.3 { return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.") } var ( remoteURL = r.FormValue("remote") repoName = r.FormValue("t") rawSuppressOutput = r.FormValue("q") rawNoCache = r.FormValue("nocache") rawRm = r.FormValue("rm") authEncoded = r.Header.Get("X-Registry-Auth") authConfig = &auth.AuthConfig{} configFileEncoded = r.Header.Get("X-Registry-Config") configFile = &auth.ConfigFile{} tag string ) repoName, tag = utils.ParseRepositoryTag(repoName) // This block can be removed when API versions prior to 1.9 are deprecated. // Both headers will be parsed and sent along to the daemon, but if a non-empty // ConfigFile is present, any value provided as an AuthConfig directly will // be overridden. See BuildFile::CmdFrom for details. if version < 1.9 && authEncoded != "" { authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) if err := json.NewDecoder(authJson).Decode(authConfig); err != nil { // for a pull it is not an error if no auth was given // to increase compatibility with the existing api it is defaulting to be empty authConfig = &auth.AuthConfig{} } } if configFileEncoded != "" { configFileJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(configFileEncoded)) if err := json.NewDecoder(configFileJson).Decode(configFile); err != nil { // for a pull it is not an error if no auth was given // to increase compatibility with the existing api it is defaulting to be empty configFile = &auth.ConfigFile{} } } var context io.Reader if remoteURL == "" { context = r.Body } else if utils.IsGIT(remoteURL) { if !strings.HasPrefix(remoteURL, "git://") { remoteURL = "https://" + remoteURL } root, err := ioutil.TempDir("", "docker-build-git") if err != nil { return err } defer os.RemoveAll(root) if output, err := exec.Command("git", "clone", remoteURL, root).CombinedOutput(); err != nil { return fmt.Errorf("Error trying to use git: %s (%s)", err, output) } c, err := archive.Tar(root, archive.Uncompressed) if err != nil { return err } context = c } else if utils.IsURL(remoteURL) { f, err := utils.Download(remoteURL) if err != nil { return err } defer f.Body.Close() dockerFile, err := ioutil.ReadAll(f.Body) if err != nil { return err } c, err := MkBuildContext(string(dockerFile), nil) if err != nil { return err } context = c } suppressOutput, err := getBoolParam(rawSuppressOutput) if err != nil { return err } noCache, err := getBoolParam(rawNoCache) if err != nil { return err } rm, err := getBoolParam(rawRm) if err != nil { return err } if version >= 1.8 { w.Header().Set("Content-Type", "application/json") } sf := utils.NewStreamFormatter(version >= 1.8) b := NewBuildFile(srv, &StdoutFormater{ Writer: utils.NewWriteFlusher(w), StreamFormatter: sf, }, &StderrFormater{ Writer: utils.NewWriteFlusher(w), StreamFormatter: sf, }, !suppressOutput, !noCache, rm, utils.NewWriteFlusher(w), sf, authConfig, configFile) id, err := b.Build(context) if err != nil { if sf.Used() { w.Write(sf.FormatError(err)) return nil } return fmt.Errorf("Error build: %s", err) } if repoName != "" { srv.runtime.repositories.Set(repoName, tag, id, false) } return nil }
func (b *buildFile) CmdAdd(args string) error { if b.context == "" { return fmt.Errorf("No context given. Impossible to use ADD") } tmp := strings.SplitN(args, " ", 2) if len(tmp) != 2 { return fmt.Errorf("Invalid ADD format") } orig, err := b.ReplaceEnvMatches(strings.Trim(tmp[0], " \t")) if err != nil { return err } dest, err := b.ReplaceEnvMatches(strings.Trim(tmp[1], " \t")) if err != nil { return err } cmd := b.config.Cmd b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) ADD %s in %s", orig, dest)} b.config.Image = b.image origPath := orig destPath := dest clobberTimes := false if utils.IsURL(orig) { clobberTimes = true resp, err := utils.Download(orig) if err != nil { return err } tmpDirName, err := ioutil.TempDir(b.context, "docker-remote") if err != nil { return err } tmpFileName := path.Join(tmpDirName, "tmp") tmpFile, err := os.OpenFile(tmpFileName, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) if err != nil { return err } defer os.RemoveAll(tmpDirName) if _, err = io.Copy(tmpFile, resp.Body); err != nil { return err } origPath = path.Join(filepath.Base(tmpDirName), filepath.Base(tmpFileName)) tmpFile.Close() // If the destination is a directory, figure out the filename. if strings.HasSuffix(dest, "/") { u, err := url.Parse(orig) if err != nil { return err } path := u.Path if strings.HasSuffix(path, "/") { path = path[:len(path)-1] } parts := strings.Split(path, "/") filename := parts[len(parts)-1] if filename == "" { return fmt.Errorf("cannot determine filename from url: %s", u) } destPath = dest + filename } } if err := b.checkPathForAddition(origPath); err != nil { return err } // Hash path and check the cache if b.utilizeCache { hash, err := b.hashPath(b.context, origPath, clobberTimes) if err != nil { return err } b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) ADD %s in %s", hash, dest)} hit, err := b.probeCache() if err != nil { return err } if hit { return nil } } // Create the container and start it container, _, err := b.runtime.Create(b.config, "") if err != nil { return err } b.tmpContainers[container.ID] = struct{}{} if err := container.EnsureMounted(); err != nil { return err } defer container.Unmount() if err := b.addContext(container, origPath, destPath); err != nil { return err } if err := b.commit(container.ID, cmd, fmt.Sprintf("ADD %s in %s", orig, dest)); err != nil { return err } b.config.Cmd = cmd return nil }
func (b *buildFile) CmdAdd(args string) error { if b.context == nil { return fmt.Errorf("No context given. Impossible to use ADD") } tmp := strings.SplitN(args, " ", 2) if len(tmp) != 2 { return fmt.Errorf("Invalid ADD format") } orig, err := b.ReplaceEnvMatches(strings.Trim(tmp[0], " \t")) if err != nil { return err } dest, err := b.ReplaceEnvMatches(strings.Trim(tmp[1], " \t")) if err != nil { return err } cmd := b.config.Cmd b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) ADD %s in %s", orig, dest)} b.config.Image = b.image // FIXME: do we really need this? var ( origPath = orig destPath = dest ) if utils.IsURL(orig) { resp, err := utils.Download(orig) if err != nil { return err } tmpDirName, err := ioutil.TempDir(b.contextPath, "docker-remote") if err != nil { return err } tmpFileName := path.Join(tmpDirName, "tmp") tmpFile, err := os.OpenFile(tmpFileName, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) if err != nil { return err } defer os.RemoveAll(tmpDirName) if _, err = io.Copy(tmpFile, resp.Body); err != nil { return err } origPath = path.Join(filepath.Base(tmpDirName), filepath.Base(tmpFileName)) tmpFile.Close() // If the destination is a directory, figure out the filename. if strings.HasSuffix(dest, "/") { u, err := url.Parse(orig) if err != nil { return err } path := u.Path if strings.HasSuffix(path, "/") { path = path[:len(path)-1] } parts := strings.Split(path, "/") filename := parts[len(parts)-1] if filename == "" { return fmt.Errorf("cannot determine filename from url: %s", u) } destPath = dest + filename } } if err := b.checkPathForAddition(origPath); err != nil { return err } // Hash path and check the cache if b.utilizeCache { var ( hash string sums = b.context.GetSums() ) if fi, err := os.Stat(path.Join(b.contextPath, origPath)); err != nil { return err } else if fi.IsDir() { var subfiles []string for file, sum := range sums { if strings.HasPrefix(file, origPath) { subfiles = append(subfiles, sum) } } sort.Strings(subfiles) hasher := sha256.New() hasher.Write([]byte(strings.Join(subfiles, ","))) hash = "dir:" + hex.EncodeToString(hasher.Sum(nil)) } else { hash = "file:" + sums[origPath] } b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) ADD %s in %s", hash, dest)} hit, err := b.probeCache() if err != nil { return err } if hit { return nil } } // Create the container and start it container, _, err := b.runtime.Create(b.config, "") if err != nil { return err } b.tmpContainers[container.ID] = struct{}{} if err := container.EnsureMounted(); err != nil { return err } defer container.Unmount() if err := b.addContext(container, origPath, destPath); err != nil { return err } if err := b.commit(container.ID, cmd, fmt.Sprintf("ADD %s in %s", orig, dest)); err != nil { return err } b.config.Cmd = cmd return nil }
func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) (*Image, error) { var ( image, base *Image config *Config maintainer string env map[string]string = make(map[string]string) tmpContainers map[string]struct{} = make(map[string]struct{}) tmpImages map[string]struct{} = make(map[string]struct{}) ) defer builder.clearTmp(tmpContainers, tmpImages) file := bufio.NewReader(dockerfile) for { line, err := file.ReadString('\n') if err != nil { if err == io.EOF { break } return nil, err } line = strings.Replace(strings.TrimSpace(line), " ", " ", 1) // Skip comments and empty line if len(line) == 0 || line[0] == '#' { continue } tmp := strings.SplitN(line, " ", 2) if len(tmp) != 2 { return nil, fmt.Errorf("Invalid Dockerfile format") } instruction := strings.Trim(tmp[0], " ") arguments := strings.Trim(tmp[1], " ") switch strings.ToLower(instruction) { case "from": fmt.Fprintf(stdout, "FROM %s\n", arguments) image, err = builder.runtime.repositories.LookupImage(arguments) if err != nil { // if builder.runtime.graph.IsNotExist(err) { // var tag, remote string // if strings.Contains(arguments, ":") { // remoteParts := strings.Split(arguments, ":") // tag = remoteParts[1] // remote = remoteParts[0] // } else { // remote = arguments // } // panic("TODO: reimplement this") // // if err := builder.runtime.graph.PullRepository(stdout, remote, tag, builder.runtime.repositories, builder.runtime.authConfig); err != nil { // // return nil, err // // } // image, err = builder.runtime.repositories.LookupImage(arguments) // if err != nil { // return nil, err // } // } else { return nil, err // } } config = &Config{} break case "maintainer": fmt.Fprintf(stdout, "MAINTAINER %s\n", arguments) maintainer = arguments break case "run": fmt.Fprintf(stdout, "RUN %s\n", arguments) if image == nil { return nil, fmt.Errorf("Please provide a source image with `from` prior to run") } config, _, err := ParseRun([]string{image.Id, "/bin/sh", "-c", arguments}, builder.runtime.capabilities) if err != nil { return nil, err } for key, value := range env { config.Env = append(config.Env, fmt.Sprintf("%s=%s", key, value)) } if cache, err := builder.getCachedImage(image, config); err != nil { return nil, err } else if cache != nil { image = cache fmt.Fprintf(stdout, "===> %s\n", image.ShortId()) break } utils.Debugf("Env -----> %v ------ %v\n", config.Env, env) // Create the container and start it c, err := builder.Create(config) if err != nil { return nil, err } if os.Getenv("DEBUG") != "" { out, _ := c.StdoutPipe() err2, _ := c.StderrPipe() go io.Copy(os.Stdout, out) go io.Copy(os.Stdout, err2) } if err := c.Start(); err != nil { return nil, err } tmpContainers[c.Id] = struct{}{} // Wait for it to finish if result := c.Wait(); result != 0 { return nil, fmt.Errorf("!!! '%s' return non-zero exit code '%d'. Aborting.", arguments, result) } // Commit the container base, err = builder.Commit(c, "", "", "", maintainer, nil) if err != nil { return nil, err } tmpImages[base.Id] = struct{}{} fmt.Fprintf(stdout, "===> %s\n", base.ShortId()) // use the base as the new image image = base break case "env": tmp := strings.SplitN(arguments, " ", 2) if len(tmp) != 2 { return nil, fmt.Errorf("Invalid ENV format") } key := strings.Trim(tmp[0], " ") value := strings.Trim(tmp[1], " ") fmt.Fprintf(stdout, "ENV %s %s\n", key, value) env[key] = value if image != nil { fmt.Fprintf(stdout, "===> %s\n", image.ShortId()) } else { fmt.Fprintf(stdout, "===> <nil>\n") } break case "cmd": fmt.Fprintf(stdout, "CMD %s\n", arguments) // Create the container and start it c, err := builder.Create(&Config{Image: image.Id, Cmd: []string{"", ""}}) if err != nil { return nil, err } if err := c.Start(); err != nil { return nil, err } tmpContainers[c.Id] = struct{}{} cmd := []string{} if err := json.Unmarshal([]byte(arguments), &cmd); err != nil { return nil, err } config.Cmd = cmd // Commit the container base, err = builder.Commit(c, "", "", "", maintainer, config) if err != nil { return nil, err } tmpImages[base.Id] = struct{}{} fmt.Fprintf(stdout, "===> %s\n", base.ShortId()) image = base break case "expose": ports := strings.Split(arguments, " ") fmt.Fprintf(stdout, "EXPOSE %v\n", ports) if image == nil { return nil, fmt.Errorf("Please provide a source image with `from` prior to copy") } // Create the container and start it c, err := builder.Create(&Config{Image: image.Id, Cmd: []string{"", ""}}) if err != nil { return nil, err } if err := c.Start(); err != nil { return nil, err } tmpContainers[c.Id] = struct{}{} config.PortSpecs = append(ports, config.PortSpecs...) // Commit the container base, err = builder.Commit(c, "", "", "", maintainer, config) if err != nil { return nil, err } tmpImages[base.Id] = struct{}{} fmt.Fprintf(stdout, "===> %s\n", base.ShortId()) image = base break case "insert": if image == nil { return nil, fmt.Errorf("Please provide a source image with `from` prior to copy") } tmp = strings.SplitN(arguments, " ", 2) if len(tmp) != 2 { return nil, fmt.Errorf("Invalid INSERT format") } sourceUrl := strings.Trim(tmp[0], " ") destPath := strings.Trim(tmp[1], " ") fmt.Fprintf(stdout, "COPY %s to %s in %s\n", sourceUrl, destPath, base.ShortId()) file, err := utils.Download(sourceUrl, stdout) if err != nil { return nil, err } defer file.Body.Close() config, _, err := ParseRun([]string{base.Id, "echo", "insert", sourceUrl, destPath}, builder.runtime.capabilities) if err != nil { return nil, err } c, err := builder.Create(config) if err != nil { return nil, err } if err := c.Start(); err != nil { return nil, err } // Wait for echo to finish if result := c.Wait(); result != 0 { return nil, fmt.Errorf("!!! '%s' return non-zero exit code '%d'. Aborting.", arguments, result) } if err := c.Inject(file.Body, destPath); err != nil { return nil, err } base, err = builder.Commit(c, "", "", "", maintainer, nil) if err != nil { return nil, err } fmt.Fprintf(stdout, "===> %s\n", base.ShortId()) image = base break default: fmt.Fprintf(stdout, "Skipping unknown instruction %s\n", strings.ToUpper(instruction)) } } if image != nil { // The build is successful, keep the temporary containers and images for i := range tmpImages { delete(tmpImages, i) } for i := range tmpContainers { delete(tmpContainers, i) } fmt.Fprintf(stdout, "Build finished. image id: %s\n", image.ShortId()) return image, nil } return nil, fmt.Errorf("An error occured during the build\n") }