Skip to content

Commit 4a90f42

Browse files
rohanKanojiamanusa
authored andcommitted
fix (kubernetes-client) : URLFromIngressImpl doesn't consider Ingress in networking.k8s.io apiGroup (#4906)
URLFromIngressImpl should handle `networking.k8s.io/v1` Ingress well. Signed-off-by: Rohan Kumar <[email protected]>
1 parent dfa920e commit 4a90f42

File tree

7 files changed

+832
-61
lines changed

7 files changed

+832
-61
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
* Fix #4863: default HttpClient retry logic to 10 attempts
3939
* Fix #4865: (java-generator) performance improvements
4040
* Fix #4873: Update all samples in `kubernetes-examples/` module to use up to date code
41+
* Fix #4906: URLFromIngressImpl considers Ingress in `networking.k8s.io` apiGroup while resolving Ingress
4142

4243
#### Dependency Upgrade
4344
* Fix #4655: Upgrade Fabric8 Kubernetes Model to Kubernetes v1.26.0

kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/URLFromEnvVarsImpl.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import org.slf4j.Logger;
2424
import org.slf4j.LoggerFactory;
2525

26+
import static io.fabric8.kubernetes.client.utils.KubernetesResourceUtil.getOrCreateAnnotations;
27+
2628
public class URLFromEnvVarsImpl implements ServiceToURLProvider {
2729
public static final Logger logger = LoggerFactory.getLogger(URLFromEnvVarsImpl.class);
2830

@@ -38,7 +40,7 @@ public String getURL(Service service, String portName, String namespace, Kuberne
3840
if (!serviceHost.isEmpty() && !servicePort.isEmpty() && !serviceProtocol.isEmpty()) {
3941
return serviceProtocol + "://" + serviceHost + ":" + servicePort;
4042
} else {
41-
String answer = URLFromServiceUtil.getOrCreateAnnotations(service).get(ANNOTATION_EXPOSE_URL);
43+
String answer = getOrCreateAnnotations(service).get(ANNOTATION_EXPOSE_URL);
4244
if (answer != null && !answer.isEmpty()) {
4345
return answer;
4446
}

kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/URLFromIngressImpl.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import io.fabric8.kubernetes.api.model.Service;
2020
import io.fabric8.kubernetes.api.model.ServicePort;
21-
import io.fabric8.kubernetes.api.model.extensions.*;
2221
import io.fabric8.kubernetes.client.KubernetesClient;
2322
import io.fabric8.kubernetes.client.ServiceToURLProvider;
2423
import io.fabric8.kubernetes.client.utils.internal.URLFromServiceUtil;
@@ -33,9 +32,18 @@ public String getURL(Service service, String portName, String namespace, Kuberne
3332
throw new RuntimeException("Couldn't find port: " + portName + " for service " + service.getMetadata().getName());
3433
}
3534

36-
IngressList ingresses = client.extensions().ingresses().inNamespace(namespace).list();
37-
if (ingresses != null && !ingresses.getItems().isEmpty()) {
38-
return URLFromServiceUtil.getURLFromIngressList(ingresses.getItems(), namespace, serviceName, port);
35+
if (client.supports(io.fabric8.kubernetes.api.model.extensions.Ingress.class)) {
36+
io.fabric8.kubernetes.api.model.extensions.IngressList ingresses = client.extensions().ingresses().inNamespace(namespace)
37+
.list();
38+
if (ingresses != null && !ingresses.getItems().isEmpty()) {
39+
return URLFromServiceUtil.getURLFromExtensionsV1beta1IngressList(ingresses.getItems(), namespace, serviceName, port);
40+
}
41+
} else if (client.supports(io.fabric8.kubernetes.api.model.networking.v1.Ingress.class)) {
42+
io.fabric8.kubernetes.api.model.networking.v1.IngressList ingresses = client.network().v1().ingresses()
43+
.inNamespace(namespace).list();
44+
if (ingresses != null && !ingresses.getItems().isEmpty()) {
45+
return URLFromServiceUtil.getURLFromNetworkingV1IngressList(ingresses.getItems(), namespace, serviceName, port);
46+
}
3947
}
4048
return null;
4149
}

kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/internal/URLFromServiceUtil.java

Lines changed: 82 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,19 @@
1616

1717
package io.fabric8.kubernetes.client.utils.internal;
1818

19-
import io.fabric8.kubernetes.api.model.*;
20-
import io.fabric8.kubernetes.api.model.extensions.*;
19+
import io.fabric8.kubernetes.api.model.IntOrString;
20+
import io.fabric8.kubernetes.api.model.Service;
21+
import io.fabric8.kubernetes.api.model.ServicePort;
2122
import io.fabric8.kubernetes.client.utils.URLUtils;
2223
import org.slf4j.Logger;
2324
import org.slf4j.LoggerFactory;
2425

25-
import java.util.LinkedHashMap;
2626
import java.util.List;
2727
import java.util.Locale;
28-
import java.util.Map;
2928
import java.util.Objects;
3029

30+
import static io.fabric8.kubernetes.client.utils.KubernetesResourceUtil.getNamespace;
31+
3132
public class URLFromServiceUtil {
3233
public static final Logger logger = LoggerFactory.getLogger(URLFromServiceUtil.class);
3334
public static final String DEFAULT_PROTO = "tcp";
@@ -36,7 +37,6 @@ public class URLFromServiceUtil {
3637
private static final String PROTO_SUFFIX = "_TCP_PROTO";
3738

3839
private URLFromServiceUtil() {
39-
throw new IllegalStateException("Utility class");
4040
}
4141

4242
public static String resolveHostFromEnvVarOrSystemProperty(String serviceName) {
@@ -65,33 +65,13 @@ public static String resolveProtocolFromEnvVarOrSystemProperty(String serviceNam
6565
DEFAULT_PROTO);
6666
}
6767

68-
public static Map<String, String> getOrCreateAnnotations(HasMetadata entity) {
69-
ObjectMeta metadata = getOrCreateMetadata(entity);
70-
Map<String, String> answer = metadata.getAnnotations();
71-
if (answer == null) {
72-
// use linked so the annotations can be in the FIFO order
73-
answer = new LinkedHashMap<>();
74-
metadata.setAnnotations(answer);
75-
}
76-
return answer;
77-
}
78-
79-
public static ObjectMeta getOrCreateMetadata(HasMetadata entity) {
80-
ObjectMeta metadata = entity.getMetadata();
81-
if (metadata == null) {
82-
metadata = new ObjectMeta();
83-
entity.setMetadata(metadata);
84-
}
85-
return metadata;
86-
}
87-
8868
public static String resolvePortFromEnvVarOrSystemProperty(String serviceName, String portName) {
8969
String envVarName = toServicePortEnvironmentVariable(serviceName, portName);
9070
return getEnvVarOrSystemProperty(envVarName, "");
9171
}
9272

9373
public static String toServicePortEnvironmentVariable(String serviceName, String portName) {
94-
String name = serviceName + PORT_SUFFIX + (portName.isEmpty() ? "_" + portName : "");
74+
String name = serviceName + PORT_SUFFIX + (!portName.isEmpty() ? "_" + portName : "");
9575
return toEnvVariable(name);
9676
}
9777

@@ -103,22 +83,73 @@ public static String toEnvVariable(String serviceName) {
10383
return serviceName.toUpperCase(Locale.ROOT).replaceAll("-", "_");
10484
}
10585

106-
public static String getURLFromIngressList(List<Ingress> ingressList, String namespace, String serviceName,
86+
public static String getURLFromExtensionsV1beta1IngressList(
87+
List<io.fabric8.kubernetes.api.model.extensions.Ingress> ingressList, String namespace, String serviceName,
88+
ServicePort port) {
89+
for (io.fabric8.kubernetes.api.model.extensions.Ingress item : ingressList) {
90+
String ns = getNamespace(item);
91+
if (Objects.equals(ns, namespace) && item.getSpec() != null) {
92+
String url = getURLFromIngressSpec(item.getSpec(), serviceName, port);
93+
if (url != null) {
94+
return url;
95+
}
96+
}
97+
}
98+
return null;
99+
}
100+
101+
public static String getURLFromNetworkingV1IngressList(
102+
List<io.fabric8.kubernetes.api.model.networking.v1.Ingress> ingressList, String namespace, String serviceName,
107103
ServicePort port) {
108-
for (Ingress item : ingressList) {
104+
for (io.fabric8.kubernetes.api.model.networking.v1.Ingress item : ingressList) {
109105
String ns = getNamespace(item);
110106
if (Objects.equals(ns, namespace) && item.getSpec() != null) {
111-
return getURLFromIngressSpec(item.getSpec(), serviceName, port);
107+
String url = getURLFromNetworkV1IngressSpec(item.getSpec(), serviceName, port);
108+
if (url != null) {
109+
return url;
110+
}
112111
}
113112
}
114113
return null;
115114
}
116115

117-
public static String getURLFromIngressSpec(IngressSpec spec, String serviceName, ServicePort port) {
118-
List<IngressRule> ingressRules = spec.getRules();
116+
public static String getURLFromNetworkV1IngressSpec(io.fabric8.kubernetes.api.model.networking.v1.IngressSpec spec,
117+
String serviceName, ServicePort port) {
118+
List<io.fabric8.kubernetes.api.model.networking.v1.IngressRule> ingressRules = spec.getRules();
119119
if (ingressRules != null && !ingressRules.isEmpty()) {
120-
for (IngressRule rule : ingressRules) {
121-
HTTPIngressRuleValue http = rule.getHttp();
120+
for (io.fabric8.kubernetes.api.model.networking.v1.IngressRule rule : ingressRules) {
121+
io.fabric8.kubernetes.api.model.networking.v1.HTTPIngressRuleValue http = rule.getHttp();
122+
if (http != null && http.getPaths() != null) {
123+
return getURLFromNetworkV1IngressRules(http.getPaths(), spec, serviceName, port, rule);
124+
}
125+
}
126+
}
127+
return null;
128+
}
129+
130+
public static String getURLFromNetworkV1IngressRules(
131+
List<io.fabric8.kubernetes.api.model.networking.v1.HTTPIngressPath> paths,
132+
io.fabric8.kubernetes.api.model.networking.v1.IngressSpec spec, String serviceName,
133+
ServicePort port, io.fabric8.kubernetes.api.model.networking.v1.IngressRule rule) {
134+
for (io.fabric8.kubernetes.api.model.networking.v1.HTTPIngressPath path : paths) {
135+
io.fabric8.kubernetes.api.model.networking.v1.IngressBackend backend = path.getBackend();
136+
if (backend != null) {
137+
String backendServiceName = backend.getService().getName();
138+
if (serviceName.equals(backendServiceName)
139+
&& portsMatch(port, new IntOrString(backend.getService().getPort().getNumber()))) {
140+
return getURLFromIngressBackend(spec.getTls() != null && !spec.getTls().isEmpty(), path.getPath(), rule.getHost());
141+
}
142+
}
143+
}
144+
return null;
145+
}
146+
147+
public static String getURLFromIngressSpec(io.fabric8.kubernetes.api.model.extensions.IngressSpec spec, String serviceName,
148+
ServicePort port) {
149+
List<io.fabric8.kubernetes.api.model.extensions.IngressRule> ingressRules = spec.getRules();
150+
if (ingressRules != null && !ingressRules.isEmpty()) {
151+
for (io.fabric8.kubernetes.api.model.extensions.IngressRule rule : ingressRules) {
152+
io.fabric8.kubernetes.api.model.extensions.HTTPIngressRuleValue http = rule.getHttp();
122153
if (http != null && http.getPaths() != null) {
123154
return getURLFromIngressRules(http.getPaths(), spec, serviceName, port, rule);
124155
}
@@ -127,30 +158,33 @@ public static String getURLFromIngressSpec(IngressSpec spec, String serviceName,
127158
return null;
128159
}
129160

130-
public static String getURLFromIngressRules(List<HTTPIngressPath> paths, IngressSpec spec, String serviceName,
131-
ServicePort port, IngressRule rule) {
132-
for (HTTPIngressPath path : paths) {
133-
IngressBackend backend = path.getBackend();
161+
public static String getURLFromIngressRules(List<io.fabric8.kubernetes.api.model.extensions.HTTPIngressPath> paths,
162+
io.fabric8.kubernetes.api.model.extensions.IngressSpec spec, String serviceName,
163+
ServicePort port, io.fabric8.kubernetes.api.model.extensions.IngressRule rule) {
164+
for (io.fabric8.kubernetes.api.model.extensions.HTTPIngressPath path : paths) {
165+
io.fabric8.kubernetes.api.model.extensions.IngressBackend backend = path.getBackend();
134166
if (backend != null) {
135167
String backendServiceName = backend.getServiceName();
136168
if (serviceName.equals(backendServiceName) && portsMatch(port, backend.getServicePort())) {
137-
String pathPostFix = path.getPath();
138-
if (spec.getTls() != null) {
139-
return getURLFromTLSHost(rule, pathPostFix);
140-
}
141-
String answer = rule.getHost();
142-
if (answer != null && !answer.isEmpty()) {
143-
pathPostFix = fixPathPostFixIfEmpty(pathPostFix);
144-
return "http://" + URLUtils.pathJoin(answer, pathPostFix);
145-
}
169+
return getURLFromIngressBackend(spec.getTls() != null && !spec.getTls().isEmpty(), path.getPath(), rule.getHost());
146170
}
147171
}
148172
}
149173
return null;
150174
}
151175

152-
public static String getURLFromTLSHost(IngressRule rule, String pathPostFix) {
153-
String host = rule.getHost();
176+
private static String getURLFromIngressBackend(boolean tlsProvided, String pathPostFix, String host) {
177+
if (tlsProvided) {
178+
return getURLFromTLSHost(host, pathPostFix);
179+
}
180+
if (host != null && !host.isEmpty()) {
181+
pathPostFix = fixPathPostFixIfEmpty(pathPostFix);
182+
return "http://" + URLUtils.pathJoin(host, pathPostFix);
183+
}
184+
return null;
185+
}
186+
187+
public static String getURLFromTLSHost(String host, String pathPostFix) {
154188
if (!host.isEmpty()) {
155189
pathPostFix = fixPathPostFixIfEmpty(pathPostFix);
156190
return "https://" + URLUtils.pathJoin(host, pathPostFix);
@@ -180,14 +214,6 @@ private static boolean portsMatch(ServicePort servicePort, IntOrString intOrStri
180214
return false;
181215
}
182216

183-
public static String getNamespace(HasMetadata entity) {
184-
if (entity != null) {
185-
return entity.getMetadata() != null ? entity.getMetadata().getNamespace() : null;
186-
} else {
187-
return null;
188-
}
189-
}
190-
191217
public static ServicePort getServicePortByName(Service service, String portName) {
192218
if (portName.isEmpty()) {
193219
return service.getSpec().getPorts().iterator().next();
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/**
2+
* Copyright (C) 2015 Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.fabric8.kubernetes.client.impl;
17+
18+
import io.fabric8.kubernetes.api.model.Service;
19+
import io.fabric8.kubernetes.api.model.ServiceBuilder;
20+
import io.fabric8.kubernetes.client.KubernetesClient;
21+
import io.fabric8.kubernetes.client.ServiceToURLProvider;
22+
import org.junit.jupiter.api.BeforeEach;
23+
import org.junit.jupiter.api.Test;
24+
25+
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
26+
import static org.mockito.Mockito.mock;
27+
28+
class URLFromEnvVarsImplTest {
29+
private URLFromEnvVarsImpl urlFromEnvVars;
30+
private KubernetesClient kubernetesClient;
31+
32+
@BeforeEach
33+
void setup() {
34+
this.urlFromEnvVars = new URLFromEnvVarsImpl();
35+
this.kubernetesClient = mock(KubernetesClient.class);
36+
}
37+
38+
@Test
39+
void getURL_whenNothingProvided_thenReturnNull() {
40+
// Given
41+
Service svc = createNewServiceBuilder().build();
42+
43+
// When
44+
String url = urlFromEnvVars.getURL(svc, "test", "default", kubernetesClient);
45+
46+
// Then
47+
assertThat(url).isNull();
48+
}
49+
50+
@Test
51+
void getURL_whenServicePropertyProvided_thenReturnServiceUrl() {
52+
final String hostProperty = "SVC1_SERVICE_HOST";
53+
final String portProperty = "SVC1_SERVICE_PORT";
54+
final String protocolProperty = "SVC1_SERVICE_PORT_80_TCP_PROTO";
55+
try {
56+
// Given
57+
System.setProperty(hostProperty, "10.111.30.220");
58+
System.setProperty(portProperty, "80");
59+
System.setProperty(protocolProperty, "tcp");
60+
Service svc = createNewServiceBuilder().build();
61+
62+
// When
63+
String url = urlFromEnvVars.getURL(svc, "test", "default", kubernetesClient);
64+
65+
// Then
66+
assertThat(url).isEqualTo("tcp://10.111.30.220:80");
67+
} finally {
68+
System.clearProperty(hostProperty);
69+
System.clearProperty(portProperty);
70+
System.clearProperty(protocolProperty);
71+
}
72+
}
73+
74+
@Test
75+
void getURL_whenServiceExposeAnnotationProvided_thenReturnServiceUrl() {
76+
// Given
77+
Service svc = createNewServiceBuilder()
78+
.editMetadata()
79+
.addToAnnotations("fabric8.io/exposeUrl", "http://example.com/svc1")
80+
.endMetadata()
81+
.build();
82+
83+
// When
84+
String url = urlFromEnvVars.getURL(svc, "test", "default", kubernetesClient);
85+
86+
// Then
87+
assertThat(url).isEqualTo("http://example.com/svc1");
88+
}
89+
90+
@Test
91+
void getPriority_whenInvoked_shouldReturnThird() {
92+
assertThat(urlFromEnvVars.getPriority()).isEqualTo(ServiceToURLProvider.ServiceToUrlImplPriority.THIRD.getValue());
93+
}
94+
95+
private ServiceBuilder createNewServiceBuilder() {
96+
return new ServiceBuilder()
97+
.withNewMetadata().withName("svc1").endMetadata()
98+
.withNewSpec()
99+
.addNewPort()
100+
.withName("test")
101+
.withProtocol("TCP")
102+
.withPort(80)
103+
.endPort()
104+
.endSpec();
105+
}
106+
}

0 commit comments

Comments
 (0)