Skip to content

Commit 737d3eb

Browse files
committed
Ref #35096: connect to k8s pod using hostname
1 parent 78cafdd commit 737d3eb

1 file changed

Lines changed: 51 additions & 9 deletions

File tree

src/main/java/eu/openanalytics/containerproxy/backend/kubernetes/KubernetesBackend.java

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,16 @@ public class KubernetesBackend extends AbstractContainerBackend {
135135
private static final String PROPERTY_IMG_PULL_SECRETS = "image-pull-secrets";
136136
private static final String PROPERTY_IMG_PULL_SECRET = "image-pull-secret";
137137
private static final String PROPERTY_NODE_SELECTOR = "node-selector";
138+
private static final String PROPERTY_CLUSTER_DOMAIN = "cluster-domain";
138139

139140
private static final String DEFAULT_NAMESPACE = "default";
140141
private static final String DEFAULT_API_VERSION = "v1";
142+
private static final String DEFAULT_CLUSTER_DOMAIN = "cluster.local";
141143

142144
private static final String SECRET_KEY_REF = "secretKeyRef";
145+
private static final String HEADLESS_SERVICE_NAME = "sp-headless-service";
146+
private static final String HEADLESS_SERVICE_LABEL = "openanalytics.eu/sp-headless-service";
147+
private static final String HEADLESS_SERVICE_LABEL_VALUE = "true";
143148

144149
private static final String ANNOTATION_MANIFEST_POLICY = "openanalytics.eu/sp-additional-manifest-policy";
145150
private final ObjectMapper writer = new ObjectMapper(new YAMLFactory());
@@ -158,6 +163,7 @@ public class KubernetesBackend extends AbstractContainerBackend {
158163
private String kubeNamespace;
159164
private String apiVersion;
160165
private String imagePullPolicy;
166+
private String clusterDomain;
161167

162168
@PostConstruct
163169
public void initialize() {
@@ -197,6 +203,7 @@ public void initialize(KubernetesClient client) {
197203
nodeSelectorString = getProperty(PROPERTY_NODE_SELECTOR);
198204
apiVersion = getProperty(PROPERTY_API_VERSION, DEFAULT_API_VERSION);
199205
imagePullPolicy = getProperty(PROPERTY_IMG_PULL_POLICY);
206+
clusterDomain = getProperty(PROPERTY_CLUSTER_DOMAIN, DEFAULT_CLUSTER_DOMAIN);
200207

201208
String imagePullSecret = getProperty(PROPERTY_IMG_PULL_SECRET);
202209
if (imagePullSecret != null) {
@@ -284,6 +291,7 @@ public Proxy startContainer(Authentication user, Container initialContainer, Con
284291
Map<String, String> serviceLabels = new HashMap<>();
285292
Map<String, String> podLabels = new HashMap<>();
286293
podLabels.put("app", containerId);
294+
podLabels.put(HEADLESS_SERVICE_LABEL, HEADLESS_SERVICE_LABEL_VALUE);
287295

288296
String podName = StringUtils.left(spec.getResourceName().getValueOrDefault("sp-pod-" + proxy.getId() + "-" + initialContainer.getIndex()), 63);
289297

@@ -325,12 +333,13 @@ public Proxy startContainer(Authentication user, Container initialContainer, Con
325333
podSpec.setVolumes(volumes);
326334
podSpec.setImagePullSecrets(imagePullSecrets);
327335
podSpec.setRestartPolicy("Never");
336+
podSpec.setHostname(podName);
337+
podSpec.setSubdomain(HEADLESS_SERVICE_NAME);
328338

329339
if (nodeSelectorString != null) {
330340
podSpec.setNodeSelector(Splitter.on(",").withKeyValueSeparator("=").split(nodeSelectorString));
331341
}
332342

333-
334343
Pod startupPod = podBuilder.withSpec(podSpec).build();
335344
Pod patchedPod = applyPodPatches(user, proxySpec, specExtension, proxy, startupPod, initialContainer);
336345
final String effectiveKubeNamespace = patchedPod.getMetadata().getNamespace(); // use the namespace of the patched Pod, in case the patch changes the namespace.
@@ -377,27 +386,30 @@ public Proxy startContainer(Authentication user, Container initialContainer, Con
377386
Service service;
378387
Map<Integer, Integer> portBindings = new HashMap<>();
379388
if (isUseInternalNetwork()) {
380-
// If SP runs inside the cluster, it can access pods directly and doesn't need any port publishing service.
389+
// If SP runs inside the cluster, it re-uses an existing headless service
390+
createHeadlessService(effectiveKubeNamespace);
381391
} else {
382392
List<ServicePort> servicePorts = spec.getPortMapping().stream()
383393
.map(p -> new ServicePortBuilder().withPort(p.getPort()).build())
384394
.toList();
385395

396+
// @formatter:off
386397
Service startupService = kubeClient.services().inNamespace(effectiveKubeNamespace)
387398
.resource(new ServiceBuilder()
388399
.withApiVersion(apiVersion)
389400
.withKind("Service")
390401
.withNewMetadata()
391-
.withName(getServiceName(proxy, initialContainer))
392-
.withLabels(serviceLabels)
402+
.withName(getServiceName(proxy, initialContainer))
403+
.withLabels(serviceLabels)
393404
.endMetadata()
394405
.withNewSpec()
395-
.addToSelector("app", containerId)
396-
.withType("NodePort")
397-
.withPorts(servicePorts)
406+
.addToSelector("app", containerId)
407+
.withType("NodePort")
408+
.withPorts(servicePorts)
398409
.endSpec()
399410
.build())
400-
.create();
411+
.create();
412+
// @formatter:on
401413

402414
// Workaround: waitUntilReady appears to be buggy.
403415
Retrying.retry((currentAttempt, maxAttempts) -> new Retrying.Result(isServiceReady(kubeClient.resource(startupService).get())), 60_000);
@@ -636,14 +648,44 @@ private boolean isServiceReady(Service service) {
636648
return service.getStatus().getLoadBalancer() != null;
637649
}
638650

651+
private void createHeadlessService(String effectiveKubeNamespace) {
652+
if (kubeClient.services().inNamespace(effectiveKubeNamespace).withName(HEADLESS_SERVICE_NAME).get() == null) {
653+
// @formatter:off
654+
kubeClient.services().inNamespace(effectiveKubeNamespace)
655+
.resource(new ServiceBuilder()
656+
.withApiVersion(apiVersion)
657+
.withKind("Service")
658+
.withNewMetadata()
659+
.withName(HEADLESS_SERVICE_NAME)
660+
.endMetadata()
661+
.withNewSpec()
662+
.addToSelector(HEADLESS_SERVICE_LABEL, HEADLESS_SERVICE_LABEL_VALUE)
663+
.withClusterIP("None")
664+
.endSpec()
665+
.build())
666+
.create();
667+
// @formatter:on
668+
}
669+
}
670+
671+
private String getPodFqdn(Pod pod) {
672+
return String.format(
673+
"%s.%s.%s.svc.%s",
674+
pod.getSpec().getHostname(),
675+
pod.getSpec().getSubdomain(),
676+
pod.getMetadata().getNamespace(),
677+
clusterDomain);
678+
679+
}
680+
639681
protected URI calculateTarget(Container container, PortMappings.PortMappingEntry portMapping, Integer servicePort) throws Exception {
640682
String targetHostName;
641683
int targetPort;
642684

643685
Pod pod = getPod(container).orElseThrow(() -> new ContainerFailedToStartException("Pod not found while calculating target", null, container));
644686

645687
if (isUseInternalNetwork()) {
646-
targetHostName = pod.getStatus().getPodIP();
688+
targetHostName = getPodFqdn(pod);
647689
targetPort = portMapping.getPort();
648690
} else {
649691
targetHostName = pod.getStatus().getHostIP();

0 commit comments

Comments
 (0)