| ... | ... |
@@ -145,8 +145,55 @@ func (builder *Builder) Commit(container *Container, repository, tag, comment, a |
| 145 | 145 |
return img, nil |
| 146 | 146 |
} |
| 147 | 147 |
|
| 148 |
-func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) error {
|
|
| 149 |
- var image, base *Image |
|
| 148 |
+func (builder *Builder) clearTmp(containers, images map[string]struct{}) {
|
|
| 149 |
+ for c := range containers {
|
|
| 150 |
+ tmp := builder.runtime.Get(c) |
|
| 151 |
+ builder.runtime.Destroy(tmp) |
|
| 152 |
+ Debugf("Removing container %s", c)
|
|
| 153 |
+ } |
|
| 154 |
+ for i := range images {
|
|
| 155 |
+ builder.runtime.graph.Delete(i) |
|
| 156 |
+ Debugf("Removing image %s", i)
|
|
| 157 |
+ } |
|
| 158 |
+} |
|
| 159 |
+ |
|
| 160 |
+func (builder *Builder) getCachedImage(image *Image, config *Config) (*Image, error) {
|
|
| 161 |
+ // Retrieve all images |
|
| 162 |
+ images, err := builder.graph.All() |
|
| 163 |
+ if err != nil {
|
|
| 164 |
+ return nil, err |
|
| 165 |
+ } |
|
| 166 |
+ |
|
| 167 |
+ // Store the tree in a map of map (map[parentId][childId]) |
|
| 168 |
+ imageMap := make(map[string]map[string]struct{})
|
|
| 169 |
+ for _, img := range images {
|
|
| 170 |
+ if _, exists := imageMap[img.Parent]; !exists {
|
|
| 171 |
+ imageMap[img.Parent] = make(map[string]struct{})
|
|
| 172 |
+ } |
|
| 173 |
+ imageMap[img.Parent][img.Id] = struct{}{}
|
|
| 174 |
+ } |
|
| 175 |
+ |
|
| 176 |
+ // Loop on the children of the given image and check the config |
|
| 177 |
+ for elem := range imageMap[image.Id] {
|
|
| 178 |
+ img, err := builder.graph.Get(elem) |
|
| 179 |
+ if err != nil {
|
|
| 180 |
+ return nil, err |
|
| 181 |
+ } |
|
| 182 |
+ if CompareConfig(&img.ContainerConfig, config) {
|
|
| 183 |
+ return img, nil |
|
| 184 |
+ } |
|
| 185 |
+ } |
|
| 186 |
+ return nil, nil |
|
| 187 |
+} |
|
| 188 |
+ |
|
| 189 |
+func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) (*Image, error) {
|
|
| 190 |
+ var ( |
|
| 191 |
+ image, base *Image |
|
| 192 |
+ maintainer string |
|
| 193 |
+ tmpContainers map[string]struct{} = make(map[string]struct{})
|
|
| 194 |
+ tmpImages map[string]struct{} = make(map[string]struct{})
|
|
| 195 |
+ ) |
|
| 196 |
+ defer builder.clearTmp(tmpContainers, tmpImages) |
|
| 150 | 197 |
|
| 151 | 198 |
file := bufio.NewReader(dockerfile) |
| 152 | 199 |
for {
|
| ... | ... |
@@ -204,6 +251,14 @@ func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) error {
|
| 204 | 204 |
return err |
| 205 | 205 |
} |
| 206 | 206 |
|
| 207 |
+ if cache, err := builder.getCachedImage(image, config); err != nil {
|
|
| 208 |
+ return nil, err |
|
| 209 |
+ } else if cache != nil {
|
|
| 210 |
+ image = cache |
|
| 211 |
+ fmt.Fprintf(stdout, "===> %s\n", image.ShortId()) |
|
| 212 |
+ break |
|
| 213 |
+ } |
|
| 214 |
+ |
|
| 207 | 215 |
// Create the container and start it |
| 208 | 216 |
c, err := builder.Create(config) |
| 209 | 217 |
if err != nil {
|
| ... | ... |
@@ -276,10 +331,16 @@ func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) error {
|
| 276 | 276 |
fmt.Fprintf(stdout, "Skipping unknown op %s\n", tmp[0]) |
| 277 | 277 |
} |
| 278 | 278 |
} |
| 279 |
- if base != nil {
|
|
| 280 |
- fmt.Fprintf(stdout, "Build finished. image id: %s\n", base.Id) |
|
| 281 |
- } else {
|
|
| 282 |
- fmt.Fprintf(stdout, "An error occured during the build\n") |
|
| 279 |
+ if image != nil {
|
|
| 280 |
+ // The build is successful, keep the temporary containers and images |
|
| 281 |
+ for i := range tmpImages {
|
|
| 282 |
+ delete(tmpImages, i) |
|
| 283 |
+ } |
|
| 284 |
+ for i := range tmpContainers {
|
|
| 285 |
+ delete(tmpContainers, i) |
|
| 286 |
+ } |
|
| 287 |
+ fmt.Fprintf(stdout, "Build finished. image id: %s\n", image.ShortId()) |
|
| 288 |
+ return image, nil |
|
| 283 | 289 |
} |
| 284 |
- return nil |
|
| 290 |
+ return nil, fmt.Errorf("An error occured during the build\n")
|
|
| 285 | 291 |
} |
| ... | ... |
@@ -474,3 +474,50 @@ func FindCgroupMountpoint(cgroupType string) (string, error) {
|
| 474 | 474 |
|
| 475 | 475 |
return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType)
|
| 476 | 476 |
} |
| 477 |
+ |
|
| 478 |
+// Compare two Config struct. Do not compare the "Image" nor "Hostname" fields |
|
| 479 |
+// If OpenStdin is set, then it differs |
|
| 480 |
+func CompareConfig(a, b *Config) bool {
|
|
| 481 |
+ if a == nil || b == nil || |
|
| 482 |
+ a.OpenStdin || b.OpenStdin {
|
|
| 483 |
+ return false |
|
| 484 |
+ } |
|
| 485 |
+ if a.AttachStdout != b.AttachStdout || |
|
| 486 |
+ a.AttachStderr != b.AttachStderr || |
|
| 487 |
+ a.User != b.User || |
|
| 488 |
+ a.Memory != b.Memory || |
|
| 489 |
+ a.MemorySwap != b.MemorySwap || |
|
| 490 |
+ a.OpenStdin != b.OpenStdin || |
|
| 491 |
+ a.Tty != b.Tty {
|
|
| 492 |
+ return false |
|
| 493 |
+ } |
|
| 494 |
+ if len(a.Cmd) != len(b.Cmd) || |
|
| 495 |
+ len(a.Dns) != len(b.Dns) || |
|
| 496 |
+ len(a.Env) != len(b.Env) || |
|
| 497 |
+ len(a.PortSpecs) != len(b.PortSpecs) {
|
|
| 498 |
+ return false |
|
| 499 |
+ } |
|
| 500 |
+ |
|
| 501 |
+ for i := 0; i < len(a.Cmd); i++ {
|
|
| 502 |
+ if a.Cmd[i] != b.Cmd[i] {
|
|
| 503 |
+ return false |
|
| 504 |
+ } |
|
| 505 |
+ } |
|
| 506 |
+ for i := 0; i < len(a.Dns); i++ {
|
|
| 507 |
+ if a.Dns[i] != b.Dns[i] {
|
|
| 508 |
+ return false |
|
| 509 |
+ } |
|
| 510 |
+ } |
|
| 511 |
+ for i := 0; i < len(a.Env); i++ {
|
|
| 512 |
+ if a.Env[i] != b.Env[i] {
|
|
| 513 |
+ return false |
|
| 514 |
+ } |
|
| 515 |
+ } |
|
| 516 |
+ for i := 0; i < len(a.PortSpecs); i++ {
|
|
| 517 |
+ if a.PortSpecs[i] != b.PortSpecs[i] {
|
|
| 518 |
+ return false |
|
| 519 |
+ } |
|
| 520 |
+ } |
|
| 521 |
+ |
|
| 522 |
+ return true |
|
| 523 |
+} |