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 20, 2018
1 parent 0e730c0 commit a7feaf0
Show file tree
Hide file tree
Showing 22 changed files with 849 additions and 22 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

* Fix #1172 : Use v1beta1 for CronJob

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

Improvements

* Added Kubernetes/Openshift examples for client.getVersion()
Expand All @@ -36,6 +38,7 @@
* Fix #1066 : Add support for Kubernetes RBAC Role and Role Binding
* Fix #1150: Add support for Kubernetes RBAC Cluster Role and Cluster Role Binding
* Fix #770: Added Support for CronJob
* Fix #1139 : Make it easy to get the URL of a service.

#### 4.0.0
Bugs
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; }
}

int 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.URLFromServiceUtil;
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 = URLFromServiceUtil.resolveHostFromEnvVarOrSystemProperty(service.getMetadata().getName());
String servicePort = URLFromServiceUtil.resolvePortFromEnvVarOrSystemProperty(service.getMetadata().getName(), "");
String serviceProtocol = URLFromServiceUtil.resolveProtocolFromEnvVarOrSystemProperty(service.getSpec().getPorts().iterator().next().getProtocol(), "");

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

@Override
public int getPriority() {
return ServiceToUrlImplPriority.THIRD.getValue();
}
}
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.URLFromServiceUtil;

public class URLFromIngressImpl implements ServiceToURLProvider {

@Override
public String getURL(Service service, String portName, String namespace, KubernetesClient client) {
ServicePort port = URLFromServiceUtil.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 URLFromServiceUtil.getURLFromIngressList(ingresses.getItems(), namespace, serviceName, port);
}
return null;
}

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

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* 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.URLFromServiceUtil;
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) {
ServicePort port = URLFromServiceUtil.getServicePortByName(service, portName);
String serviceProto = port.getProtocol();
NodePortUrlComponents urlComponents = null;
Integer nodePort = port.getNodePort();
if(nodePort != null) {
try {
NodeList nodeList = client.nodes().list();
if(nodeList != null && nodeList.getItems() != null) {
for(Node item : nodeList.getItems()) {
urlComponents = getUrlComponentsFromNodeList(item.getStatus(), nodePort);
if(urlComponents != null) {
break;
}
}
}
} catch (KubernetesClientException exception) {
logger.warn("Could not find a node! " + exception);
}
}

return urlComponents != null ? (serviceProto + "://" + urlComponents.getClusterIP() + ":" + urlComponents.getPortNumber()).toLowerCase() : null;
}

private NodePortUrlComponents getUrlComponentsFromNodeList(NodeStatus nodeStatus, Integer nodePort) {
if(nodeStatus != null) {
List<NodeAddress> addresses = nodeStatus.getAddresses();
for (NodeAddress address : addresses) {
String ip = address.getAddress();
if (!ip.isEmpty()) {
return new NodePortUrlComponents(ip, nodePort);
}
}
}
return null;
}

private class NodePortUrlComponents {
public String getClusterIP() {
return clusterIP;
}

private String clusterIP;

public Integer getPortNumber() {
return portNumber;
}

private Integer portNumber;

public NodePortUrlComponents(String clusterIP, Integer portNumber) {
this.clusterIP = clusterIP;
this.portNumber = portNumber;
}
}

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

}
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,40 @@ 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");
}
return getUrlHelper(portName);
}

private String getUrlHelper(String portName) {
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() - second.getPriority();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,24 @@
import io.fabric8.kubernetes.client.RequestConfig;
import io.fabric8.kubernetes.client.Handlers;
import io.fabric8.kubernetes.client.VersionInfo;
import io.fabric8.kubernetes.client.dsl.*;
import io.fabric8.kubernetes.client.dsl.AppsAPIGroupDSL;
import io.fabric8.kubernetes.client.dsl.AutoscalingAPIGroupDSL;
import io.fabric8.kubernetes.client.dsl.BatchAPIGroupDSL;
import io.fabric8.kubernetes.client.dsl.ExtensionsAPIGroupDSL;
import io.fabric8.kubernetes.client.dsl.FunctionCallable;
import io.fabric8.kubernetes.client.dsl.KubernetesListMixedOperation;
import io.fabric8.kubernetes.client.dsl.MixedOperation;
import io.fabric8.kubernetes.client.dsl.NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable;
import io.fabric8.kubernetes.client.dsl.NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicable;
import io.fabric8.kubernetes.client.dsl.NetworkAPIGroupDSL;
import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
import io.fabric8.kubernetes.client.dsl.ParameterNamespaceListVisitFromServerGetDeleteRecreateWaitApplicable;
import io.fabric8.kubernetes.client.dsl.PodResource;
import io.fabric8.kubernetes.client.dsl.RbacAPIGroupDSL;
import io.fabric8.kubernetes.client.dsl.Resource;
import io.fabric8.kubernetes.client.dsl.RollableScalableResource;
import io.fabric8.kubernetes.client.dsl.ServiceResource;
import io.fabric8.kubernetes.client.dsl.StorageAPIGroupDSL;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.ConfigurationPolicy;
Expand Down Expand Up @@ -247,7 +264,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 a7feaf0

Please sign in to comment.