// Execute runs the command func (c *CommandImport) Execute(b *Build) (s State, err error) { s = b.state args := c.cfg.args if len(args) == 0 { return s, fmt.Errorf("IMPORT requires at least one argument") } if b.prevExportContainerID == "" { return s, fmt.Errorf("You have to EXPORT something first in order to IMPORT") } // TODO: EXPORT and IMPORT cache is not invalidated properly in between // different tracks of the same build. The EXPORT may be cached // because it was built earlier with the same prerequisites, but the actual // data in the exports container may be from the latest EXPORT of different // build. So we need to prefix ~/.rocker_exports dir with some id somehow. if b.currentExportContainerName == "" { return s, fmt.Errorf("You have to EXPORT something first to do IMPORT") } exportsContainer, err := b.getExportsContainer(b.currentExportContainerName) if err != nil { return s, err } log.Infof("| Import from %s (%.12s)", b.currentExportContainerName, exportsContainer.ID) // If only one argument was given to IMPORT, use the same path for destination // IMPORT /my/dir/file.tar --> ADD ./EXPORT_VOLUME/my/dir/file.tar /my/dir/file.tar if len(args) < 2 { args = []string{args[0], "/"} } dest := args[len(args)-1] // last one is always the dest src := []string{} for _, arg := range args[0 : len(args)-1] { argResolved, err := util.ResolvePath(ExportsPath, arg) if err != nil { return s, fmt.Errorf("Invalid IMPORT source: %s", arg) } src = append(src, argResolved) } s.Commit("IMPORT %q : %q %s", b.prevExportContainerID, src, dest) // Check cache s, hit, err := b.probeCache(s) if err != nil { return s, err } if hit { return s, nil } // Remember original stuff so we can restore it when we finished origState := s var importID string defer func() { s = origState s.NoCache.ContainerID = importID }() cmd := []string{"/opt/rsync/bin/rsync", "-a"} if b.cfg.Verbose { cmd = append(cmd, "--verbose") } cmd = append(cmd, src...) cmd = append(cmd, dest) s.Config.Cmd = cmd s.Config.Entrypoint = []string{} // Append exports container as a volume s.NoCache.HostConfig.Binds = append(s.NoCache.HostConfig.Binds, mountsToBinds(exportsContainer.Mounts, "")...) if importID, err = b.client.CreateContainer(s); err != nil { return s, err } log.Infof("| Running in %.12s: %s", importID, strings.Join(cmd, " ")) if err = b.client.RunContainer(importID, false); err != nil { return s, err } // TODO: if b.exportsCacheBusted and IMPORT cache was invalidated, // CommitCommand then caches it anyway. return s, nil }
// Execute runs the command func (c *CommandExport) Execute(b *Build) (s State, err error) { s = b.state args := c.cfg.args if len(args) == 0 { return s, fmt.Errorf("EXPORT requires at least one argument") } // If only one argument was given to EXPORT, use basename of a file // EXPORT /my/dir/file.tar --> /EXPORT_VOLUME/file.tar if len(args) < 2 { args = []string{args[0], "/"} } src := args[0 : len(args)-1] dest := args[len(args)-1] // last one is always the dest // EXPORT /my/dir my_dir --> /EXPORT_VOLUME/my_dir // EXPORT /my/dir /my_dir --> /EXPORT_VOLUME/my_dir // EXPORT /my/dir stuff/ --> /EXPORT_VOLUME/stuff/my_dir // EXPORT /my/dir /stuff/ --> /EXPORT_VOLUME/stuff/my_dir // EXPORT /my/dir/* / --> /EXPORT_VOLUME/stuff/my_dir s.Commit("EXPORT %q to %s, prev_export_container_salt: %s", src, dest, b.prevExportContainerID) // build the command cmdDestPath, err := util.ResolvePath(ExportsPath, dest) if err != nil { return s, fmt.Errorf("Invalid EXPORT destination: %s", dest) } s, hit, err := b.probeCacheAndPreserveCommits(s) if err != nil { return s, err } if hit { b.prevExportContainerID = s.ExportsID b.currentExportContainerName = exportsContainerName(s.ParentID, s.GetCommits()) log.Infof("| Export container: %s", b.currentExportContainerName) log.Debugf("===EXPORT CONTAINER NAME: %s ('%s', '%s')", b.currentExportContainerName, s.ParentID, s.GetCommits()) s.CleanCommits() return s, nil } prevExportContainerName := b.currentExportContainerName b.currentExportContainerName = exportsContainerName(s.ImageID, s.GetCommits()) exportsContainer, err := b.getExportsContainerAndSync(b.currentExportContainerName, prevExportContainerName) if err != nil { return s, err } // Remember original stuff so we can restore it when we finished var exportsID string origState := s defer func() { s = origState s.ExportsID = exportsID b.prevExportContainerID = exportsID }() // Append exports container as a volume s.NoCache.HostConfig.Binds = append(s.NoCache.HostConfig.Binds, mountsToBinds(exportsContainer.Mounts, "")...) cmd := []string{"/opt/rsync/bin/rsync", "-a", "--delete-during"} if b.cfg.Verbose { cmd = append(cmd, "--verbose") } cmd = append(cmd, src...) cmd = append(cmd, cmdDestPath) s.Config.Cmd = cmd s.Config.Entrypoint = []string{} if exportsID, err = b.client.CreateContainer(s); err != nil { return s, err } defer b.client.RemoveContainer(exportsID) log.Infof("| Running in %.12s: %s", exportsID, strings.Join(cmd, " ")) if err = b.client.RunContainer(exportsID, false); err != nil { return s, err } return s, nil }