From 32d999dc28c2f0cc69169c9d2908081687bccee5 Mon Sep 17 00:00:00 2001 From: teddylear Date: Sat, 11 Feb 2023 19:33:11 -0500 Subject: [PATCH 1/3] feat: Adding chdir option similar to terraform so packer can run files in another directory --- main.go | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++- main_test.go | 53 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 060bf12278b..d42d9940354 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ import ( "math/rand" "os" "runtime" + "strings" "sync" "syscall" "time" @@ -123,6 +124,52 @@ func realMain() int { return 0 } +func extractChdirOption(args []string) (string, []string, error) { + if len(args) == 0 { + return "", args, nil + } + + const argName = "-chdir" + const argPrefix = argName + "=" + var argValue string + var argPos int + + for i, arg := range args { + if !strings.HasPrefix(arg, "-") { + // Because the chdir option is a subcommand-agnostic one, we require + // it to appear before any subcommand argument, so if we find a + // non-option before we find -chdir then we are finished. + break + } + if arg == argName || arg == argPrefix { + return "", args, fmt.Errorf("must include an equals sign followed by a directory path, like -chdir=example") + } + if strings.HasPrefix(arg, argPrefix) { + argPos = i + argValue = arg[len(argPrefix):] + } + } + + // When we fall out here, we'll have populated argValue with a non-empty + // string if the -chdir=... option was present and valid, or left it + // empty if it wasn't present. + if argValue == "" { + return "", args, nil + } + + // If we did find the option then we'll need to produce a new args that + // doesn't include it anymore. + if argPos == 0 { + // Easy case: we can just slice off the front + return argValue, args[1:], nil + } + // Otherwise we need to construct a new array and copy to it. + newArgs := make([]string, len(args)-1) + copy(newArgs, args[:argPos]) + copy(newArgs[argPos:], args[argPos+1:]) + return argValue, newArgs, nil +} + // wrappedMain is called only when we're wrapped by panicwrap and // returns the exit status to exit with. func wrappedMain() int { @@ -188,9 +235,27 @@ func wrappedMain() int { } log.Printf("[INFO] Setting cache directory: %s", cacheDir) + // Below is same as terraform's chdir setup + // https://github.com/hashicorp/terraform/blob/main/main.go + // The arguments can begin with a -chdir option to ask Terraform to switch + // to a different working directory for the rest of its work. If that + // option is present then extractChdirOption returns a trimmed args with that option removed. + overrideWd, args, err := extractChdirOption(os.Args[1:]) + if err != nil { + fmt.Fprintf(os.Stdout, "%s Invalid -chdir option: \n\n%s\n", ErrorPrefix, err) + return 1 + } + if overrideWd != "" { + err := os.Chdir(overrideWd) + if err != nil { + fmt.Fprintf(os.Stdout, "%s Error handling -chdir option: \n\n%s\n", ErrorPrefix, err) + return 1 + } + } + // Determine if we're in machine-readable mode by mucking around with // the arguments... - args, machineReadable := extractMachineReadable(os.Args[1:]) + args, machineReadable := extractMachineReadable(args) defer packer.CleanupClients() diff --git a/main_test.go b/main_test.go index f4d44bbf120..bf18e07b004 100644 --- a/main_test.go +++ b/main_test.go @@ -33,6 +33,59 @@ func TestExcludeHelpFunc(t *testing.T) { } } +func TestExtractChdir(t *testing.T) { + cases := []struct { + desc, overrideWd string + args, expected []string + err error + }{ + { + desc: "TestHappyPath", + args: []string{"-chdir=example", "foo", "bar"}, + expected: []string{"foo", "bar"}, + overrideWd: "example", + err: nil, + }, + { + desc: "TestEmptyArgs", + args: []string{}, + expected: []string{}, + overrideWd: "", + err: nil, + }, + { + desc: "TestNoChdirArg", + args: []string{"foo", "bar"}, + expected: []string{"foo", "bar"}, + overrideWd: "", + err: nil, + }, + { + desc: "TestChdirNotFirst", + args: []string{"foo", "-chdir=example"}, + expected: []string{"foo", "-chdir=example"}, + overrideWd: "", + err: nil, + }, + } + + for _, tc := range cases { + overrideWd, args, err := extractChdirOption(tc.args) + if overrideWd != tc.overrideWd { + t.Fatalf("%s: bad overrideWd, expected: %s got: %s for args: %#v", + tc.desc, tc.overrideWd, overrideWd, tc.args) + } + + if !reflect.DeepEqual(args, tc.expected) { + t.Fatalf("%s: bad result args, expected: %#v, got: %#v for args: %#v", tc.desc, tc.expected, args, tc.args) + } + + if err != tc.err { + t.Fatalf("%s: bad err, expected: %s, got: %s for args: %#v", tc.desc, tc.err, err, tc.args) + } + } +} + func TestExtractMachineReadable(t *testing.T) { var args, expected, result []string var mr bool From 0fe82bb690427c342f8a89051ad01d3113586eb7 Mon Sep 17 00:00:00 2001 From: teddylear Date: Wed, 8 Mar 2023 17:35:37 -0500 Subject: [PATCH 2/3] fix: add break statement for chdir option --- main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/main.go b/main.go index d42d9940354..7ae7b4e10df 100644 --- a/main.go +++ b/main.go @@ -147,6 +147,7 @@ func extractChdirOption(args []string) (string, []string, error) { if strings.HasPrefix(arg, argPrefix) { argPos = i argValue = arg[len(argPrefix):] + break } } From 47103ba826e38ad2992bb1aa37519ce6ae2946b4 Mon Sep 17 00:00:00 2001 From: teddylear Date: Wed, 8 Mar 2023 17:48:10 -0500 Subject: [PATCH 3/3] fix: format --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 7ae7b4e10df..572e08f85e7 100644 --- a/main.go +++ b/main.go @@ -147,7 +147,7 @@ func extractChdirOption(args []string) (string, []string, error) { if strings.HasPrefix(arg, argPrefix) { argPos = i argValue = arg[len(argPrefix):] - break + break } }