diff --git a/builder.go b/builder.go index 72989a8a0a..84fb01eb49 100644 --- a/builder.go +++ b/builder.go @@ -165,6 +165,35 @@ func (builder *Builder) clearTmp(containers, images map[string]struct{}) { } } +func (builder *Builder) getCachedImage(image *Image, config *Config) (*Image, error) { + // Retrieve all images + images, err := builder.graph.All() + if err != nil { + return nil, err + } + + // Store the tree in a map of map (map[parentId][childId]) + imageMap := make(map[string]map[string]struct{}) + for _, img := range images { + if _, exists := imageMap[img.Parent]; !exists { + imageMap[img.Parent] = make(map[string]struct{}) + } + imageMap[img.Parent][img.Id] = struct{}{} + } + + // Loop on the children of the given image and check the config + for elem := range imageMap[image.Id] { + img, err := builder.graph.Get(elem) + if err != nil { + return nil, err + } + if CompareConfig(&img.ContainerConfig, config) { + return img, nil + } + } + return nil, nil +} + func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) (*Image, error) { var ( image, base *Image @@ -183,7 +212,7 @@ func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) (*Image, e } return nil, err } - line = strings.TrimSpace(line) + line = strings.Replace(strings.TrimSpace(line), " ", " ", 1) // Skip comments and empty line if len(line) == 0 || line[0] == '#' { continue @@ -202,10 +231,12 @@ func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) (*Image, e if builder.runtime.graph.IsNotExist(err) { var tag, remote string - if strings.Contains(remote, ":") { - remoteParts := strings.Split(remote, ":") + if strings.Contains(arguments, ":") { + remoteParts := strings.Split(arguments, ":") tag = remoteParts[1] remote = remoteParts[0] + } else { + remote = arguments } if err := builder.runtime.graph.PullRepository(stdout, remote, tag, builder.runtime.repositories, builder.runtime.authConfig); err != nil { @@ -216,7 +247,6 @@ func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) (*Image, e if err != nil { return nil, err } - } else { return nil, err } @@ -237,6 +267,14 @@ func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) (*Image, e return nil, err } + 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 + } + // Create the container and start it c, err := builder.Create(config) if err != nil { @@ -344,10 +382,10 @@ func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) (*Image, e break default: - fmt.Fprintf(stdout, "Skipping unknown instruction %s\n", instruction) + fmt.Fprintf(stdout, "Skipping unknown instruction %s\n", strings.ToUpper(instruction)) } } - if base != nil { + if image != nil { // The build is successful, keep the temporary containers and images for i := range tmpImages { delete(tmpImages, i) @@ -355,9 +393,8 @@ func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) (*Image, e for i := range tmpContainers { delete(tmpContainers, i) } - fmt.Fprintf(stdout, "Build finished. image id: %s\n", base.ShortId()) - } else { - fmt.Fprintf(stdout, "An error occured during the build\n") + fmt.Fprintf(stdout, "Build finished. image id: %s\n", image.ShortId()) + return image, nil } - return base, nil + return nil, fmt.Errorf("An error occured during the build\n") } diff --git a/utils.go b/utils.go index 047a29abef..095be2f4bf 100644 --- a/utils.go +++ b/utils.go @@ -474,3 +474,50 @@ func FindCgroupMountpoint(cgroupType string) (string, error) { return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType) } + +// Compare two Config struct. Do not compare the "Image" nor "Hostname" fields +// If OpenStdin is set, then it differs +func CompareConfig(a, b *Config) bool { + if a == nil || b == nil || + a.OpenStdin || b.OpenStdin { + return false + } + if a.AttachStdout != b.AttachStdout || + a.AttachStderr != b.AttachStderr || + a.User != b.User || + a.Memory != b.Memory || + a.MemorySwap != b.MemorySwap || + a.OpenStdin != b.OpenStdin || + a.Tty != b.Tty { + return false + } + if len(a.Cmd) != len(b.Cmd) || + len(a.Dns) != len(b.Dns) || + len(a.Env) != len(b.Env) || + len(a.PortSpecs) != len(b.PortSpecs) { + return false + } + + for i := 0; i < len(a.Cmd); i++ { + if a.Cmd[i] != b.Cmd[i] { + return false + } + } + for i := 0; i < len(a.Dns); i++ { + if a.Dns[i] != b.Dns[i] { + return false + } + } + for i := 0; i < len(a.Env); i++ { + if a.Env[i] != b.Env[i] { + return false + } + } + for i := 0; i < len(a.PortSpecs); i++ { + if a.PortSpecs[i] != b.PortSpecs[i] { + return false + } + } + + return true +}