github.com/rootless-containers/rootlesskit/v2@v2.3.4/pkg/port/portutil/portutil_test.go (about) 1 package portutil 2 3 import ( 4 "reflect" 5 "testing" 6 7 "github.com/rootless-containers/rootlesskit/v2/pkg/port" 8 "gotest.tools/v3/assert" 9 ) 10 11 func TestParsePortSpec(t *testing.T) { 12 type testCase struct { 13 s string 14 // nil for invalid string 15 expected *port.Spec 16 } 17 testCases := []testCase{ 18 { 19 s: "127.0.0.1:8080:80/tcp", 20 expected: &port.Spec{ 21 Proto: "tcp", 22 ParentIP: "127.0.0.1", 23 ParentPort: 8080, 24 ChildPort: 80, 25 }, 26 }, 27 { 28 s: "127.0.0.1:8080:80/tcp4", 29 expected: &port.Spec{ 30 Proto: "tcp4", 31 ParentIP: "127.0.0.1", 32 ParentPort: 8080, 33 ChildPort: 80, 34 }, 35 }, 36 { 37 s: "127.0.0.1:8080:10.0.2.100:80/tcp", 38 expected: &port.Spec{ 39 Proto: "tcp", 40 ParentIP: "127.0.0.1", 41 ParentPort: 8080, 42 ChildIP: "10.0.2.100", 43 ChildPort: 80, 44 }, 45 }, 46 { 47 s: "bad", 48 }, 49 { 50 s: "127.0.0.1:8080:80/tcp,127.0.0.1:4040:40/tcp", 51 // one entry per one string 52 }, 53 { 54 s: "8080", 55 // future version may support short formats like this 56 }, 57 { 58 s: "[::1]:8080:80/tcp", 59 expected: &port.Spec{ 60 Proto: "tcp", 61 ParentIP: "::1", 62 ParentPort: 8080, 63 ChildPort: 80, 64 }, 65 }, 66 { 67 s: "[::1]:8080:[::2]:80/udp", 68 expected: &port.Spec{ 69 Proto: "udp", 70 ParentIP: "::1", 71 ParentPort: 8080, 72 ChildIP: "::2", 73 ChildPort: 80, 74 }, 75 }, 76 } 77 for _, tc := range testCases { 78 tc := tc 79 t.Run(tc.s, func(t *testing.T) { 80 got, err := ParsePortSpec(tc.s) 81 if tc.expected == nil { 82 if err == nil { 83 t.Fatalf("error is expected for %q", tc.s) 84 } 85 } else { 86 if err != nil { 87 t.Fatalf("got error for %q: %v", tc.s, err) 88 } 89 if !reflect.DeepEqual(got, tc.expected) { 90 t.Fatalf("expected %+v, got %+v", tc.expected, got) 91 } 92 } 93 }) 94 } 95 } 96 97 func TestValidatePortSpec(t *testing.T) { 98 existingPorts := make(map[int]*port.Status) 99 100 // bind to all host IPs 101 existingPorts[1] = &port.Status{ 102 ID: 1, 103 Spec: port.Spec{ 104 Proto: "tcp", 105 ParentIP: "", 106 ParentPort: 80, 107 ChildPort: 80, 108 }, 109 } 110 // bind to only host IP 10.10.10.10 111 existingPorts[2] = &port.Status{ 112 ID: 2, 113 Spec: port.Spec{ 114 Proto: "tcp", 115 ParentIP: "10.10.10.10", 116 ParentPort: 8080, 117 ChildPort: 8080, 118 }, 119 } 120 // avoid typing the spec over and over for small changes 121 spec := port.Spec{ 122 Proto: "tcp", 123 ParentIP: "127.0.0.1", 124 ParentPort: 1001, 125 ChildPort: 1001, 126 } 127 128 // proto must be supplied and must equal "udp" or "tcp" 129 invalidProtos := []string{"", "NaN", "TCP"} 130 validProtos := []string{"udp", "tcp", "sctp"} 131 for _, p := range invalidProtos { 132 s := spec 133 s.Proto = p 134 err := ValidatePortSpec(s, existingPorts) 135 assert.ErrorContains(t, err, "unknown proto") 136 } 137 for _, p := range validProtos { 138 s := spec 139 s.Proto = p 140 err := ValidatePortSpec(s, existingPorts) 141 assert.NilError(t, err) 142 143 } 144 145 s := port.Spec{Proto: "tcp", ParentIP: "invalid", ParentPort: 80, ChildPort: 80} 146 assert.ErrorContains(t, ValidatePortSpec(s, existingPorts), "invalid ParentIP") 147 148 s = port.Spec{Proto: "tcp", ParentPort: 80, ChildIP: "invalid", ChildPort: 80} 149 assert.ErrorContains(t, ValidatePortSpec(s, existingPorts), "invalid ChildIP") 150 151 invalidPorts := []int{-200, 0, 1000000} 152 validPorts := []int{20, 500, 1337, 65000} 153 154 // 0 < parentPort <= 65535 155 for _, p := range invalidPorts { 156 s := spec 157 s.ParentPort = p 158 err := ValidatePortSpec(s, existingPorts) 159 assert.ErrorContains(t, err, "invalid ParentPort") 160 } 161 for _, p := range validPorts { 162 s := spec 163 s.ParentPort = p 164 err := ValidatePortSpec(s, existingPorts) 165 assert.NilError(t, err) 166 } 167 168 // 0 < childPort <= 65535 169 for _, p := range invalidPorts { 170 s := spec 171 s.ChildPort = p 172 err := ValidatePortSpec(s, existingPorts) 173 assert.ErrorContains(t, err, "invalid ChildPort") 174 } 175 for _, p := range validPorts { 176 s := spec 177 s.ChildPort = p 178 err := ValidatePortSpec(s, existingPorts) 179 assert.NilError(t, err) 180 } 181 182 // ChildPorts can overlap so long as parent port/IPs don't 183 // existing ports include tcp 10.10.10.10:8080, tcp *:80, no udp 184 185 // udp doesn't conflict with tcp 186 s = port.Spec{Proto: "udp", ParentPort: 80, ChildPort: 80} 187 assert.NilError(t, ValidatePortSpec(s, existingPorts)) 188 189 // same parent, same child, different IP has no conflict 190 s = port.Spec{Proto: "tcp", ParentIP: "10.10.10.11", ParentPort: 8080, ChildPort: 8080} 191 assert.NilError(t, ValidatePortSpec(s, existingPorts)) 192 193 // same IP different parentPort, same child port has no conflict 194 s = port.Spec{Proto: "tcp", ParentIP: "10.10.10.10", ParentPort: 8081, ChildPort: 8080} 195 assert.NilError(t, ValidatePortSpec(s, existingPorts)) 196 197 // Same parent IP and Port should conflict, even if child port different 198 // conflict with ID 1: 199 s = port.Spec{Proto: "tcp", ParentPort: 80, ChildPort: 90} 200 err := ValidatePortSpec(s, existingPorts) 201 assert.Error(t, err, "conflict with ID 1") 202 203 // conflict with ID 2 204 s = port.Spec{Proto: "tcp", ParentIP: "10.10.10.10", ParentPort: 8080, ChildPort: 8080} 205 err = ValidatePortSpec(s, existingPorts) 206 assert.Error(t, err, "conflict with ID 2") 207 }