Skip to content

Commit b129e66

Browse files
authored
Merge pull request #13 from serverscom/fix-location-path
fix location path
2 parents cb985d4 + ec6d7c4 commit b129e66

6 files changed

Lines changed: 220 additions & 92 deletions

File tree

internal/ingress/controller/store/store.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type Storer interface {
3737
ListIngress() []*networkv1.Ingress
3838
GetService(key string) (*corev1.Service, error)
3939
GetNodesIpList() []string
40-
GetIngressServiceInfo(ingress *networkv1.Ingress) (map[string]ServiceInfo, error)
40+
GetIngressHostsInfo(ingress *networkv1.Ingress) (map[string]HostInfo, error)
4141
}
4242

4343
// Store represents cache store, implements Storer
@@ -75,8 +75,8 @@ func (s *Store) GetNodesIpList() []string {
7575
}
7676

7777
// GetIngressServiceInfo returns ingress services info.
78-
func (s *Store) GetIngressServiceInfo(ingress *networkv1.Ingress) (map[string]ServiceInfo, error) {
79-
return getIngressServiceInfo(ingress, s)
78+
func (s *Store) GetIngressHostsInfo(ingress *networkv1.Ingress) (map[string]HostInfo, error) {
79+
return getIngressHostsInfo(ingress, s)
8080
}
8181

8282
type Informer struct {

internal/ingress/controller/store/store_test.go

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package store
22

33
import (
4-
"errors"
4+
"fmt"
55
"testing"
66
"time"
77

@@ -144,6 +144,7 @@ func TestGetIngressServiceInfo(t *testing.T) {
144144
}
145145
s.listers.Node.Add(node1)
146146

147+
hostname := "example.com"
147148
serviceName := "test-service"
148149
service := &corev1.Service{
149150
ObjectMeta: metav1.ObjectMeta{
@@ -172,7 +173,7 @@ func TestGetIngressServiceInfo(t *testing.T) {
172173
Spec: networkv1.IngressSpec{
173174
Rules: []networkv1.IngressRule{
174175
{
175-
Host: "example.com",
176+
Host: hostname,
176177
IngressRuleValue: networkv1.IngressRuleValue{
177178
HTTP: &networkv1.HTTPIngressRuleValue{
178179
Paths: []networkv1.HTTPIngressPath{
@@ -193,20 +194,22 @@ func TestGetIngressServiceInfo(t *testing.T) {
193194
},
194195
}
195196

196-
serviceInfo, err := s.GetIngressServiceInfo(ingress)
197+
hostsInfo, err := s.GetIngressHostsInfo(ingress)
197198
g.Expect(err).To(BeNil())
199+
g.Expect(hostsInfo[hostname].Paths).ToNot(BeEmpty())
198200

199-
g.Expect(serviceInfo).To(HaveKey(serviceName))
200-
g.Expect(serviceInfo[serviceName].Hosts).To(ContainElement("example.com"))
201-
g.Expect(serviceInfo[serviceName].NodePort).To(Equal(30000))
202-
g.Expect(serviceInfo[serviceName].NodeIps).To(ConsistOf("192.168.1.1"))
203-
g.Expect(serviceInfo[serviceName].Annotations).To(HaveKeyWithValue("key", "value"))
201+
p := hostsInfo[hostname].Paths[0]
202+
203+
g.Expect(p.Service.Name).To(Equal(serviceName))
204+
g.Expect(p.NodePort).To(Equal(30000))
205+
g.Expect(p.NodeIps).To(ConsistOf("192.168.1.1"))
206+
g.Expect(p.Service.Annotations).To(HaveKeyWithValue("key", "value"))
204207

205208
// check if service doens't have NodePort
206209
service.Spec.Ports[0].NodePort = 0
207210
s.listers.Service.Update(service)
208211

209-
serviceInfo, err = s.GetIngressServiceInfo(ingress)
210-
expectedErr := errors.New("service doesn't have NodePort, only services with type 'NodePort' or 'LoadBalancer' supported")
212+
_, err = s.GetIngressHostsInfo(ingress)
213+
expectedErr := fmt.Errorf("service %s has no NodePort (only NodePort/LoadBalancer supported)", serviceName)
211214
g.Expect(err).To(Equal(expectedErr))
212215
}

internal/ingress/controller/store/utils.go

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,57 +3,69 @@ package store
33
import (
44
"fmt"
55

6+
corev1 "k8s.io/api/core/v1"
67
networkv1 "k8s.io/api/networking/v1"
78
)
89

9-
// ServiceInfo represents helper struct for ingress service
10-
type ServiceInfo struct {
11-
Hosts []string
12-
NodeIps []string
13-
NodePort int
14-
Annotations map[string]string
10+
// PathInfo represents info about a path in the ingress controller
11+
type PathInfo struct {
12+
Path string
13+
Service *corev1.Service
14+
NodePort int
15+
NodeIps []string
1516
}
1617

17-
// GetIngressServiceInfo get services info from ingress
18-
func getIngressServiceInfo(ingress *networkv1.Ingress, store Storer) (map[string]ServiceInfo, error) {
19-
servicesInfo := make(map[string]ServiceInfo)
18+
// HostInfo represents info about a host in the ingress controller
19+
type HostInfo struct {
20+
Host string
21+
Paths []PathInfo
22+
}
23+
24+
// getIngressHostsInfo get hosts info from ingress
25+
func getIngressHostsInfo(ingress *networkv1.Ingress, store Storer) (map[string]HostInfo, error) {
26+
hostsInfo := make(map[string]HostInfo)
2027
nodeIps := store.GetNodesIpList()
2128

2229
for _, rule := range ingress.Spec.Rules {
2330
if rule.HTTP == nil {
2431
continue
2532
}
2633

34+
hInfo := hostsInfo[rule.Host]
35+
hInfo.Host = rule.Host
36+
2737
for _, path := range rule.HTTP.Paths {
28-
service, err := store.GetService(ingress.Namespace + "/" + path.Backend.Service.Name)
38+
svc, err := store.GetService(ingress.Namespace + "/" + path.Backend.Service.Name)
2939
if err != nil {
3040
return nil, fmt.Errorf("error getting service: %v", err)
3141
}
3242

33-
for _, port := range service.Spec.Ports {
43+
var nodePort int32
44+
found := false
45+
for _, port := range svc.Spec.Ports {
3446
if port.Port == path.Backend.Service.Port.Number {
35-
if port.NodePort != 0 {
36-
serviceName := path.Backend.Service.Name
37-
if _, ok := servicesInfo[serviceName]; !ok {
38-
servicesInfo[serviceName] = ServiceInfo{
39-
Hosts: []string{rule.Host},
40-
NodePort: int(port.NodePort),
41-
NodeIps: nodeIps,
42-
Annotations: service.Annotations,
43-
}
44-
} else {
45-
sTmp := servicesInfo[serviceName]
46-
sTmp.Hosts = append(sTmp.Hosts, rule.Host)
47-
servicesInfo[serviceName] = sTmp
48-
}
49-
} else {
50-
return nil, fmt.Errorf("service doesn't have NodePort, only services with type 'NodePort' or 'LoadBalancer' supported")
47+
if port.NodePort == 0 {
48+
return nil, fmt.Errorf("service %s has no NodePort (only NodePort/LoadBalancer supported)", svc.Name)
5149
}
50+
nodePort = port.NodePort
51+
found = true
5252
break
5353
}
5454
}
55+
if !found {
56+
return nil, fmt.Errorf("service %s: port %d not found", svc.Name, path.Backend.Service.Port.Number)
57+
}
58+
59+
hInfo.Paths = append(hInfo.Paths, PathInfo{
60+
Path: path.Path,
61+
Service: svc,
62+
NodePort: int(nodePort),
63+
NodeIps: nodeIps,
64+
})
5565
}
66+
67+
hostsInfo[rule.Host] = hInfo
5668
}
5769

58-
return servicesInfo, nil
70+
return hostsInfo, nil
5971
}

internal/mocks/store.go

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/service/loadbalancer/manager.go

Lines changed: 50 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -145,59 +145,77 @@ func (m *Manager) GetIds() []string {
145145
func (m *Manager) TranslateIngressToLB(ingress *networkv1.Ingress, sslCerts map[string]string) (*serverscom.L7LoadBalancerCreateInput, error) {
146146
m.lock.Lock()
147147
defer m.lock.Unlock()
148-
sInfo, err := m.store.GetIngressServiceInfo(ingress)
148+
149+
hostsInfo, err := m.store.GetIngressHostsInfo(ingress)
149150
if err != nil {
150151
return nil, err
151152
}
152153

153-
var upstreamZones []serverscom.L7UpstreamZoneInput
154-
var upstreams []serverscom.L7UpstreamInput
155154
var vhostZones []serverscom.L7VHostZoneInput
156-
var locationZones []serverscom.L7LocationZoneInput
157155

158-
for sKey, service := range sInfo {
159-
sslId := ""
160-
sslEnabled := false
156+
upstreamMap := make(map[string]serverscom.L7UpstreamZoneInput)
157+
158+
for host, hInfo := range hostsInfo {
159+
var locationZones []serverscom.L7LocationZoneInput
161160
vhostPorts := []int32{80}
162-
upstreamId := fmt.Sprintf("upstream-zone-%s", sKey)
163-
for _, ip := range service.NodeIps {
164-
upstreams = append(upstreams, serverscom.L7UpstreamInput{
165-
IP: ip,
166-
Weight: 1,
167-
Port: int32(service.NodePort),
168-
})
161+
sslEnabled := false
162+
sslId := ""
163+
164+
if id, ok := sslCerts[host]; ok {
165+
sslId = id
166+
sslEnabled = true
167+
vhostPorts = []int32{443}
169168
}
170169

171-
for _, host := range service.Hosts {
172-
if id, ok := sslCerts[host]; ok {
173-
sslId = id
174-
sslEnabled = true
175-
vhostPorts = []int32{443}
176-
}
170+
vhostAnnotations := make(map[string]string)
171+
for _, p := range hInfo.Paths {
172+
upstreamId := fmt.Sprintf("upstream-zone-%s-%d", p.Service.Name, p.NodePort)
173+
177174
locationZones = append(locationZones, serverscom.L7LocationZoneInput{
178-
Location: "/",
175+
Location: p.Path,
179176
UpstreamID: upstreamId,
180177
})
178+
179+
if _, ok := upstreamMap[upstreamId]; !ok {
180+
var ups []serverscom.L7UpstreamInput
181+
for _, ip := range p.NodeIps {
182+
ups = append(ups, serverscom.L7UpstreamInput{
183+
IP: ip,
184+
Port: int32(p.NodePort),
185+
Weight: 1,
186+
})
187+
}
188+
upstream := serverscom.L7UpstreamZoneInput{
189+
ID: upstreamId,
190+
Upstreams: ups,
191+
}
192+
upstream = *annotations.FillLBUpstreamZoneWithServiceAnnotations(&upstream, p.Service.Annotations)
193+
upstreamMap[upstreamId] = upstream
194+
}
195+
196+
// last-win strategy for vhost annotations
197+
for k, v := range p.Service.Annotations {
198+
vhostAnnotations[k] = v
199+
}
181200
}
182201

183-
vZInput := serverscom.L7VHostZoneInput{
184-
ID: fmt.Sprintf("vhost-zone-%s", sKey),
185-
Domains: service.Hosts,
202+
vz := serverscom.L7VHostZoneInput{
203+
ID: fmt.Sprintf("vhost-zone-%s", host),
204+
Domains: []string{host},
186205
SSLCertID: sslId,
187206
SSL: sslEnabled,
188207
Ports: vhostPorts,
189208
LocationZones: locationZones,
190209
}
191-
vZInput = *annotations.FillLBVHostZoneWithServiceAnnotations(&vZInput, service.Annotations)
192-
vhostZones = append(vhostZones, vZInput)
210+
vz = *annotations.FillLBVHostZoneWithServiceAnnotations(&vz, vhostAnnotations)
211+
vhostZones = append(vhostZones, vz)
212+
}
193213

194-
uZInput := serverscom.L7UpstreamZoneInput{
195-
ID: upstreamId,
196-
Upstreams: upstreams,
197-
}
198-
uZInput = *annotations.FillLBUpstreamZoneWithServiceAnnotations(&uZInput, service.Annotations)
199-
upstreamZones = append(upstreamZones, uZInput)
214+
var upstreamZones []serverscom.L7UpstreamZoneInput
215+
for _, u := range upstreamMap {
216+
upstreamZones = append(upstreamZones, u)
200217
}
218+
201219
if len(vhostZones) == 0 || len(upstreamZones) == 0 {
202220
return nil, errors.New("vhost or upstream can't be empty, can't continue")
203221
}

0 commit comments

Comments
 (0)