Kaynağa Gözat

Merge pull request #36156 from r2d4/move-shell-parser

Move dockerfile ENV shell parser functions into subpackage
Akihiro Suda 7 yıl önce
ebeveyn
işleme
421664aba1

+ 4 - 3
builder/dockerfile/builder.go

@@ -15,6 +15,7 @@ import (
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/builder/dockerfile/instructions"
 	"github.com/docker/docker/builder/dockerfile/instructions"
 	"github.com/docker/docker/builder/dockerfile/parser"
 	"github.com/docker/docker/builder/dockerfile/parser"
+	"github.com/docker/docker/builder/dockerfile/shell"
 	"github.com/docker/docker/builder/fscache"
 	"github.com/docker/docker/builder/fscache"
 	"github.com/docker/docker/builder/remotecontext"
 	"github.com/docker/docker/builder/remotecontext"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
@@ -256,8 +257,8 @@ func emitImageID(aux *streamformatter.AuxFormatter, state *dispatchState) error
 	return aux.Emit(types.BuildResult{ID: state.imageID})
 	return aux.Emit(types.BuildResult{ID: state.imageID})
 }
 }
 
 
-func processMetaArg(meta instructions.ArgCommand, shlex *ShellLex, args *buildArgs) error {
-	// ShellLex currently only support the concatenated string format
+func processMetaArg(meta instructions.ArgCommand, shlex *shell.Lex, args *buildArgs) error {
+	// shell.Lex currently only support the concatenated string format
 	envs := convertMapToEnvList(args.GetAllAllowed())
 	envs := convertMapToEnvList(args.GetAllAllowed())
 	if err := meta.Expand(func(word string) (string, error) {
 	if err := meta.Expand(func(word string) (string, error) {
 		return shlex.ProcessWord(word, envs)
 		return shlex.ProcessWord(word, envs)
@@ -283,7 +284,7 @@ func (b *Builder) dispatchDockerfileWithCancellation(parseResult []instructions.
 	for _, stage := range parseResult {
 	for _, stage := range parseResult {
 		totalCommands += len(stage.Commands)
 		totalCommands += len(stage.Commands)
 	}
 	}
-	shlex := NewShellLex(escapeToken)
+	shlex := shell.NewLex(escapeToken)
 	for _, meta := range metaArgs {
 	for _, meta := range metaArgs {
 		currentCommandIndex = printCommand(b.Stdout, currentCommandIndex, totalCommands, &meta)
 		currentCommandIndex = printCommand(b.Stdout, currentCommandIndex, totalCommands, &meta)
 
 

+ 4 - 3
builder/dockerfile/dispatchers.go

@@ -20,6 +20,7 @@ import (
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/builder/dockerfile/instructions"
 	"github.com/docker/docker/builder/dockerfile/instructions"
 	"github.com/docker/docker/builder/dockerfile/parser"
 	"github.com/docker/docker/builder/dockerfile/parser"
+	"github.com/docker/docker/builder/dockerfile/shell"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/jsonmessage"
 	"github.com/docker/docker/pkg/jsonmessage"
@@ -47,7 +48,7 @@ func dispatchEnv(d dispatchRequest, c *instructions.EnvCommand) error {
 		for i, envVar := range runConfig.Env {
 		for i, envVar := range runConfig.Env {
 			envParts := strings.SplitN(envVar, "=", 2)
 			envParts := strings.SplitN(envVar, "=", 2)
 			compareFrom := envParts[0]
 			compareFrom := envParts[0]
-			if equalEnvKeys(compareFrom, name) {
+			if shell.EqualEnvKeys(compareFrom, name) {
 				runConfig.Env[i] = newVar
 				runConfig.Env[i] = newVar
 				gotOne = true
 				gotOne = true
 				break
 				break
@@ -197,7 +198,7 @@ func dispatchTriggeredOnBuild(d dispatchRequest, triggers []string) error {
 	return nil
 	return nil
 }
 }
 
 
-func (d *dispatchRequest) getExpandedImageName(shlex *ShellLex, name string) (string, error) {
+func (d *dispatchRequest) getExpandedImageName(shlex *shell.Lex, name string) (string, error) {
 	substitutionArgs := []string{}
 	substitutionArgs := []string{}
 	for key, value := range d.state.buildArgs.GetAllMeta() {
 	for key, value := range d.state.buildArgs.GetAllMeta() {
 		substitutionArgs = append(substitutionArgs, key+"="+value)
 		substitutionArgs = append(substitutionArgs, key+"="+value)
@@ -242,7 +243,7 @@ func (d *dispatchRequest) getImageOrStage(name string) (builder.Image, error) {
 	}
 	}
 	return imageMount.Image(), nil
 	return imageMount.Image(), nil
 }
 }
-func (d *dispatchRequest) getFromImage(shlex *ShellLex, name string) (builder.Image, error) {
+func (d *dispatchRequest) getFromImage(shlex *shell.Lex, name string) (builder.Image, error) {
 	name, err := d.getExpandedImageName(shlex, name)
 	name, err := d.getExpandedImageName(shlex, name)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err

+ 2 - 1
builder/dockerfile/dispatchers_test.go

@@ -12,6 +12,7 @@ import (
 	"github.com/docker/docker/api/types/strslice"
 	"github.com/docker/docker/api/types/strslice"
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/builder/dockerfile/instructions"
 	"github.com/docker/docker/builder/dockerfile/instructions"
+	"github.com/docker/docker/builder/dockerfile/shell"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/go-connections/nat"
 	"github.com/docker/go-connections/nat"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
@@ -141,7 +142,7 @@ func TestFromWithArg(t *testing.T) {
 	cmd := &instructions.Stage{
 	cmd := &instructions.Stage{
 		BaseName: "alpine:${THETAG}",
 		BaseName: "alpine:${THETAG}",
 	}
 	}
-	err := processMetaArg(metaArg, NewShellLex('\\'), args)
+	err := processMetaArg(metaArg, shell.NewLex('\\'), args)
 
 
 	sb := newDispatchRequest(b, '\\', nil, args, newStagesBuildResults())
 	sb := newDispatchRequest(b, '\\', nil, args, newStagesBuildResults())
 	require.NoError(t, err)
 	require.NoError(t, err)

+ 0 - 6
builder/dockerfile/dispatchers_unix.go

@@ -21,9 +21,3 @@ func normalizeWorkdir(_ string, current string, requested string) (string, error
 	}
 	}
 	return requested, nil
 	return requested, nil
 }
 }
-
-// equalEnvKeys compare two strings and returns true if they are equal. On
-// Windows this comparison is case insensitive.
-func equalEnvKeys(from, to string) bool {
-	return from == to
-}

+ 0 - 6
builder/dockerfile/dispatchers_windows.go

@@ -93,9 +93,3 @@ func normalizeWorkdirWindows(current string, requested string) (string, error) {
 	// Upper-case drive letter
 	// Upper-case drive letter
 	return (strings.ToUpper(string(requested[0])) + requested[1:]), nil
 	return (strings.ToUpper(string(requested[0])) + requested[1:]), nil
 }
 }
-
-// equalEnvKeys compare two strings and returns true if they are equal. On
-// Windows this comparison is case insensitive.
-func equalEnvKeys(from, to string) bool {
-	return strings.ToUpper(from) == strings.ToUpper(to)
-}

+ 3 - 2
builder/dockerfile/evaluator.go

@@ -28,6 +28,7 @@ import (
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/builder"
 	"github.com/docker/docker/builder/dockerfile/instructions"
 	"github.com/docker/docker/builder/dockerfile/instructions"
+	"github.com/docker/docker/builder/dockerfile/shell"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/runconfig/opts"
 	"github.com/docker/docker/runconfig/opts"
@@ -187,7 +188,7 @@ func commitStage(state *dispatchState, stages *stagesBuildResults) error {
 
 
 type dispatchRequest struct {
 type dispatchRequest struct {
 	state   *dispatchState
 	state   *dispatchState
-	shlex   *ShellLex
+	shlex   *shell.Lex
 	builder *Builder
 	builder *Builder
 	source  builder.Source
 	source  builder.Source
 	stages  *stagesBuildResults
 	stages  *stagesBuildResults
@@ -196,7 +197,7 @@ type dispatchRequest struct {
 func newDispatchRequest(builder *Builder, escapeToken rune, source builder.Source, buildArgs *buildArgs, stages *stagesBuildResults) dispatchRequest {
 func newDispatchRequest(builder *Builder, escapeToken rune, source builder.Source, buildArgs *buildArgs, stages *stagesBuildResults) dispatchRequest {
 	return dispatchRequest{
 	return dispatchRequest{
 		state:   newDispatchState(buildArgs),
 		state:   newDispatchState(buildArgs),
-		shlex:   NewShellLex(escapeToken),
+		shlex:   shell.NewLex(escapeToken),
 		builder: builder,
 		builder: builder,
 		source:  source,
 		source:  source,
 		stages:  stages,
 		stages:  stages,

+ 0 - 0
builder/dockerfile/envVarTest → builder/dockerfile/shell/envVarTest


+ 9 - 0
builder/dockerfile/shell/equal_env_unix.go

@@ -0,0 +1,9 @@
+// +build !windows
+
+package shell
+
+// EqualEnvKeys compare two strings and returns true if they are equal. On
+// Windows this comparison is case insensitive.
+func EqualEnvKeys(from, to string) bool {
+	return from == to
+}

+ 9 - 0
builder/dockerfile/shell/equal_env_windows.go

@@ -0,0 +1,9 @@
+package shell
+
+import "strings"
+
+// EqualEnvKeys compare two strings and returns true if they are equal. On
+// Windows this comparison is case insensitive.
+func EqualEnvKeys(from, to string) bool {
+	return strings.ToUpper(from) == strings.ToUpper(to)
+}

+ 12 - 12
builder/dockerfile/shell_parser.go → builder/dockerfile/shell/lex.go

@@ -1,4 +1,4 @@
-package dockerfile
+package shell
 
 
 import (
 import (
 	"bytes"
 	"bytes"
@@ -9,25 +9,25 @@ import (
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 )
 )
 
 
-// ShellLex performs shell word splitting and variable expansion.
+// Lex performs shell word splitting and variable expansion.
 //
 //
-// ShellLex takes a string and an array of env variables and
+// Lex takes a string and an array of env variables and
 // process all quotes (" and ') as well as $xxx and ${xxx} env variable
 // process all quotes (" and ') as well as $xxx and ${xxx} env variable
 // tokens.  Tries to mimic bash shell process.
 // tokens.  Tries to mimic bash shell process.
 // It doesn't support all flavors of ${xx:...} formats but new ones can
 // It doesn't support all flavors of ${xx:...} formats but new ones can
 // be added by adding code to the "special ${} format processing" section
 // be added by adding code to the "special ${} format processing" section
-type ShellLex struct {
+type Lex struct {
 	escapeToken rune
 	escapeToken rune
 }
 }
 
 
-// NewShellLex creates a new ShellLex which uses escapeToken to escape quotes.
-func NewShellLex(escapeToken rune) *ShellLex {
-	return &ShellLex{escapeToken: escapeToken}
+// NewLex creates a new Lex which uses escapeToken to escape quotes.
+func NewLex(escapeToken rune) *Lex {
+	return &Lex{escapeToken: escapeToken}
 }
 }
 
 
 // ProcessWord will use the 'env' list of environment variables,
 // ProcessWord will use the 'env' list of environment variables,
 // and replace any env var references in 'word'.
 // and replace any env var references in 'word'.
-func (s *ShellLex) ProcessWord(word string, env []string) (string, error) {
+func (s *Lex) ProcessWord(word string, env []string) (string, error) {
 	word, _, err := s.process(word, env)
 	word, _, err := s.process(word, env)
 	return word, err
 	return word, err
 }
 }
@@ -39,12 +39,12 @@ func (s *ShellLex) ProcessWord(word string, env []string) (string, error) {
 // this splitting is done **after** the env var substitutions are done.
 // this splitting is done **after** the env var substitutions are done.
 // Note, each one is trimmed to remove leading and trailing spaces (unless
 // Note, each one is trimmed to remove leading and trailing spaces (unless
 // they are quoted", but ProcessWord retains spaces between words.
 // they are quoted", but ProcessWord retains spaces between words.
-func (s *ShellLex) ProcessWords(word string, env []string) ([]string, error) {
+func (s *Lex) ProcessWords(word string, env []string) ([]string, error) {
 	_, words, err := s.process(word, env)
 	_, words, err := s.process(word, env)
 	return words, err
 	return words, err
 }
 }
 
 
-func (s *ShellLex) process(word string, env []string) (string, []string, error) {
+func (s *Lex) process(word string, env []string) (string, []string, error) {
 	sw := &shellWord{
 	sw := &shellWord{
 		envs:        env,
 		envs:        env,
 		escapeToken: s.escapeToken,
 		escapeToken: s.escapeToken,
@@ -327,7 +327,7 @@ func (sw *shellWord) getEnv(name string) string {
 	for _, env := range sw.envs {
 	for _, env := range sw.envs {
 		i := strings.Index(env, "=")
 		i := strings.Index(env, "=")
 		if i < 0 {
 		if i < 0 {
-			if equalEnvKeys(name, env) {
+			if EqualEnvKeys(name, env) {
 				// Should probably never get here, but just in case treat
 				// Should probably never get here, but just in case treat
 				// it like "var" and "var=" are the same
 				// it like "var" and "var=" are the same
 				return ""
 				return ""
@@ -335,7 +335,7 @@ func (sw *shellWord) getEnv(name string) string {
 			continue
 			continue
 		}
 		}
 		compareName := env[:i]
 		compareName := env[:i]
-		if !equalEnvKeys(name, compareName) {
+		if !EqualEnvKeys(name, compareName) {
 			continue
 			continue
 		}
 		}
 		return env[i+1:]
 		return env[i+1:]

+ 3 - 3
builder/dockerfile/shell_parser_test.go → builder/dockerfile/shell/lex_test.go

@@ -1,4 +1,4 @@
-package dockerfile
+package shell
 
 
 import (
 import (
 	"bufio"
 	"bufio"
@@ -18,7 +18,7 @@ func TestShellParser4EnvVars(t *testing.T) {
 	assert.NoError(t, err)
 	assert.NoError(t, err)
 	defer file.Close()
 	defer file.Close()
 
 
-	shlex := NewShellLex('\\')
+	shlex := NewLex('\\')
 	scanner := bufio.NewScanner(file)
 	scanner := bufio.NewScanner(file)
 	envs := []string{"PWD=/home", "SHELL=bash", "KOREAN=한국어"}
 	envs := []string{"PWD=/home", "SHELL=bash", "KOREAN=한국어"}
 	for scanner.Scan() {
 	for scanner.Scan() {
@@ -70,7 +70,7 @@ func TestShellParser4Words(t *testing.T) {
 	}
 	}
 	defer file.Close()
 	defer file.Close()
 
 
-	shlex := NewShellLex('\\')
+	shlex := NewLex('\\')
 	envs := []string{}
 	envs := []string{}
 	scanner := bufio.NewScanner(file)
 	scanner := bufio.NewScanner(file)
 	lineNum := 0
 	lineNum := 0

+ 0 - 0
builder/dockerfile/wordsTest → builder/dockerfile/shell/wordsTest