suport multistage onbuild

This commit is contained in:
Dani Raznikov 2020-04-10 16:38:22 +03:00
parent 2609944787
commit 70eb7ebcfa
3 changed files with 138 additions and 6 deletions

View File

@ -87,6 +87,75 @@ func Stages(opts *config.KanikoOptions) ([]config.KanikoStage, error) {
return kanikoStages, nil
}
func ParseInstructions(opts *config.KanikoOptions) ([]instructions.Stage, []instructions.ArgCommand, error) {
var err error
var d []uint8
match, _ := regexp.MatchString("^https?://", opts.DockerfilePath)
if match {
response, e := http.Get(opts.DockerfilePath)
if e != nil {
return nil, nil, e
}
d, err = ioutil.ReadAll(response.Body)
} else {
d, err = ioutil.ReadFile(opts.DockerfilePath)
}
if err != nil {
return nil, nil, errors.Wrap(err, fmt.Sprintf("reading dockerfile at path %s", opts.DockerfilePath))
}
stages, metaArgs, err := Parse(d)
if err != nil {
return nil, nil, errors.Wrap(err, "parsing dockerfile")
}
return stages, metaArgs, nil
}
func ResolveCrossStageInstructions(stages []instructions.Stage) map[string]string {
nameToIndex := make(map[string]string)
for i, stage := range stages {
index := strconv.Itoa(i)
if stage.Name != "" {
nameToIndex[stage.Name] = index
}
ResolveCommands(stage.Commands, nameToIndex)
}
logrus.Debugf("Built stage name to index map: %v", nameToIndex)
return nameToIndex
}
func MakeKanikoStages(opts *config.KanikoOptions, stages []instructions.Stage, metaArgs []instructions.ArgCommand) ([]config.KanikoStage, error) {
targetStage, err := targetStage(stages, opts.Target)
if err != nil {
return nil, errors.Wrap(err, "Error finding target stage")
}
var kanikoStages []config.KanikoStage
for index, stage := range stages {
resolvedBaseName, err := util.ResolveEnvironmentReplacement(stage.BaseName, opts.BuildArgs, false)
if err != nil {
return nil, errors.Wrap(err, "resolving base name")
}
stage.Name = resolvedBaseName
logrus.Infof("Resolved base name %s to %s", stage.BaseName, stage.Name)
kanikoStages = append(kanikoStages, config.KanikoStage{
Stage: stage,
BaseImageIndex: baseImageIndex(index, stages),
BaseImageStoredLocally: (baseImageIndex(index, stages) != -1),
SaveStage: saveStage(index, stages),
Final: index == targetStage,
MetaArgs: metaArgs,
Index: index,
})
if index == targetStage {
break
}
}
return kanikoStages, nil
}
// baseImageIndex returns the index of the stage the current stage is built off
// returns -1 if the current stage isn't built off a previous stage
func baseImageIndex(currentStage int, stages []instructions.Stage) int {
@ -221,7 +290,6 @@ func resolveStages(stages []instructions.Stage) {
if val, ok := nameToIndex[strings.ToLower(c.From)]; ok {
c.From = val
}
}
}
}
@ -264,3 +332,18 @@ func saveStage(index int, stages []instructions.Stage) bool {
return false
}
// TODO move it to private method or put elsewhere
func ResolveCommands(cmds []instructions.Command, stageNameToIdx map[string]string) () {
for _, cmd := range cmds {
switch c := cmd.(type) {
case *instructions.CopyCommand:
if c.From != "" {
if val, ok := stageNameToIdx[strings.ToLower(c.From)]; ok {
c.From = val
}
}
}
}
}

View File

