Skip to content

Commit 9764a70

Browse files
committed
updated e2e tests
Signed-off-by: Praful Khanduri <[email protected]> Skip static port forwards on gRPC because conflicts Signed-off-by: Praful Khanduri <[email protected]> Revert unintended changes to templates/default.yaml Signed-off-by: Praful Khanduri <[email protected]> fixed failing golangci lint err Signed-off-by: Praful Khanduri <[email protected]>
1 parent d907638 commit 9764a70

File tree

13 files changed

+362
-124
lines changed

13 files changed

+362
-124
lines changed

.github/workflows/test.yml

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -612,28 +612,3 @@ jobs:
612612
sudo chown $(whoami) /dev/kvm
613613
- name: Smoke test
614614
run: limactl start --tty=false
615-
616-
static-port-forwarding:
617-
name: "Static Port Forwarding Tests"
618-
runs-on: ubuntu-24.04
619-
timeout-minutes: 30
620-
steps:
621-
- uses: actions/checkout@v4
622-
with:
623-
fetch-depth: 1
624-
- name: Install test dependencies
625-
run: |
626-
sudo apt-get update
627-
sudo apt-get install -y --no-install-recommends ovmf qemu-system-x86 qemu-utils
628-
qemu-system-x86_64 --version
629-
sudo modprobe kvm
630-
# `sudo usermod -aG kvm $(whoami)` does not take an effect on GHA
631-
sudo chown $(whoami) /dev/kvm
632-
- name: Make
633-
run: make
634-
- name: Install
635-
run: sudo make install
636-
- name: Run plain mode static port forwarding test
637-
run: bash hack/test-plain-static-port-forward.sh
638-
- name: Run non-plain mode static port forwarding test
639-
run: bash hack/test-nonplain-static-port-forward.sh

cmd/limactl/editflags/editflags.go

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,9 @@ func RegisterCreate(cmd *cobra.Command, commentPrefix string) {
9797

9898
flags.Bool("plain", false, commentPrefix+"Plain mode. Disables mounts, port forwarding, containerd, etc.")
9999

100-
flags.StringSlice("port-forward", nil, commentPrefix+"Port forwards (host:guest), e.g., '8080:80,2222:22'")
100+
flags.StringArray("port-forward", nil, commentPrefix+"Port forwards (host:guest), e.g., '8080:80' or '9090:9090,static=true' for static port-forwards")
101101
_ = cmd.RegisterFlagCompletionFunc("port-forward", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
102-
return []string{"8080:80", "3000:3000"}, cobra.ShellCompDirectiveNoFileComp
103-
})
104-
flags.StringSlice("static-port-forward", nil, commentPrefix+"Static port forwards (host:guest), works even in plain mode, e.g., '8080:80,2222:22'")
105-
_ = cmd.RegisterFlagCompletionFunc("static-port-forward", func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
106-
return []string{"8080:80", "3000:3000"}, cobra.ShellCompDirectiveNoFileComp
102+
return []string{"8080:80", "3000:3000", "8080:80,static=true"}, cobra.ShellCompDirectiveNoFileComp
107103
})
108104
}
109105

@@ -113,6 +109,56 @@ func defaultExprFunc(expr string) func(v *flag.Flag) (string, error) {
113109
}
114110
}
115111

