Skip to content

Commit 7ecdea9

Browse files
Rewrite Dockerfiles for image source references
When an image source has one or more `as` names, replace the exact equivalents inside of the Dockerfile with resolved DockerImage references. Stage names are not replaced, but the strategy `from` can be if a reference exists.
1 parent 68319da commit 7ecdea9

File tree

9 files changed

+426
-446
lines changed

9 files changed

+426
-446
lines changed

pkg/build/builder/common.go

Lines changed: 106 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package builder
22

33
import (
4+
"bytes"
45
"context"
56
"crypto/sha1"
67
"encoding/json"
@@ -9,28 +10,31 @@ import (
910
"math/rand"
1011
"os"
1112
"path/filepath"
13+
"strings"
1214
"time"
1315

1416
"k8s.io/apimachinery/pkg/api/errors"
1517
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18+
"k8s.io/apimachinery/pkg/util/sets"
1619
"k8s.io/apimachinery/pkg/util/wait"
1720

1821
"github.com/docker/distribution/reference"
22+
dockercmd "github.com/docker/docker/builder/dockerfile/command"
23+
"github.com/docker/docker/builder/dockerfile/parser"
1924
"github.com/fsouza/go-dockerclient"
20-
25+
"github.com/openshift/imagebuilder"
2126
s2igit "github.com/openshift/source-to-image/pkg/scm/git"
2227
"github.com/openshift/source-to-image/pkg/util"
2328

2429
buildapiv1 "github.com/openshift/api/build/v1"
30+
buildclientv1 "github.com/openshift/client-go/build/clientset/versioned/typed/build/v1"
2531
"github.com/openshift/origin/pkg/build/builder/timing"
2632
builderutil "github.com/openshift/origin/pkg/build/builder/util"
2733
"github.com/openshift/origin/pkg/build/builder/util/dockerfile"
2834
buildutil "github.com/openshift/origin/pkg/build/util"
2935
"github.com/openshift/origin/pkg/git"
3036
imageapi "github.com/openshift/origin/pkg/image/apis/image"
3137
utilglog "github.com/openshift/origin/pkg/util/glog"
32-
33-
buildclientv1 "github.com/openshift/client-go/build/clientset/versioned/typed/build/v1"
3438
)
3539

3640
// glog is a placeholder until the builders pass an output stream down
@@ -339,7 +343,12 @@ func readSourceInfo() (*git.SourceInfo, error) {
339343
// Also append the environment variables and labels in the Dockerfile.
340344
func addBuildParameters(dir string, build *buildapiv1.Build, sourceInfo *git.SourceInfo) error {
341345
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))
343352
if err != nil {
344353
return err
345354
}
@@ -375,12 +384,101 @@ func addBuildParameters(dir string, build *buildapiv1.Build, sourceInfo *git.Sou
375384
return err
376385
}
377386

378-
instructions := dockerfile.ParseTreeToDockerfile(node)
387+
out := dockerfile.Write(node)
388+
glog.V(4).Infof("Replacing dockerfile\n%s\nwith:\n%s", string(in), string(out))
389+
return overwriteFile(dockerfilePath, out)
390+
}
379391

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\nwith:\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)
382456
if err != nil {
383457
return err
384458
}
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+
}
386484
}

0 commit comments

Comments
 (0)