@ -25,6 +25,7 @@ import (
"github.com/GoogleContainerTools/kaniko/pkg/config"
"github.com/GoogleContainerTools/kaniko/testutil"
"github.com/moby/buildkit/frontend/dockerfile/instructions"
"github.com/sirupsen/logrus"
)
func Test_Stages_ArgValueWithQuotes(t *testing.T) {
@ -364,3 +365,30 @@ func Test_baseImageIndex(t *testing.T) {
})
}
}
func Test_ResolveStages(t *testing.T) {
in := &instructions.CopyCommand{
SourcesAndDest: []string{
"/var/bo", "foo.txt",
},
From: "boo",
Chown: "",
}
ibn := &instructions.CopyCommand{
SourcesAndDest: []string{
"/var/bo", "foo.txt",
},
From: "poo",
Chown: "",
}
foo := []instructions.Command{in, ibn}
stageMap := map[string]string{"boo": "1"}
logrus.Infof("%#v", foo)
ResolveCommands(foo, stageMap)
logrus.Infof("%#v", foo)
logrus.Infof("%#v", foo[0])
}

View File

@ -78,7 +78,7 @@ type stageBuilder struct {
}
// newStageBuilder returns a new type stageBuilder which contains all the information required to build the stage
func newStageBuilder(opts *config.KanikoOptions, stage config.KanikoStage, crossStageDeps map[int][]string, dcm map[string]string, sid map[string]string) (*stageBuilder, error) {
func newStageBuilder(opts *config.KanikoOptions, stage config.KanikoStage, crossStageDeps map[int][]string, dcm map[string]string, sid map[string]string, stageNameToIdx map[string]string) (*stageBuilder, error) {
sourceImage, err := util.RetrieveSourceImage(stage, opts)
if err != nil {
return nil, err
@ -89,7 +89,7 @@ func newStageBuilder(opts *config.KanikoOptions, stage config.KanikoStage, cross
return nil, err
}
if err := resolveOnBuild(&stage, &imageConfig.Config); err != nil {
if err := resolveOnBuild(&stage, &imageConfig.Config, stageNameToIdx); err != nil {
return nil, err
}
@ -550,8 +550,25 @@ func DoBuild(opts *config.KanikoOptions) (v1.Image, error) {
digestToCacheKey := make(map[string]string)
stageIdxToDigest := make(map[string]string)
//* dani plan
// 1. parse opts - to []instruction.Stages
// 2. resolve environment - get map stageidx-->name
// 3. convert []instruction.Stages to []config.KanikoStages
// 4. pass to newStageBuilder() the map to resolve on build
//*/
// Parse dockerfile
stages, err := dockerfile.Stages(opts)
//stages, err := dockerfile.Stages(opts)
//if err != nil {
// return nil, err
//}
instrct, metaArgs, err := dockerfile.ParseInstructions(opts)
if err != nil {
return nil, err
}
stageNameToIdx := dockerfile.ResolveCrossStageInstructions(instrct)
stages, err := dockerfile.MakeKanikoStages(opts, instrct, metaArgs)
if err != nil {
return nil, err
}
@ -571,7 +588,7 @@ func DoBuild(opts *config.KanikoOptions) (v1.Image, error) {
logrus.Infof("Built cross stage deps: %v", crossStageDependencies)
for index, stage := range stages {
sb, err := newStageBuilder(opts, stage, crossStageDependencies, digestToCacheKey, stageIdxToDigest)
sb, err := newStageBuilder(opts, stage, crossStageDependencies, digestToCacheKey, stageIdxToDigest, stageNameToIdx)
if err != nil {
return nil, err
}
@ -771,7 +788,7 @@ func getHasher(snapshotMode string) (func(string) (string, error), error) {
return nil, fmt.Errorf("%s is not a valid snapshot mode", snapshotMode)
}
func resolveOnBuild(stage *config.KanikoStage, config *v1.Config) error {
func resolveOnBuild(stage *config.KanikoStage, config *v1.Config, stageNameToIdx map[string]string) error {
if config.OnBuild == nil || len(config.OnBuild) == 0 {
return nil
}
@ -780,6 +797,10 @@ func resolveOnBuild(stage *config.KanikoStage, config *v1.Config) error {
if err != nil {
return err
}
// Iterate over commands and replace references to other stages with their index
dockerfile.ResolveCommands(cmds, stageNameToIdx)
// Append to the beginning of the commands in the stage
stage.Commands = append(cmds, stage.Commands...)
logrus.Infof("Executing %v build triggers", len(cmds))