-
Notifications
You must be signed in to change notification settings - Fork 195
/
service.go
220 lines (177 loc) · 6.06 KB
/
service.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Cilium
package tests
import (
"context"
"fmt"
corev1 "k8s.io/api/core/v1"
"github.com/cilium/cilium-cli/connectivity/check"
)
// PodToService sends an HTTP request from all client Pods
// to all Services in the test context.
func PodToService(opts ...Option) check.Scenario {
options := &labelsOption{}
for _, opt := range opts {
opt(options)
}
return &podToService{
sourceLabels: options.sourceLabels,
destinationLabels: options.destinationLabels,
}
}
// podToService implements a Scenario.
type podToService struct {
sourceLabels map[string]string
destinationLabels map[string]string
}
func (s *podToService) Name() string {
return "pod-to-service"
}
func (s *podToService) Run(ctx context.Context, t *check.Test) {
var i int
ct := t.Context()
for _, pod := range ct.ClientPods() {
pod := pod // copy to avoid memory aliasing when using reference
if !hasAllLabels(pod, s.sourceLabels) {
continue
}
for _, svc := range ct.EchoServices() {
if !hasAllLabels(svc, s.destinationLabels) {
continue
}
t.NewAction(s, fmt.Sprintf("curl-%d", i), &pod, svc, check.IPFamilyAny).Run(func(a *check.Action) {
a.ExecInPod(ctx, ct.CurlCommand(svc, check.IPFamilyAny))
a.ValidateFlows(ctx, pod, a.GetEgressRequirements(check.FlowParameters{
DNSRequired: true,
AltDstPort: svc.Port(),
}))
})
i++
}
}
}
// PodToRemoteNodePort sends an HTTP request from all client Pods
// to all echo Services' NodePorts, but only to other nodes.
func PodToRemoteNodePort() check.Scenario {
return &podToRemoteNodePort{}
}
// podToRemoteNodePort implements a Scenario.
type podToRemoteNodePort struct{}
func (s *podToRemoteNodePort) Name() string {
return "pod-to-remote-nodeport"
}
func (s *podToRemoteNodePort) Run(ctx context.Context, t *check.Test) {
var i int
for _, pod := range t.Context().ClientPods() {
pod := pod // copy to avoid memory aliasing when using reference
for _, svc := range t.Context().EchoServices() {
for _, node := range t.Context().Nodes() {
node := node // copy to avoid memory aliasing when using reference
remote := true
for _, addr := range node.Status.Addresses {
if pod.Pod.Status.HostIP == addr.Address {
remote = false
break
}
}
if !remote {
continue
}
// If src and dst pod are running on different nodes,
// call the Cilium Pod's host IP on the service's NodePort.
curlNodePort(ctx, s, t, fmt.Sprintf("curl-%d", i), &pod, svc, node)
i++
}
}
}
}
// PodToLocalNodePort sends an HTTP request from all client Pods
// to all echo Services' NodePorts, but only on the same node as
// the client Pods.
func PodToLocalNodePort() check.Scenario {
return &podToLocalNodePort{}
}
// podToLocalNodePort implements a Scenario.
type podToLocalNodePort struct{}
func (s *podToLocalNodePort) Name() string {
return "pod-to-local-nodeport"
}
func (s *podToLocalNodePort) Run(ctx context.Context, t *check.Test) {
var i int
for _, pod := range t.Context().ClientPods() {
pod := pod // copy to avoid memory aliasing when using reference
for _, svc := range t.Context().EchoServices() {
for _, node := range t.Context().Nodes() {
node := node // copy to avoid memory aliasing when using reference
for _, addr := range node.Status.Addresses {
if pod.Pod.Status.HostIP == addr.Address {
// If src and dst pod are running on the same node,
// call the Cilium Pod's host IP on the service's NodePort.
curlNodePort(ctx, s, t, fmt.Sprintf("curl-%d", i), &pod, svc, node)
i++
}
}
}
}
}
}
func curlNodePort(ctx context.Context, s check.Scenario, t *check.Test,
name string, pod *check.Pod, svc check.Service, node *corev1.Node) {
// Get the NodePort allocated to the Service.
np := uint32(svc.Service.Spec.Ports[0].NodePort)
t.ForEachIPFamily(func(ipFam check.IPFamily) {
for _, addr := range node.Status.Addresses {
if check.GetIPFamily(addr.Address) != ipFam {
continue
}
// On GKE ExternalIP is not reachable from inside a cluster
if addr.Type == corev1.NodeExternalIP {
if f, ok := t.Context().Feature(check.FeatureFlavor); ok && f.Enabled && f.Mode == "gke" {
continue
}
}
// TODO(brb):
// Disable outside to nodeport via IPv6 when IPsec is enabled until
// https://github.com/cilium/cilium/issues/23461 has been resolved.
if check.GetIPFamily(addr.Address) == check.IPFamilyV6 {
if f, ok := t.Context().Feature(check.FeatureEncryptionPod); ok && f.Enabled && f.Mode == "ipsec" {
continue
}
}
// Manually construct an HTTP endpoint to override the destination IP
// and port of the request.
ep := check.HTTPEndpoint(name, fmt.Sprintf("%s://%s:%d%s", svc.Scheme(), addr.Address, np, svc.Path()))
// Create the Action with the original svc as this will influence what the
// flow matcher looks for in the flow logs.
t.NewAction(s, name, pod, svc, check.IPFamilyAny).Run(func(a *check.Action) {
a.ExecInPod(ctx, t.Context().CurlCommand(ep, check.IPFamilyAny))
a.ValidateFlows(ctx, pod, a.GetEgressRequirements(check.FlowParameters{
// The fact that curl is hitting the NodePort instead of the
// backend Pod's port is specified here. This will cause the matcher
// to accept both the NodePort and the ClusterIP (container) port.
AltDstPort: np,
}))
})
}
})
}
// OutsideToNodePort sends an HTTP request from client pod running on a node w/o
// Cilium to NodePort services.
func OutsideToNodePort() check.Scenario {
return &outsideToNodePort{}
}
type outsideToNodePort struct{}
func (s *outsideToNodePort) Name() string {
return "outside-to-nodeport"
}
func (s *outsideToNodePort) Run(ctx context.Context, t *check.Test) {
clientPod := t.Context().HostNetNSPodsByNode()[t.NodesWithoutCilium()[0]]
i := 0
for _, svc := range t.Context().EchoServices() {
for _, node := range t.Context().Nodes() {
node := node // copy to avoid memory aliasing when using reference
curlNodePort(ctx, s, t, fmt.Sprintf("curl-%d", i), &clientPod, svc, node)
i++
}
}
}