From cf2423112123da94b5f89559978b267efbd7c710 Mon Sep 17 00:00:00 2001 From: Priya Wadhwa Date: Tue, 20 Mar 2018 17:15:09 -0700 Subject: [PATCH] WIP --- pkg/commands/env.go | 24 +++++--------- pkg/commands/env_test.go | 37 +++++++++++++++++++++ pkg/commands/expose.go | 39 +++++++++++++--------- pkg/commands/expose_test.go | 27 +++++++++++++-- pkg/util/command_util.go | 29 ++++++++++++++-- pkg/util/command_util_test.go | 62 +++++++++++++++++++++++++++++++++++ 6 files changed, 181 insertions(+), 37 deletions(-) diff --git a/pkg/commands/env.go b/pkg/commands/env.go index f7f004940..23fbfd658 100644 --- a/pkg/commands/env.go +++ b/pkg/commands/env.go @@ -17,11 +17,9 @@ limitations under the License. package commands import ( - "bytes" + "github.com/GoogleCloudPlatform/k8s-container-builder/pkg/util" "github.com/containers/image/manifest" "github.com/docker/docker/builder/dockerfile/instructions" - "github.com/docker/docker/builder/dockerfile/parser" - "github.com/docker/docker/builder/dockerfile/shell" "github.com/sirupsen/logrus" "os" "strings" @@ -33,26 +31,20 @@ type EnvCommand struct { func (e *EnvCommand) ExecuteCommand(config *manifest.Schema2Config) error { logrus.Info("cmd: ENV") - // The dockerfile/shell package handles processing env values - // It handles escape characters and supports expansion from the config.Env array - // Shlex handles some of the following use cases (these and more are tested in integration tests) - // ""a'b'c"" -> "a'b'c" - // "Rex\ The\ Dog \" -> "Rex The Dog" - // "a\"b" -> "a"b" + envString := envToString(e.cmd) - p, err := parser.Parse(bytes.NewReader([]byte(envString))) - if err != nil { - return err - } - shlex := shell.NewLex(p.EscapeToken) newEnvs := e.cmd.Env for index, pair := range newEnvs { - expandedValue, err := shlex.ProcessWord(pair.Value, config.Env) + expandedKey, err := util.ResolveEnvironmentReplacement(envString, pair.Key, config.Env) + if err != nil { + return err + } + expandedValue, err := util.ResolveEnvironmentReplacement(envString, pair.Value, config.Env) if err != nil { return err } newEnvs[index] = instructions.KeyValuePair{ - Key: pair.Key, + Key: expandedKey, Value: expandedValue, } logrus.Infof("Setting environment variable %s=%s", pair.Key, expandedValue) diff --git a/pkg/commands/env_test.go b/pkg/commands/env_test.go index f0cccdaa4..c7c767059 100644 --- a/pkg/commands/env_test.go +++ b/pkg/commands/env_test.go @@ -71,3 +71,40 @@ func TestEnvToString(t *testing.T) { actualString := envToString(envCmd) testutil.CheckErrorAndDeepEqual(t, false, nil, expectedString, actualString) } + +func Test_EnvExecute(t *testing.T) { + cfg := &manifest.Schema2Config{ + Env: []string{ + "path=/usr/", + "home=/root", + }, + } + + envCmd := &EnvCommand{ + &instructions.EnvCommand{ + Env: []instructions.KeyValuePair{ + { + Key: "path", + Value: "/some/path", + }, + { + Key: "HOME", + Value: "$home", + }, + { + Key: "$path", + Value: "$home", + }, + }, + }, + } + + expectedEnvs := []string{ + "path=/some/path", + "home=/root", + "HOME=/root", + "/usr/=/root", + } + err := envCmd.ExecuteCommand(cfg) + testutil.CheckErrorAndDeepEqual(t, false, err, expectedEnvs, cfg.Env) +} diff --git a/pkg/commands/expose.go b/pkg/commands/expose.go index daf4dc781..883a4cd35 100644 --- a/pkg/commands/expose.go +++ b/pkg/commands/expose.go @@ -18,6 +18,7 @@ package commands import ( "fmt" + "github.com/GoogleCloudPlatform/k8s-container-builder/pkg/util" "github.com/containers/image/manifest" "github.com/docker/docker/builder/dockerfile/instructions" "github.com/sirupsen/logrus" @@ -29,25 +30,16 @@ type ExposeCommand struct { } func (r *ExposeCommand) ExecuteCommand(config *manifest.Schema2Config) error { - return updateExposedPorts(r.cmd.Ports, config) -} - -func validProtocol(protocol string) bool { - validProtocols := [2]string{"tcp", "udp"} - for _, p := range validProtocols { - if protocol == p { - return true - } - } - return false -} - -func updateExposedPorts(ports []string, config *manifest.Schema2Config) error { // Grab the currently exposed ports existingPorts := config.ExposedPorts - + exposeString := r.exposeToString() // Add any new ones in - for _, p := range ports { + for _, p := range r.cmd.Ports { + // Resolve any environment variables + p, err := util.ResolveEnvironmentReplacement(exposeString, p, config.Env) + if err != nil { + return err + } // Add the default protocol if one isn't specified if !strings.Contains(p, "/") { p = p + "/tcp" @@ -64,6 +56,21 @@ func updateExposedPorts(ports []string, config *manifest.Schema2Config) error { return nil } +func validProtocol(protocol string) bool { + validProtocols := [2]string{"tcp", "udp"} + for _, p := range validProtocols { + if protocol == p { + return true + } + } + return false +} + +func (r *ExposeCommand) exposeToString() string { + expose := []string{"EXPOSE"} + return strings.Join(append(expose, r.cmd.Ports...), " ") +} + func (r *ExposeCommand) FilesToSnapshot() []string { return []string{} } diff --git a/pkg/commands/expose_test.go b/pkg/commands/expose_test.go index fc7fb2c0e..ad23cca25 100644 --- a/pkg/commands/expose_test.go +++ b/pkg/commands/expose_test.go @@ -19,6 +19,7 @@ package commands import ( "github.com/GoogleCloudPlatform/k8s-container-builder/testutil" "github.com/containers/image/manifest" + "github.com/docker/docker/builder/dockerfile/instructions" "testing" ) @@ -27,6 +28,10 @@ func TestUpdateExposedPorts(t *testing.T) { ExposedPorts: manifest.Schema2PortSet{ "8080/tcp": {}, }, + Env: []string{ + "port=udp", + "num=8085", + }, } ports := []string{ @@ -34,6 +39,15 @@ func TestUpdateExposedPorts(t *testing.T) { "8081/tcp", "8082", "8083/udp", + "8084/$port", + "$num", + "$num/$port", + } + + exposeCmd := &ExposeCommand{ + &instructions.ExposeCommand{ + Ports: ports, + }, } expectedPorts := manifest.Schema2PortSet{ @@ -41,9 +55,12 @@ func TestUpdateExposedPorts(t *testing.T) { "8081/tcp": {}, "8082/tcp": {}, "8083/udp": {}, + "8084/udp": {}, + "8085/tcp": {}, + "8085/udp": {}, } - err := updateExposedPorts(ports, cfg) + err := exposeCmd.ExecuteCommand(cfg) testutil.CheckErrorAndDeepEqual(t, false, err, expectedPorts, cfg.ExposedPorts) } @@ -56,6 +73,12 @@ func TestInvalidProtocol(t *testing.T) { "80/garbage", } - err := updateExposedPorts(ports, cfg) + exposeCmd := &ExposeCommand{ + &instructions.ExposeCommand{ + Ports: ports, + }, + } + + err := exposeCmd.ExecuteCommand(cfg) testutil.CheckErrorAndDeepEqual(t, true, err, nil, nil) } diff --git a/pkg/util/command_util.go b/pkg/util/command_util.go index b904a4987..de10fed42 100644 --- a/pkg/util/command_util.go +++ b/pkg/util/command_util.go @@ -16,8 +16,31 @@ limitations under the License. package util -// ResolvEnvironmentReplacement resolves replacing env variables in fp from envs -// Ex: fp = $foo/newdir, envs = [foo=/foodir], then this should return /foodir/newdir -func ResolveEnvironmentReplacement(fp string, envs []string) string { +import ( + "bytes" + "github.com/docker/docker/builder/dockerfile/parser" + "github.com/docker/docker/builder/dockerfile/shell" + "path/filepath" +) +// ResolveEnvironmentReplacement resolves replacing env variables in some text from envs +// It takes in a string representation of the command, the value to be resolved, and a list of envs (config.Env) +// Ex: fp = $foo/newdir, envs = [foo=/foodir], then this should return /foodir/newdir +// The dockerfile/shell package handles processing env values +// It handles escape characters and supports expansion from the config.Env array +// Shlex handles some of the following use cases (these and more are tested in integration tests) +// ""a'b'c"" -> "a'b'c" +// "Rex\ The\ Dog \" -> "Rex The Dog" +// "a\"b" -> "a"b" +func ResolveEnvironmentReplacement(command, value string, envs []string) (string, error) { + p, err := parser.Parse(bytes.NewReader([]byte(command))) + if err != nil { + return "", err + } + shlex := shell.NewLex(p.EscapeToken) + processedWord, err := shlex.ProcessWord(value, envs) + if err != nil { + return "", err + } + return filepath.Clean(processedWord), nil } diff --git a/pkg/util/command_util_test.go b/pkg/util/command_util_test.go index 3a65ebf87..59cdc2454 100644 --- a/pkg/util/command_util_test.go +++ b/pkg/util/command_util_test.go @@ -15,3 +15,65 @@ limitations under the License. */ package util + +import ( + "github.com/GoogleCloudPlatform/k8s-container-builder/testutil" + "testing" +) + +var testEnvReplacement = []struct { + path string + command string + envs []string + expectedPath string +}{ + { + path: "/simple/path", + command: "WORKDIR /simple/path", + envs: []string{ + "simple=/path/", + }, + expectedPath: "/simple/path", + }, + { + path: "${a}/b", + command: "WORKDIR ${a}/b", + envs: []string{ + "a=/path/", + "b=/path2/", + }, + expectedPath: "/path/b", + }, + { + path: "/$a/b", + command: "COPY ${a}/b /c/", + envs: []string{ + "a=/path/", + "b=/path2/", + }, + expectedPath: "/path/b", + }, + { + path: "\\$foo", + command: "COPY \\$foo /quux", + envs: []string{ + "foo=/path/", + }, + expectedPath: "$foo", + }, + { + path: "8080/$protocol", + command: "EXPOSE 8080/$protocol", + envs: []string{ + "protocol=udp", + }, + expectedPath: "8080/udp", + }, +} + +func Test_EnvReplacement(t *testing.T) { + for _, test := range testEnvReplacement { + actualPath, err := ResolveEnvironmentReplacement(test.command, test.path, test.envs) + testutil.CheckErrorAndDeepEqual(t, false, err, test.expectedPath, actualPath) + } +}