Skip to content

Commit

Permalink
Fix fabric8io#1139 : Make it easy to get the URL of a service.
Browse files Browse the repository at this point in the history
+ Implemented getUrl() method for service
+ Fixed an arquillian dependency error that comes up while
  running tests in IntelliJ
  • Loading branch information
rohanKanojia committed Aug 2, 2018
1 parent 79d38b1 commit 36caf46
Show file tree
Hide file tree
Showing 22 changed files with 807 additions and 21 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@

* Fix #1147: Cluster context was being ignored when loading the Config from a kubeconfig file

* Fix #1158: Add support for label selectors in the mock server

* Fix #1139 : Make it easy to get the URL of a service.

Improvements

* Added Kubernetes/Openshift examples for client.getVersion()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ public MixedOperation<Secret, SecretList, DoneableSecret, Resource<Secret, Donea
}

@Override
public MixedOperation<Service, ServiceList, DoneableService, Resource<Service, DoneableService>> services() {
public MixedOperation<Service, ServiceList, DoneableService, ServiceResource<Service, DoneableService>> services() {
return delegate.services();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ public MixedOperation<Secret, SecretList, DoneableSecret, Resource<Secret, Donea
}

@Override
public MixedOperation<Service, ServiceList, DoneableService, Resource<Service, DoneableService>> services() {
public MixedOperation<Service, ServiceList, DoneableService, ServiceResource<Service, DoneableService>> services() {
return new ServiceOperationsImpl(httpClient, getConfiguration(), getNamespace());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public interface KubernetesClient extends Client {

MixedOperation<Secret, SecretList, DoneableSecret, Resource<Secret, DoneableSecret>> secrets();

MixedOperation<Service, ServiceList, DoneableService, Resource<Service, DoneableService>> services();
MixedOperation<Service, ServiceList, DoneableService, ServiceResource<Service, DoneableService>> services();

MixedOperation<ServiceAccount, ServiceAccountList, DoneableServiceAccount, Resource<ServiceAccount, DoneableServiceAccount>> serviceAccounts();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* 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 io.fabric8.kubernetes.client;

import io.fabric8.kubernetes.api.model.Service;

public interface ServiceToURLProvider {
enum ServiceToUrlImplPriority {
FIRST(0), SECOND(1), THIRD(2), FOURTH(3);

private final int value;

ServiceToUrlImplPriority(final int newVal) {
value = newVal;
}

public int getValue() { return value; }
}

ServiceToUrlImplPriority getPriority();

String getURL(Service service, String portName, String namespace, KubernetesClient client);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* 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 io.fabric8.kubernetes.client;

import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.client.utils.URLFromServiceImplUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class URLFromEnvVarsImpl implements ServiceToURLProvider {
public static Logger logger = LoggerFactory.getLogger(URLFromEnvVarsImpl.class);

public static final String ANNOTATION_EXPOSE_URL = "fabric8.io/exposeUrl";

@Override
public String getURL(Service service, String portName, String namespace, KubernetesClient client) {
String serviceHost = URLFromServiceImplUtil.serviceToHostOrBlank(service.getMetadata().getName());
String servicePort = URLFromServiceImplUtil.serviceToPortOrBlank(service.getMetadata().getName(), "");
String serviceProtocol = URLFromServiceImplUtil.serviceToProtocol(service.getSpec().getPorts().iterator().next().getProtocol(), "");

if(!serviceHost.isEmpty() && !servicePort.isEmpty() && !serviceProtocol.isEmpty()) {
return serviceProtocol + "://" + serviceHost + ":" + servicePort;
} else {
String answer = URLFromServiceImplUtil.getOrCreateAnnotations(service).get(ANNOTATION_EXPOSE_URL);
if(answer != null && !answer.isEmpty()) {
return answer;
}
}
return null;
}

@Override
public ServiceToUrlImplPriority getPriority() {
return ServiceToUrlImplPriority.THIRD;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* 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 io.fabric8.kubernetes.client;

import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.ServicePort;
import io.fabric8.kubernetes.api.model.extensions.*;
import io.fabric8.kubernetes.client.utils.URLFromServiceImplUtil;

public class URLFromIngressImpl implements ServiceToURLProvider {

@Override
public String getURL(Service service, String portName, String namespace, KubernetesClient client) {
ServicePort port = URLFromServiceImplUtil.getServicePortByName(service, portName);
String serviceName = service.getMetadata().getName();
if(port == null) {
throw new RuntimeException("Couldn't find port: " + portName + " for service " + service.getMetadata().getName());
}

IngressList ingresses = client.extensions().ingresses().inNamespace(namespace).list();
if(ingresses != null && !ingresses.getItems().isEmpty()) {
return URLFromServiceImplUtil.getURLFromIngressList(ingresses.getItems(), namespace, serviceName, port);
}
return null;
}

@Override
public ServiceToUrlImplPriority getPriority() {
return ServiceToUrlImplPriority.FIRST;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* 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 io.fabric8.kubernetes.client;

import io.fabric8.kubernetes.api.model.*;
import io.fabric8.kubernetes.client.utils.URLFromServiceImplUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

public class URLFromNodePortImpl implements ServiceToURLProvider {
public static Logger logger = LoggerFactory.getLogger(URLFromNodePortImpl.class);

public String getURL(Service service, String portName, String namespace, KubernetesClient client) {
boolean bFound = false;
ServicePort port = URLFromServiceImplUtil.getServicePortByName(service, portName);
String serviceProto = port.getProtocol();
String clusterIP = null;
Integer portNumber = 0;
Integer nodePort = port.getNodePort();
if(nodePort != null) {
try {
NodeList nodeList = client.nodes().list();
if(nodeList != null && nodeList.getItems() != null) {
for(Node item : nodeList.getItems()) {
NodeStatus status = item.getStatus();
if(!bFound && status != null) {
List<NodeAddress> addresses = status.getAddresses();
for(NodeAddress address : addresses) {
String ip = address.getAddress();
if (!ip.isEmpty()) {
clusterIP = ip;
portNumber = nodePort;
bFound = true;
break;
}
}
}
}
}
} catch (KubernetesClientException exception) {
logger.warn("Could not find a node! " + exception);
}
}
return (serviceProto + "://" + clusterIP + ":" + portNumber).toLowerCase();
}

@Override
public ServiceToUrlImplPriority getPriority() {
return ServiceToUrlImplPriority.SECOND;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Copyright (C) 2015 Red Hat, Inc.
*
* 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 io.fabric8.kubernetes.client.dsl;

public interface ServiceResource<T, D> extends Resource<T, D> {
String getURL(String portName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,19 @@
*/
package io.fabric8.kubernetes.client.dsl.internal;

import io.fabric8.kubernetes.api.model.Endpoints;
import io.fabric8.kubernetes.client.dsl.Resource;
import okhttp3.OkHttpClient;
import io.fabric8.kubernetes.api.model.DoneableService;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.ServiceBuilder;
import io.fabric8.kubernetes.api.model.ServiceList;
import io.fabric8.kubernetes.api.model.*;
import io.fabric8.kubernetes.client.*;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.dsl.ServiceResource;
import io.fabric8.kubernetes.client.utils.URLUtils;
import okhttp3.OkHttpClient;

import io.fabric8.kubernetes.client.dsl.base.HasMetadataOperation;

import java.util.Map;
import java.util.TreeMap;
import java.util.*;
import java.util.concurrent.TimeUnit;

public class ServiceOperationsImpl extends HasMetadataOperation<Service, ServiceList, DoneableService, Resource<Service, DoneableService>> {
public class ServiceOperationsImpl extends HasMetadataOperation<Service, ServiceList, DoneableService, ServiceResource<Service, DoneableService>> implements ServiceResource<Service, DoneableService> {

public ServiceOperationsImpl(OkHttpClient client, Config config, String namespace) {
this(client, config, null, namespace, null, true, null, null, false, -1, new TreeMap<String, String>(), new TreeMap<String, String>(), new TreeMap<String, String[]>(), new TreeMap<String, String[]>(), new TreeMap<String, String>());
Expand Down Expand Up @@ -83,4 +79,37 @@ public Service waitUntilReady(long amount, TimeUnit timeUnit) throws Interrupted

return get();
}

public String getURL(String portName) {
String clusterIP = getMandatory().getSpec().getClusterIP();
if("None".equals(clusterIP)) {
throw new IllegalStateException("Service: " + getMandatory().getMetadata().getName() + " in namespace " +
namespace + " is head-less. Search for endpoints instead");
}

ServiceLoader<ServiceToURLProvider> urlProvider = ServiceLoader.load(ServiceToURLProvider.class, Thread.currentThread().getContextClassLoader());
Iterator<ServiceToURLProvider> iterator = urlProvider.iterator();
List<ServiceToURLProvider> servicesList = new ArrayList<>();

while(iterator.hasNext()) {
servicesList.add(iterator.next());
}

// Sort all loaded implementations according to priority
Collections.sort(servicesList, new ServiceToUrlSortComparator());
for(ServiceToURLProvider serviceToURLProvider : servicesList) {
String url = serviceToURLProvider.getURL(getMandatory(), portName, namespace, new DefaultKubernetesClient(client, getConfig()));
if(url != null && URLUtils.isValidURL(url)) {
return url;
}
}

return null;
}

public class ServiceToUrlSortComparator implements Comparator<ServiceToURLProvider> {
public int compare(ServiceToURLProvider first, ServiceToURLProvider second) {
return first.getPriority().getValue() - second.getPriority().getValue();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ public MixedOperation<Pod, PodList, DoneablePod, PodResource<Pod, DoneablePod>>
return delegate.pods();
}

public MixedOperation<io.fabric8.kubernetes.api.model.Service, ServiceList, DoneableService, Resource<io.fabric8.kubernetes.api.model.Service, DoneableService>> services() {
public MixedOperation<io.fabric8.kubernetes.api.model.Service, ServiceList, DoneableService, ServiceResource<io.fabric8.kubernetes.api.model.Service, DoneableService>> services() {
return delegate.services();
}

Expand Down
Loading

0 comments on commit 36caf46

Please sign in to comment.