diff --git a/builder/vmware/common/driver_parser.go b/builder/vmware/common/driver_parser.go index 58292f736..d080b307a 100644 --- a/builder/vmware/common/driver_parser.go +++ b/builder/vmware/common/driver_parser.go @@ -704,6 +704,7 @@ func parseParameter(val tkParameter) (pParameter, error) { } address := net.ParseIP(cidr[0]) + bits, err := strconv.Atoi(cidr[1]) if err != nil { return nil, err @@ -1038,10 +1039,12 @@ func (e *configDeclaration) IP4() (net.IP, error) { return nil, fmt.Errorf("No IP4 addresses found") } + // Try and parse it as an IP4. If so, then it's good to return it as-is. if res := net.ParseIP(result[0]); res != nil { return res, nil } + // Otherwise make an attempt to resolve it to an address. res, err := net.ResolveIPAddr("ip4", result[0]) if err != nil { return nil, err @@ -1069,10 +1072,12 @@ func (e *configDeclaration) IP6() (net.IP, error) { return nil, fmt.Errorf("No IP6 addresses found") } + // If we were able to parse it into an IP, then we can just return it. if res := net.ParseIP(result[0]); res != nil { return res, nil } + // Otherwise, try to resolve it into an address. res, err := net.ResolveIPAddr("ip6", result[0]) if err != nil { return nil, err @@ -1277,8 +1282,6 @@ func tokenizeNetworkingConfig(in chan byte) chan string { } switch by { - case '\r': - fallthrough case '\t': fallthrough case ' ': @@ -1290,16 +1293,20 @@ func tokenizeNetworkingConfig(in chan byte) chan string { out <- state state = "" + case '\r': + // If windows has tampered with our newlines, then we normalize + // things by converting its value from 0x0d to 0x0a. + fallthrough case '\n': // Newlines can repeat, so this case is responsible for writing - // to the chan, and consolidating multiple newlines into singles. + // to the chan, and consolidating multiple newlines into a single. if repeat_newline { continue } if len(state) > 0 { out <- state } - out <- string(by) + out <- "\n" state = "" repeat_newline = true continue @@ -1317,6 +1324,7 @@ func tokenizeNetworkingConfig(in chan byte) chan string { if len(state) > 0 { out <- state } + close(out) }(out) return out } @@ -1355,6 +1363,7 @@ func splitNetworkingConfig(in chan string) chan []string { if len(row) > 0 { out <- row } + close(out) }(out) return out } @@ -1698,7 +1707,7 @@ func parseNetworkingCommand_add_dhcp_mac_to_ip(row []string) (*networkingCommand } ip := net.ParseIP(row[2]) - if ip != nil { + if ip == nil { return nil, fmt.Errorf("Unable to parse third argument as ipv4. : %v", row[2]) } @@ -1850,6 +1859,7 @@ func parseNetworkingConfig(rows chan []string) chan networkingCommandEntry { out <- *entry } } + close(out) }(rows, out) return out } @@ -2197,7 +2207,7 @@ This consumes bytes within a pair of some bytes, like parentheses, brackets, bra We start by reading bytes until we encounter openByte. These will be returned as the first parameter. Then we can enter a goro and consume bytes until we get to -closeByte. At that point we're done, and suicide. +closeByte. At that point we're done, and we can exit. **/ func consumeOpenClosePair(openByte, closeByte byte, in chan byte) ([]byte, chan byte) { result := make([]byte, 0) diff --git a/builder/vmware/common/driver_parser_test.go b/builder/vmware/common/driver_parser_test.go index 1b9a066a8..01abffdbb 100644 --- a/builder/vmware/common/driver_parser_test.go +++ b/builder/vmware/common/driver_parser_test.go @@ -7,6 +7,7 @@ import ( "encoding/hex" "os" "path/filepath" + "strings" ) func consumeString(s string) (out chan byte) { @@ -20,6 +21,13 @@ func consumeString(s string) (out chan byte) { return } +func collectIntoStringList(in chan string) (result []string) { + for item := range in { + result = append(result, item) + } + return +} + func uncommentFromString(s string) string { inCh := consumeString(s) out := uncomment(inCh) @@ -856,3 +864,223 @@ func TestParserReadDhcpdLeases(t *testing.T) { } } } + +func TestParserTokenizeNetworkingConfig(t *testing.T) { + tests := []string{ + "words words words", + "newlines\n\n\n\n\n\n\n\nnewlines\r\r\r\r\r\r\r\rnewlines\n\n\n\n", + " newline-less", + } + expects := [][]string{ + []string{"words", "words", "words"}, + []string{"newlines", "\n", "newlines", "\n", "newlines", "\n"}, + []string{"newline-less"}, + } + + for testnum := 0; testnum < len(tests); testnum += 1 { + inCh := consumeString(tests[testnum]) + outCh := tokenizeNetworkingConfig(inCh) + result := collectIntoStringList(outCh) + + expected := expects[testnum] + if len(result) != len(expected) { + t.Errorf("test %d expected %d items, got %d instead", 1+testnum, len(expected), len(result)) + continue + } + + ok := true + for index := 0; index < len(expected); index += 1 { + if result[index] != expected[index] { + ok = false + } + } + if !ok { + t.Errorf("test %d expected %#v, got %#v", 1+testnum, expects[testnum], result) + } + } +} + +func TestParserSplitNetworkingConfig(t *testing.T) { + tests := []string{ + "this is a story\n\n\nabout some newlines", + "\n\n\nthat can begin and end with newlines\n\n\n", + " in\n\n\nsome\ncases\nit\ncan\nend\nwith\nan\nempty\nstring\n\n\n\n", + "\n\n\nand\nbegin\nwith\nan\nempty\nstring ", + } + expects := [][]string{ + []string{"this is a story", "about some newlines"}, + []string{"that can begin and end with newlines"}, + []string{"in", "some", "cases", "it", "can", "end", "with", "an", "empty", "string"}, + []string{"and", "begin", "with", "an", "empty", "string"}, + } + + for testnum := 0; testnum < len(tests); testnum += 1 { + inCh := consumeString(tests[testnum]) + stringCh := tokenizeNetworkingConfig(inCh) + outCh := splitNetworkingConfig(stringCh) + + result := make([]string, 0) + for item := range outCh { + result = append(result, strings.Join(item, " ")) + } + + expected := expects[testnum] + if len(result) != len(expected) { + t.Errorf("test %d expected %d items, got %d instead", 1+testnum, len(expected), len(result)) + continue + } + + ok := true + for index := 0; index < len(expected); index += 1 { + if result[index] != expected[index] { + ok = false + } + } + if !ok { + t.Errorf("test %d expected %#v, got %#v", 1+testnum, expects[testnum], result) + } + } +} + +func TestParserParseNetworkingConfigVersion(t *testing.T) { + success_tests := []string{"VERSION=4,2"} + failure_tests := []string{ + "VERSION=1=2", + "VERSION=3,4,5", + "VERSION=a,b", + } + + for testnum := 0; testnum < len(success_tests); testnum += 1 { + test := []string{success_tests[testnum]} + if _, err := networkingReadVersion(test); err != nil { + t.Errorf("success-test %d parsing failed: %v", 1+testnum, err) + } + } + + for testnum := 0; testnum < len(success_tests); testnum += 1 { + test := []string{failure_tests[testnum]} + if _, err := networkingReadVersion(test); err == nil { + t.Errorf("failure-test %d should have failed", 1+testnum) + } + } +} + +func TestParserParseNetworkingConfigEntries(t *testing.T) { + tests := []string{ + "answer VNET_999_ANYTHING option", + "remove_answer VNET_123_ALSOANYTHING", + "add_nat_portfwd 24 udp 42 127.0.0.1 24", + "remove_nat_portfwd 42 tcp 2502", + "add_dhcp_mac_to_ip 57005 00:0d:0e:0a:0d:00 127.0.0.2", + "remove_dhcp_mac_to_ip 57005 00:0d:0e:0a:0d:00", + "add_bridge_mapping string 51", + "remove_bridge_mapping string", + "add_nat_prefix 57005 /24", + "remove_nat_prefix 57005 /31", + } + + for testnum := 0; testnum < len(tests); testnum += 1 { + test := strings.Split(tests[testnum], " ") + parser := NetworkingParserByCommand(test[0]) + if parser == nil { + t.Errorf("test %d unable to parse command: %#v", 1+testnum, test) + continue + } + operand_parser := *parser + + _, err := operand_parser(test[1:]) + if err != nil { + t.Errorf("test %d unable to parse command parameters %#v: %v", 1+testnum, test, err) + } + } +} + +func TestParserReadNetworingConfig(t *testing.T) { + expected_answer_vnet_1 := map[string]string{ + "DHCP": "yes", + "DHCP_CFG_HASH": "01F4CE0D79A1599698B6E5814CCB68058BB0ED5E", + "HOSTONLY_NETMASK": "255.255.255.0", + "HOSTONLY_SUBNET": "192.168.70.0", + "NAT": "no", + "VIRTUAL_ADAPTER": "yes", + } + + f, err := os.Open(filepath.Join("testdata", "networking-example")) + if err != nil { + t.Fatalf("Unable to open networking-example sample: %v", err) + } + defer f.Close() + + config, err := ReadNetworkingConfig(f) + if err != nil { + t.Fatalf("error parsing networking-example: %v", err) + } + + if vnet, ok := config.answer[1]; ok { + for ans_key := range expected_answer_vnet_1 { + result, ok := vnet[ans_key] + if !ok { + t.Errorf("unable to find key %s in VNET_%d answer", ans_key, 1) + continue + } + + if result != expected_answer_vnet_1[ans_key] { + t.Errorf("expected key %s for VNET_%d to be %v, got %v", ans_key, 1, expected_answer_vnet_1[ans_key], result) + } + } + + } else { + t.Errorf("unable to find VNET_%d answer", 1) + } + + expected_answer_vnet_8 := map[string]string{ + "DHCP": "yes", + "DHCP_CFG_HASH": "C30F14F65A0FE4B5DCC6C67497D7A8A33E5E538C", + "HOSTONLY_NETMASK": "255.255.255.0", + "HOSTONLY_SUBNET": "172.16.41.0", + "NAT": "yes", + "VIRTUAL_ADAPTER": "yes", + } + + if vnet, ok := config.answer[8]; ok { + for ans_key := range expected_answer_vnet_8 { + result, ok := vnet[ans_key] + if !ok { + t.Errorf("unable to find key %s in VNET_%d answer", ans_key, 8) + continue + } + + if result != expected_answer_vnet_8[ans_key] { + t.Errorf("expected key %s for VNET_%d to be %v, got %v", ans_key, 8, expected_answer_vnet_8[ans_key], result) + } + } + + } else { + t.Errorf("unable to find VNET_%d answer", 8) + } + + expected_nat_portfwd_8 := map[string]string{ + "tcp/2200": "172.16.41.129:3389", + "tcp/2201": "172.16.41.129:3389", + "tcp/2222": "172.16.41.129:22", + "tcp/3389": "172.16.41.131:3389", + "tcp/55985": "172.16.41.129:5985", + "tcp/55986": "172.16.41.129:5986", + } + + if vnet, ok := config.nat_portfwd[8-1]; ok { + for nat_key := range expected_nat_portfwd_8 { + result, ok := vnet[nat_key] + if !ok { + t.Errorf("unable to find key %s in VNET_%d nat_portfwd", nat_key, 8) + continue + } + + if result != expected_nat_portfwd_8[nat_key] { + t.Errorf("expected key %s for VNET_%d to be %v, got %v", nat_key, 8, expected_nat_portfwd_8[nat_key], result) + } + } + } else { + t.Errorf("unable to find VNET_%d answer", 8-1) + } +} diff --git a/builder/vmware/common/testdata/networking-example b/builder/vmware/common/testdata/networking-example new file mode 100644 index 000000000..7117ac8f6 --- /dev/null +++ b/builder/vmware/common/testdata/networking-example @@ -0,0 +1,19 @@ +VERSION=1,0 +answer VNET_1_DHCP yes +answer VNET_1_DHCP_CFG_HASH 01F4CE0D79A1599698B6E5814CCB68058BB0ED5E +answer VNET_1_HOSTONLY_NETMASK 255.255.255.0 +answer VNET_1_HOSTONLY_SUBNET 192.168.70.0 +answer VNET_1_NAT no +answer VNET_1_VIRTUAL_ADAPTER yes +answer VNET_8_DHCP yes +answer VNET_8_DHCP_CFG_HASH C30F14F65A0FE4B5DCC6C67497D7A8A33E5E538C +answer VNET_8_HOSTONLY_NETMASK 255.255.255.0 +answer VNET_8_HOSTONLY_SUBNET 172.16.41.0 +answer VNET_8_NAT yes +answer VNET_8_VIRTUAL_ADAPTER yes +add_nat_portfwd 8 tcp 2200 172.16.41.129 3389 +add_nat_portfwd 8 tcp 2201 172.16.41.129 3389 +add_nat_portfwd 8 tcp 2222 172.16.41.129 22 +add_nat_portfwd 8 tcp 3389 172.16.41.131 3389 +add_nat_portfwd 8 tcp 55985 172.16.41.129 5985 +add_nat_portfwd 8 tcp 55986 172.16.41.129 5986