@@ -3,6 +3,7 @@ package builder
3
3
import (
4
4
"context"
5
5
"fmt"
6
+ "github.com/openshift/imagebuilder"
6
7
"os"
7
8
"os/exec"
8
9
"path/filepath"
@@ -14,6 +15,7 @@ import (
14
15
15
16
corev1 "k8s.io/api/core/v1"
16
17
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18
+ "k8s.io/apimachinery/pkg/util/sets"
17
19
18
20
s2iapi "github.com/openshift/source-to-image/pkg/api"
19
21
"github.com/openshift/source-to-image/pkg/tar"
@@ -86,7 +88,11 @@ func (d *DockerBuilder) Build() error {
86
88
87
89
buildTag := randomBuildTag (d .build .Namespace , d .build .Name )
88
90
dockerfilePath := getDockerfilePath (buildDir , d .build )
89
- imageNames := getDockerfileFrom (dockerfilePath )
91
+
92
+ imageNames , multiStage , err := rewriteDockerfileForImages (dockerfilePath , d .build )
93
+ if err != nil {
94
+ return err
95
+ }
90
96
if len (imageNames ) == 0 {
91
97
return fmt .Errorf ("no FROM image in Dockerfile" )
92
98
}
@@ -126,6 +132,14 @@ func (d *DockerBuilder) Build() error {
126
132
}
127
133
}
128
134
135
+ if s := d .build .Spec .Strategy .DockerStrategy ; s != nil && multiStage {
136
+ if s .ImageOptimizationPolicy == nil {
137
+ policy := buildapiv1 .ImageOptimizationSkipLayers
138
+ s .ImageOptimizationPolicy = & policy
139
+ glog .V (2 ).Infof ("Detected multi-stage Dockerfile, image will be built with imageOptimizationPolicy set to SkipLayers" )
140
+ }
141
+ }
142
+
129
143
startTime := metav1 .Now ()
130
144
err = d .dockerBuild (buildDir , buildTag , d .build .Spec .Source .Secrets )
131
145
@@ -465,7 +479,7 @@ func insertEnvAfterFrom(node *parser.Node, env []corev1.EnvVar) error {
465
479
return nil
466
480
}
467
481
468
- // getDockerfilefrom returns all the images behind "FROM" instruction in the dockerfile
482
+ // getDockerfileFrom returns all the images behind "FROM" instruction in the Dockerfile.
469
483
func getDockerfileFrom (dockerfilePath string ) []string {
470
484
var froms []string
471
485
if "" == dockerfilePath {
@@ -487,3 +501,49 @@ func getDockerfileFrom(dockerfilePath string) []string {
487
501
}
488
502
return froms
489
503
}
504
+
505
+ // rewriteDockerfileForImages returns all qualified images referenced by the Dockerfile, whether the
506
+ // build is a multi-stage build, or returns an error.
507
+ // TODO: implement image rewriting (so that multiple replacements can be made in multi-stage image).
508
+ func rewriteDockerfileForImages (dockerfilePath string , build * buildapiv1.Build ) ([]string , bool , error ) {
509
+ if len (dockerfilePath ) == 0 {
510
+ return nil , false , nil
511
+ }
512
+ node , err := parseDockerfile (dockerfilePath )
513
+ if err != nil {
514
+ return nil , false , err
515
+ }
516
+ names := make (map [string ]string )
517
+ images := sets .NewString ()
518
+ stages := imagebuilder .NewStages (node , imagebuilder .NewBuilder (nil ))
519
+ for _ , stage := range stages {
520
+ for _ , child := range stage .Node .Children {
521
+ switch {
522
+ case child .Value == dockercmd .From && child .Next != nil :
523
+ image := child .Next .Value
524
+ names [stage .Name ] = image
525
+ images .Insert (image )
526
+ case child .Value == dockercmd .Copy :
527
+ if ref , ok := nodeHasFromRef (child ); ok {
528
+ if len (ref ) > 0 {
529
+ if image , ok := names [ref ]; ! ok {
530
+ images .Insert (image )
531
+ }
532
+ }
533
+ }
534
+ }
535
+ }
536
+ }
537
+ return images .List (), len (stages ) > 0 , nil
538
+ }
539
+
540
+ func nodeHasFromRef (node * parser.Node ) (string , bool ) {
541
+ for _ , arg := range node .Flags {
542
+ switch {
543
+ case strings .HasPrefix (arg , "--from=" ):
544
+ from := strings .TrimPrefix (arg , "--from=" )
545
+ return from , true
546
+ }
547
+ }
548
+ return "" , false
549
+ }
0 commit comments