Because rkt pod runs after plugin.SetUpPod() is called, so getRunningPods() does not return the newly created pod, which causes the hostport iptable rules to be missing for this new pod.
233 lines
5.8 KiB
Go
233 lines
5.8 KiB
Go
/*
|
|
Copyright 2016 The Kubernetes Authors All rights reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package hostport
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
"testing"
|
|
|
|
"k8s.io/kubernetes/pkg/api"
|
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
|
utiliptables "k8s.io/kubernetes/pkg/util/iptables"
|
|
)
|
|
|
|
type fakeSocket struct {
|
|
port int32
|
|
protocol string
|
|
closed bool
|
|
}
|
|
|
|
func (f *fakeSocket) Close() error {
|
|
if f.closed {
|
|
return fmt.Errorf("Socket %q.%s already closed!", f.port, f.protocol)
|
|
}
|
|
f.closed = true
|
|
return nil
|
|
}
|
|
|
|
func openFakeSocket(hp *hostport) (closeable, error) {
|
|
return &fakeSocket{hp.port, hp.protocol, false}, nil
|
|
}
|
|
|
|
type ruleMatch struct {
|
|
hostport int
|
|
chain string
|
|
match string
|
|
}
|
|
|
|
func TestOpenPodHostports(t *testing.T) {
|
|
fakeIptables := NewFakeIptables()
|
|
|
|
h := &handler{
|
|
hostPortMap: make(map[hostport]closeable),
|
|
iptables: fakeIptables,
|
|
portOpener: openFakeSocket,
|
|
}
|
|
|
|
tests := []struct {
|
|
pod *api.Pod
|
|
ip string
|
|
matches []*ruleMatch
|
|
}{
|
|
// New pod that we are going to add
|
|
{
|
|
&api.Pod{
|
|
ObjectMeta: api.ObjectMeta{
|
|
Name: "test-pod",
|
|
Namespace: api.NamespaceDefault,
|
|
},
|
|
Spec: api.PodSpec{
|
|
Containers: []api.Container{{
|
|
Ports: []api.ContainerPort{{
|
|
HostPort: 4567,
|
|
ContainerPort: 80,
|
|
Protocol: api.ProtocolTCP,
|
|
}, {
|
|
HostPort: 5678,
|
|
ContainerPort: 81,
|
|
Protocol: api.ProtocolUDP,
|
|
}},
|
|
}},
|
|
},
|
|
},
|
|
"10.1.1.2",
|
|
[]*ruleMatch{
|
|
{
|
|
-1,
|
|
"KUBE-HOSTPORTS",
|
|
"-m comment --comment \"test-pod_default hostport 4567\" -m tcp -p tcp --dport 4567",
|
|
},
|
|
{
|
|
4567,
|
|
"",
|
|
"-m comment --comment \"test-pod_default hostport 4567\" -s 10.1.1.2/32 -j KUBE-MARK-MASQ",
|
|
},
|
|
{
|
|
4567,
|
|
"",
|
|
"-m comment --comment \"test-pod_default hostport 4567\" -m tcp -p tcp -j DNAT --to-destination 10.1.1.2:80",
|
|
},
|
|
{
|
|
-1,
|
|
"KUBE-HOSTPORTS",
|
|
"-m comment --comment \"test-pod_default hostport 5678\" -m udp -p udp --dport 5678",
|
|
},
|
|
{
|
|
5678,
|
|
"",
|
|
"-m comment --comment \"test-pod_default hostport 5678\" -s 10.1.1.2/32 -j KUBE-MARK-MASQ",
|
|
},
|
|
{
|
|
5678,
|
|
"",
|
|
"-m comment --comment \"test-pod_default hostport 5678\" -m udp -p udp -j DNAT --to-destination 10.1.1.2:81",
|
|
},
|
|
},
|
|
},
|
|
// Already running pod
|
|
{
|
|
&api.Pod{
|
|
ObjectMeta: api.ObjectMeta{
|
|
Name: "another-test-pod",
|
|
Namespace: api.NamespaceDefault,
|
|
},
|
|
Spec: api.PodSpec{
|
|
Containers: []api.Container{{
|
|
Ports: []api.ContainerPort{{
|
|
HostPort: 123,
|
|
ContainerPort: 654,
|
|
Protocol: api.ProtocolTCP,
|
|
}},
|
|
}},
|
|
},
|
|
},
|
|
"10.1.1.5",
|
|
[]*ruleMatch{
|
|
{
|
|
-1,
|
|
"KUBE-HOSTPORTS",
|
|
"-m comment --comment \"another-test-pod_default hostport 123\" -m tcp -p tcp --dport 123",
|
|
},
|
|
{
|
|
123,
|
|
"",
|
|
"-m comment --comment \"another-test-pod_default hostport 123\" -s 10.1.1.5/32 -j KUBE-MARK-MASQ",
|
|
},
|
|
{
|
|
123,
|
|
"",
|
|
"-m comment --comment \"another-test-pod_default hostport 123\" -m tcp -p tcp -j DNAT --to-destination 10.1.1.5:654",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
runningPods := make([]*RunningPod, 0)
|
|
|
|
// Fill in any match rules missing chain names
|
|
for _, test := range tests {
|
|
for _, match := range test.matches {
|
|
if match.hostport >= 0 {
|
|
found := false
|
|
for _, c := range test.pod.Spec.Containers {
|
|
for _, cp := range c.Ports {
|
|
if int(cp.HostPort) == match.hostport {
|
|
match.chain = string(hostportChainName(cp, kubecontainer.GetPodFullName(test.pod)))
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatalf("Failed to find ContainerPort for match %d/'%s'", match.hostport, match.match)
|
|
}
|
|
}
|
|
}
|
|
runningPods = append(runningPods, &RunningPod{
|
|
Pod: test.pod,
|
|
IP: net.ParseIP(test.ip),
|
|
})
|
|
}
|
|
|
|
err := h.OpenPodHostportsAndSync(&RunningPod{Pod: tests[0].pod, IP: net.ParseIP(tests[0].ip)}, "br0", runningPods)
|
|
if err != nil {
|
|
t.Fatalf("Failed to OpenPodHostportsAndSync: %v", err)
|
|
}
|
|
|
|
// Generic rules
|
|
genericRules := []*ruleMatch{
|
|
{-1, "POSTROUTING", "-m comment --comment \"SNAT for localhost access to hostports\" -o br0 -s 127.0.0.0/8 -j MASQUERADE"},
|
|
{-1, "PREROUTING", "-m comment --comment \"kube hostport portals\" -m addrtype --dst-type LOCAL -j KUBE-HOSTPORTS"},
|
|
{-1, "OUTPUT", "-m comment --comment \"kube hostport portals\" -m addrtype --dst-type LOCAL -j KUBE-HOSTPORTS"},
|
|
}
|
|
|
|
for _, rule := range genericRules {
|
|
_, chain, err := fakeIptables.getChain(utiliptables.TableNAT, utiliptables.Chain(rule.chain))
|
|
if err != nil {
|
|
t.Fatalf("Expected NAT chain %s did not exist", rule.chain)
|
|
}
|
|
if !matchRule(chain, rule.match) {
|
|
t.Fatalf("Expected %s chain rule match '%s' not found", rule.chain, rule.match)
|
|
}
|
|
}
|
|
|
|
// Pod rules
|
|
for _, test := range tests {
|
|
for _, match := range test.matches {
|
|
// Ensure chain exists
|
|
_, chain, err := fakeIptables.getChain(utiliptables.TableNAT, utiliptables.Chain(match.chain))
|
|
if err != nil {
|
|
t.Fatalf("Expected NAT chain %s did not exist", match.chain)
|
|
}
|
|
if !matchRule(chain, match.match) {
|
|
t.Fatalf("Expected NAT chain %s rule containing '%s' not found", match.chain, match.match)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func matchRule(chain *fakeChain, match string) bool {
|
|
for _, rule := range chain.rules {
|
|
if strings.Contains(rule, match) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|