112+
func ParsePortForward(spec string) (hostPort, guestPort string, isStatic bool, err error) {
113+
parts := strings.Split(spec, ",")
114+
if len(parts) > 2 {
115+
return "", "", false, fmt.Errorf("invalid port forward format %q, expected HOST:GUEST or HOST:GUEST,static=true", spec)
116+
}
117+
118+
portParts := strings.Split(strings.TrimSpace(parts[0]), ":")
119+
if len(portParts) != 2 {
120+
return "", "", false, fmt.Errorf("invalid port forward format %q, expected HOST:GUEST", parts[0])
121+
}
122+
123+
hostPort = strings.TrimSpace(portParts[0])
124+
guestPort = strings.TrimSpace(portParts[1])
125+
126+
if len(parts) == 2 {
127+
staticPart := strings.TrimSpace(parts[1])
128+
if strings.HasPrefix(staticPart, "static=") {
129+
staticValue := strings.TrimPrefix(staticPart, "static=")
130+
isStatic, err = strconv.ParseBool(staticValue)
131+
if err != nil {
132+
return "", "", false, fmt.Errorf("invalid value for static parameter: %q", staticValue)
133+
}
134+
} else {
135+
return "", "", false, fmt.Errorf("invalid parameter %q, expected 'static=' followed by a boolean value", staticPart)
136+
}
137+
}
138+
139+
return hostPort, guestPort, isStatic, nil
140+
}
141+
142+
func BuildPortForwardExpression(portForwards []string) (string, error) {
143+
if len(portForwards) == 0 {
144+
return "", nil
145+
}
146+
147+
expr := `.portForwards += [`
148+
for i, spec := range portForwards {
149+
hostPort, guestPort, isStatic, err := ParsePortForward(spec)
150+
if err != nil {
151+
return "", err
152+
}
153+
expr += fmt.Sprintf(`{"guestPort": %q, "hostPort": %q, "static": %v}`, guestPort, hostPort, isStatic)
154+
if i < len(portForwards)-1 {
155+
expr += ","
156+
}
157+
}
158+
expr += `]`
159+
return expr, nil
160+
}
161+
116162
// YQExpressions returns YQ expressions.
117163
func YQExpressions(flags *flag.FlagSet, newInstance bool) ([]string, error) {
118164
type def struct {
@@ -272,31 +318,13 @@ func YQExpressions(flags *flag.FlagSet, newInstance bool) ([]string, error) {
272318
{"vm-type", d(".vmType = %q"), true, false},
273319
{"plain", d(".plain = %s"), true, false},
274320
{
275-
"static-port-forward",
321+
"port-forward",
276322
func(_ *flag.Flag) (string, error) {
277-
ss, err := flags.GetStringSlice("static-port-forward")
323+
ss, err := flags.GetStringArray("port-forward")
278324
if err != nil {
279325
return "", err
280326
}
281-
if len(ss) == 0 {
282-
return "", nil
283-
}
284-
285-
expr := `.portForwards += [`
286-
for i, s := range ss {
287-
parts := strings.Split(s, ":")
288-
if len(parts) != 2 {
289-
return "", fmt.Errorf("invalid static port forward format %q, expected HOST:GUEST", s)
290-
}
291-
guestPort := strings.TrimSpace(parts[0])
292-
hostPort := strings.TrimSpace(parts[1])
293-
expr += fmt.Sprintf(`{"guestPort": %s, "hostPort": %s}`, guestPort, hostPort)
294-
if i < len(ss)-1 {
295-
expr += ","
296-
}
297-
}
298-
expr += `]`
299-
return expr, nil
327+
return BuildPortForwardExpression(ss)
300328
},
301329
false,
302330
false,

cmd/limactl/editflags/editflags_test.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,137 @@ func TestCompleteMemoryGiB(t *testing.T) {
2323
assert.DeepEqual(t, []float32{1, 2, 4}, completeMemoryGiB(8<<30))
2424
assert.DeepEqual(t, []float32{1, 2, 4, 8, 10}, completeMemoryGiB(20<<30))
2525
}
26+
27+
func TestBuildPortForwardExpression(t *testing.T) {
28+
tests := []struct {
29+
name string
30+
portForwards []string
31+
expected string
32+
expectError bool
33+
}{
34+
{
35+
name: "empty port forwards",
36+
portForwards: []string{},
37+
expected: "",
38+
},
39+
{
40+
name: "single dynamic port forward",
41+
portForwards: []string{"8080:80"},
42+
expected: `.portForwards += [{"guestPort": "80", "hostPort": "8080", "static": false}]`,
43+
},
44+
{
45+
name: "single static port forward",
46+
portForwards: []string{"8080:80,static=true"},
47+
expected: `.portForwards += [{"guestPort": "80", "hostPort": "8080", "static": true}]`,
48+
},
49+
{
50+
name: "multiple mixed port forwards",
51+
portForwards: []string{"8080:80", "2222:22,static=true", "3000:3000"},
52+
expected: `.portForwards += [{"guestPort": "80", "hostPort": "8080", "static": false},{"guestPort": "22", "hostPort": "2222", "static": true},{"guestPort": "3000", "hostPort": "3000", "static": false}]`,
53+
},
54+
{
55+
name: "invalid format - missing colon",
56+
portForwards: []string{"8080"},
57+
expectError: true,
58+
},
59+
{
60+
name: "invalid format - too many colons",
61+
portForwards: []string{"8080:80:extra"},
62+
expectError: true,
63+
},
64+
{
65+
name: "invalid static parameter",
66+
portForwards: []string{"8080:80,invalid=true"},
67+
expectError: true,
68+
},
69+
{
70+
name: "too many parameters",
71+
portForwards: []string{"8080:80,static=true,extra=value"},
72+
expectError: true,
73+
},
74+
{
75+
name: "whitespace handling",
76+
portForwards: []string{" 8080 : 80 , static=true "},
77+
expected: `.portForwards += [{"guestPort": "80", "hostPort": "8080", "static": true}]`,
78+
},
79+
}
80+
81+
for _, tt := range tests {
82+
t.Run(tt.name, func(t *testing.T) {
83+
result, err := BuildPortForwardExpression(tt.portForwards)
84+
if tt.expectError {
85+
assert.Check(t, err != nil)
86+
} else {
87+
assert.NilError(t, err)
88+
assert.Equal(t, tt.expected, result)
89+
}
90+
})
91+
}
92+
}
93+
94+
func TestParsePortForward(t *testing.T) {
95+
tests := []struct {
96+
name string
97+
spec string
98+
hostPort string
99+
guestPort string
100+
isStatic bool
101+
expectError bool
102+
}{
103+
{
104+
name: "dynamic port forward",
105+
spec: "8080:80",
106+
hostPort: "8080",
107+
guestPort: "80",
108+
isStatic: false,
109+
},
110+
{
111+
name: "static port forward",
112+
spec: "8080:80,static=true",
113+
hostPort: "8080",
114+
guestPort: "80",
115+
isStatic: true,
116+
},
117+
{
118+
name: "whitespace handling",
119+
spec: " 8080 : 80 , static=true ",
120+
hostPort: "8080",
121+
guestPort: "80",
122+
isStatic: true,
123+
},
124+
{
125+
name: "invalid format - missing colon",
126+
spec: "8080",
127+
expectError: true,
128+
},
129+
{
130+
name: "invalid format - too many colons",
131+
spec: "8080:80:extra",
132+
expectError: true,
133+
},
134+
{
135+
name: "invalid parameter",
136+
spec: "8080:80,invalid=true",
137+
expectError: true,
138+
},
139+
{
140+
name: "too many parameters",
141+
spec: "8080:80,static=true,extra=value",
142+
expectError: true,
143+
},
144+
}
145+
146+
for _, tt := range tests {
147+
t.Run(tt.name, func(t *testing.T) {
148+
hostPort, guestPort, isStatic, err := ParsePortForward(tt.spec)
149+
if tt.expectError {
150+
assert.Check(t, err != nil)
151+
} else {
152+
assert.NilError(t, err)
153+
assert.Equal(t, tt.hostPort, hostPort)
154+
assert.Equal(t, tt.guestPort, guestPort)
155+
assert.Equal(t, tt.isStatic, isStatic)
156+
}
157+
})
158+
}
159+
}

hack/test-nonplain-static-port-forward.sh

100644100755
Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
1-
#!/bin/bash
1+
#!/usr/bin/env bash
2+
3+
# SPDX-FileCopyrightText: Copyright The Lima Authors
4+
# SPDX-License-Identifier: Apache-2.0
5+
26
set -euxo pipefail
37

48
INSTANCE=nonplain-static-port-forward
5-
TEMPLATE=hack/test-templates/nonplain-static-port-forward.yaml
9+
TEMPLATE=hack/test-templates/static-port-forward.yaml
610

711
limactl delete -f $INSTANCE || true
812

913
limactl start --name=$INSTANCE --tty=false $TEMPLATE
1014

1115
limactl shell $INSTANCE -- bash -c 'until [ -e /run/nginx.pid ]; do sleep 1; done'
16+
limactl shell $INSTANCE -- bash -c 'until systemctl is-active --quiet test-server-9080; do sleep 1; done'
17+
limactl shell $INSTANCE -- bash -c 'until systemctl is-active --quiet test-server-9070; do sleep 1; done'
1218

13-
curl -sSf http://127.0.0.1:9090 | grep -i 'nginx' && echo 'nginx port forwarding works!'
19+
curl -sSf http://127.0.0.1:9090 | grep -i 'nginx' && echo 'Static port forwarding (9090) works in normal mode!'
20+
curl -sSf http://127.0.0.1:9080 | grep -i 'Dynamic port 9080' && echo 'Dynamic port forwarding (9080) works in normal mode!'
21+
curl -sSf http://127.0.0.1:9070 | grep -i 'Dynamic port 9070' && echo 'Dynamic port forwarding (9070) works in normal mode!'
1422

15-
limactl delete -f $INSTANCE
23+
limactl delete -f $INSTANCE
24+
echo "All tests passed for normal mode - both static and dynamic ports work!"
25+
# EOF
Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,35 @@
1-
#!/bin/bash
1+
#!/usr/bin/env bash
2+
3+
# SPDX-FileCopyrightText: Copyright The Lima Authors
4+
# SPDX-License-Identifier: Apache-2.0
5+
26
set -euxo pipefail
37

48
INSTANCE=plain-static-port-forward
5-
TEMPLATE=hack/test-templates/plain-static-port-forward.yaml
9+
TEMPLATE=hack/test-templates/static-port-forward.yaml
610

711
limactl delete -f $INSTANCE || true
812

9-
limactl start --name=$INSTANCE --tty=false $TEMPLATE
13+
limactl start --name=$INSTANCE --plain=true --tty=false $TEMPLATE
1014

1115
limactl shell $INSTANCE -- bash -c 'until [ -e /run/nginx.pid ]; do sleep 1; done'
1216

13-
curl -sSf http://127.0.0.1:9090 | grep -i 'nginx' && echo 'nginx port forwarding works!'
17+
curl -sSf http://127.0.0.1:9090 | grep -i 'nginx' && echo 'Static port forwarding (9090) works in plain mode!'
18+
19+
if curl -sSf http://127.0.0.1:9080 2>/dev/null; then
20+
echo 'ERROR: Dynamic port 9080 should not be forwarded in plain mode!'
21+
exit 1
22+
else
23+
echo 'Dynamic port 9080 is correctly NOT forwarded in plain mode.'
24+
fi
1425

15-
if curl -sSf http://127.0.0.1:9080; then
16-
echo 'ERROR: Port 9080 should not be forwarded in plain mode!'
17-
exit 1
26+
if curl -sSf http://127.0.0.1:9070 2>/dev/null; then
27+
echo 'ERROR: Dynamic port 9070 should not be forwarded in plain mode!'
28+
exit 1
1829
else
19-
echo 'Port 9080 is correctly NOT forwarded in plain mode.'
30+
echo 'Dynamic port 9070 is correctly NOT forwarded in plain mode.'
2031
fi
2132

22-
limactl delete -f $INSTANCE
33+
limactl delete -f $INSTANCE
34+
echo "All tests passed for plain mode - only static ports work!"
35+
# EOF

hack/test-templates.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ declare -A CHECKS=(
5050
["snapshot-online"]=""
5151
["snapshot-offline"]=""
5252
["port-forwards"]="1"
53+
["static-port-forwards"]=""
5354
["vmnet"]=""
5455
["disk"]=""
5556
["user-v2"]=""
@@ -90,6 +91,12 @@ case "$NAME" in
9091
CHECKS["param-env-variables"]="1"
9192
CHECKS["set-user"]="1"
9293
;;
94+
"static-port-forward")
95+
CHECKS["static-port-forwards"]="1"
96+
CHECKS["port-forwards"]=""
97+
CHECKS["container-engine"]=""
98+
CHECKS["restart"]=""
99+
;;
93100
"docker")
94101
CONTAINER_ENGINE="docker"
95102
;;
@@ -383,6 +390,13 @@ if [[ -n ${CHECKS["port-forwards"]} ]]; then
383390
set +x
384391
fi
385392

393+
if [[ -n ${CHECKS["static-port-forwards"]} ]]; then
394+
INFO "Testing static port forwarding functionality"
395+
"${scriptdir}/test-plain-static-port-forward.sh" "$NAME"
396+
"${scriptdir}/test-nonplain-static-port-forward.sh" "$NAME"
397+
INFO "All static port forwarding tests passed!"
398+
fi
399+
386400
if [[ -n ${CHECKS["vmnet"]} ]]; then
387401
INFO "Testing vmnet functionality"
388402
guestip="$(limactl shell "$NAME" ip -4 -j addr show dev lima0 | jq -r '.[0].addr_info[0].local')"

0 commit comments

Comments
 (0)