mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Implement caching for docker builder
This commit is contained in:
parent
9959e2cd63
commit
a46fc3a59e
2 changed files with 115 additions and 7 deletions
75
builder.go
75
builder.go
|
@ -145,8 +145,55 @@ func (builder *Builder) Commit(container *Container, repository, tag, comment, a
|
||||||
return img, nil
|
return img, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) error {
|
func (builder *Builder) clearTmp(containers, images map[string]struct{}) {
|
||||||
var image, base *Image
|
for c := range containers {
|
||||||
|
tmp := builder.runtime.Get(c)
|
||||||
|
builder.runtime.Destroy(tmp)
|
||||||
|
Debugf("Removing container %s", c)
|
||||||
|
}
|
||||||
|
for i := range images {
|
||||||
|
builder.runtime.graph.Delete(i)
|
||||||
|
Debugf("Removing image %s", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
maintainer 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)
|
file := bufio.NewReader(dockerfile)
|
||||||
for {
|
for {
|
||||||
|
@ -204,6 +251,14 @@ func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) error {
|
||||||
return err
|
return 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
|
// Create the container and start it
|
||||||
c, err := builder.Create(config)
|
c, err := builder.Create(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -276,10 +331,16 @@ func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) error {
|
||||||
fmt.Fprintf(stdout, "Skipping unknown op %s\n", tmp[0])
|
fmt.Fprintf(stdout, "Skipping unknown op %s\n", tmp[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if base != nil {
|
if image != nil {
|
||||||
fmt.Fprintf(stdout, "Build finished. image id: %s\n", base.Id)
|
// The build is successful, keep the temporary containers and images
|
||||||
} else {
|
for i := range tmpImages {
|
||||||
fmt.Fprintf(stdout, "An error occured during the build\n")
|
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
|
return nil, fmt.Errorf("An error occured during the build\n")
|
||||||
}
|
}
|
||||||
|
|
47
utils.go
47
utils.go
|
@ -474,3 +474,50 @@ func FindCgroupMountpoint(cgroupType string) (string, error) {
|
||||||
|
|
||||||
return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType)
|
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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue