1
1
package builder
2
2
3
3
import (
4
+ "bytes"
4
5
"context"
5
6
"crypto/sha1"
6
7
"encoding/json"
@@ -9,28 +10,31 @@ import (
9
10
"math/rand"
10
11
"os"
11
12
"path/filepath"
13
+ "strings"
12
14
"time"
13
15
14
16
"k8s.io/apimachinery/pkg/api/errors"
15
17
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18
+ "k8s.io/apimachinery/pkg/util/sets"
16
19
"k8s.io/apimachinery/pkg/util/wait"
17
20
18
21
"github.com/docker/distribution/reference"
22
+ dockercmd "github.com/docker/docker/builder/dockerfile/command"
23
+ "github.com/docker/docker/builder/dockerfile/parser"
19
24
"github.com/fsouza/go-dockerclient"
20
-
25
+ "github.com/openshift/imagebuilder"
21
26
s2igit "github.com/openshift/source-to-image/pkg/scm/git"
22
27
"github.com/openshift/source-to-image/pkg/util"
23
28
24
29
buildapiv1 "github.com/openshift/api/build/v1"
30
+ buildclientv1 "github.com/openshift/client-go/build/clientset/versioned/typed/build/v1"
25
31
"github.com/openshift/origin/pkg/build/builder/timing"
26
32
builderutil "github.com/openshift/origin/pkg/build/builder/util"
27
33
"github.com/openshift/origin/pkg/build/builder/util/dockerfile"
28
34
buildutil "github.com/openshift/origin/pkg/build/util"
29
35
"github.com/openshift/origin/pkg/git"
30
36
imageapi "github.com/openshift/origin/pkg/image/apis/image"
31
37
utilglog "github.com/openshift/origin/pkg/util/glog"
32
-
33
- buildclientv1 "github.com/openshift/client-go/build/clientset/versioned/typed/build/v1"
34
38
)
35
39
36
40
// glog is a placeholder until the builders pass an output stream down
@@ -339,7 +343,12 @@ func readSourceInfo() (*git.SourceInfo, error) {
339
343
// Also append the environment variables and labels in the Dockerfile.
340
344
func addBuildParameters (dir string , build * buildapiv1.Build , sourceInfo * git.SourceInfo ) error {
341
345
dockerfilePath := getDockerfilePath (dir , build )
342
- node , err := parseDockerfile (dockerfilePath )
346
+
347
+ in , err := ioutil .ReadFile (dockerfilePath )
348
+ if err != nil {
349
+ return err
350
+ }
351
+ node , err := imagebuilder .ParseDockerfile (bytes .NewBuffer (in ))
343
352
if err != nil {
344
353
return err
345
354
}
@@ -375,12 +384,101 @@ func addBuildParameters(dir string, build *buildapiv1.Build, sourceInfo *git.Sou
375
384
return err
376
385
}
377
386
378
- instructions := dockerfile .ParseTreeToDockerfile (node )
387
+ out := dockerfile .Write (node )
388
+ glog .V (4 ).Infof ("Replacing dockerfile\n %s\n with:\n %s" , string (in ), string (out ))
389
+ return overwriteFile (dockerfilePath , out )
390
+ }
379
391
380
- // Overwrite the Dockerfile.
381
- fi , err := os .Stat (dockerfilePath )
392
+ // rewriteDockerfileForImages returns all qualified images referenced by the Dockerfile, whether the
393
+ // build is a multi-stage build, or returns an error.
394
+ // TODO: implement image rewriting (so that multiple replacements can be made in multi-stage image).
395
+ func rewriteDockerfileForImages (dockerfilePath string , build * buildapiv1.Build ) ([]string , bool , error ) {
396
+ if len (dockerfilePath ) == 0 {
397
+ return nil , false , nil
398
+ }
399
+ in , err := ioutil .ReadFile (dockerfilePath )
400
+ if err != nil {
401
+ return nil , false , err
402
+ }
403
+ node , err := imagebuilder .ParseDockerfile (bytes .NewBuffer (in ))
404
+ if err != nil {
405
+ return nil , false , err
406
+ }
407
+ replacements := make (map [string ]string )
408
+ for _ , image := range build .Spec .Source .Images {
409
+ if image .From .Kind != "DockerImage" || len (image .From .Name ) == 0 {
410
+ continue
411
+ }
412
+ for _ , name := range image .As {
413
+ replacements [name ] = image .From .Name
414
+ }
415
+ }
416
+ names := make (map [string ]string )
417
+ images := sets .NewString ()
418
+ stages := imagebuilder .NewStages (node , imagebuilder .NewBuilder (nil ))
419
+ for _ , stage := range stages {
420
+ for _ , child := range stage .Node .Children {
421
+ switch {
422
+ case child .Value == dockercmd .From && child .Next != nil :
423
+ image := child .Next .Value
424
+ if replacement , ok := replacements [image ]; ok {
425
+ image = replacement
426
+ child .Next .Value = image
427
+ }
428
+ names [stage .Name ] = image
429
+ images .Insert (image )
430
+ case child .Value == dockercmd .Copy :
431
+ if ref , ok := nodeHasFromRef (child ); ok {
432
+ fmt .Printf ("got image from COPY %q: %v\n " , ref , child )
433
+ if len (ref ) > 0 {
434
+ if _ , ok := names [ref ]; ! ok {
435
+ if replacement , ok := replacements [ref ]; ok {
436
+ ref = replacement
437
+ nodeReplaceFromRef (child , ref )
438
+ }
439
+ images .Insert (ref )
440
+ }
441
+ }
442
+ }
443
+ }
444
+ }
445
+ }
446
+ out := dockerfile .Write (node )
447
+ glog .V (4 ).Infof ("Replacing dockerfile\n %s\n with:\n %s" , string (in ), string (out ))
448
+ if err := overwriteFile (dockerfilePath , out ); err != nil {
449
+ return nil , false , err
450
+ }
451
+ return images .List (), len (stages ) > 1 , nil
452
+ }
453
+
454
+ func overwriteFile (name string , out []byte ) error {
455
+ f , err := os .OpenFile (name , os .O_TRUNC | os .O_WRONLY , 0 )
382
456
if err != nil {
383
457
return err
384
458
}
385
- return ioutil .WriteFile (dockerfilePath , instructions , fi .Mode ())
459
+ if _ , err := f .Write (out ); err != nil {
460
+ f .Close ()
461
+ return err
462
+ }
463
+ return f .Close ()
464
+ }
465
+
466
+ func nodeHasFromRef (node * parser.Node ) (string , bool ) {
467
+ for _ , arg := range node .Flags {
468
+ switch {
469
+ case strings .HasPrefix (arg , "--from=" ):
470
+ from := strings .TrimPrefix (arg , "--from=" )
471
+ return from , true
472
+ }
473
+ }
474
+ return "" , false
475
+ }
476
+
477
+ func nodeReplaceFromRef (node * parser.Node , name string ) {
478
+ for i , arg := range node .Flags {
479
+ switch {
480
+ case strings .HasPrefix (arg , "--from=" ):
481
+ node .Flags [i ] = fmt .Sprintf ("--from=%s" , name )
482
+ }
483
+ }
386
484
}
0 commit comments