You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by kr...@apache.org on 2019/09/23 14:16:22 UTC
[knox] branch master updated: KNOX-1914 - New admin API to be used
by the UI to fetch available service discovery types (#147)
This is an automated email from the ASF dual-hosted git repository.
krisden pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git
The following commit(s) were added to refs/heads/master by this push:
new 51b9ab7 KNOX-1914 - New admin API to be used by the UI to fetch available service discovery types (#147)
51b9ab7 is described below
commit 51b9ab77960eeb1a65248ac2f602d7903765ac82
Author: Sandor Molnar <sm...@apache.org>
AuthorDate: Mon Sep 23 16:16:17 2019 +0200
KNOX-1914 - New admin API to be used by the UI to fetch available service discovery types (#147)
---
.../new-desc-wizard/new-desc-wizard.component.html | 2 +-
.../resource-detail/resource-detail.component.html | 2 +-
.../admin-ui/app/resource/resource.service.ts | 27 +++++-
.../discovery/ambari/AmbariServiceDiscovery.java | 2 +-
.../discovery/ServiceDiscoveryFactory.java | 81 ----------------
.../discovery/ServiceDiscoveryFactoryTest.java | 12 +++
.../ServiceDiscoveryCollectionMarshaller.java | 70 ++++++++++++++
.../service/admin/ServiceDiscoveryResource.java | 102 +++++++++++++++++++++
gateway-spi/pom.xml | 4 +
.../discovery/ServiceDiscoveryFactory.java | 81 ++++++++++++++++
10 files changed, 295 insertions(+), 88 deletions(-)
diff --git a/gateway-admin-ui/admin-ui/app/new-desc-wizard/new-desc-wizard.component.html b/gateway-admin-ui/admin-ui/app/new-desc-wizard/new-desc-wizard.component.html
index c709991..960f300 100644
--- a/gateway-admin-ui/admin-ui/app/new-desc-wizard/new-desc-wizard.component.html
+++ b/gateway-admin-ui/admin-ui/app/new-desc-wizard/new-desc-wizard.component.html
@@ -85,7 +85,7 @@
<span>
<select id="select" autofocus required class="md-select form-control"
[(ngModel)]="descriptor.discoveryType" (change)="descriptor.setDirty()">
- <option *ngFor="let typeOption of resourceService.getSupportedDiscoveryTypes()"
+ <option *ngFor="let typeOption of resourceService.discoveryTypes"
class="md-option"
[value]="typeOption">{{typeOption}}</option>
</select>
diff --git a/gateway-admin-ui/admin-ui/app/resource-detail/resource-detail.component.html b/gateway-admin-ui/admin-ui/app/resource-detail/resource-detail.component.html
index c110af3..f6bb824 100644
--- a/gateway-admin-ui/admin-ui/app/resource-detail/resource-detail.component.html
+++ b/gateway-admin-ui/admin-ui/app/resource-detail/resource-detail.component.html
@@ -366,7 +366,7 @@
<span>
<select id="select" autofocus required class="md-select form-control"
[(ngModel)]="descriptor.discoveryType" (change)="descriptor.setDirty()">
- <option *ngFor="let typeOption of resourceService.getSupportedDiscoveryTypes()"
+ <option *ngFor="let typeOption of resourceService.discoveryTypes"
class="md-option"
[value]="typeOption"
[selected]="descriptor.discoveryType === typeOption">{{typeOption}}</option>
diff --git a/gateway-admin-ui/admin-ui/app/resource/resource.service.ts b/gateway-admin-ui/admin-ui/app/resource/resource.service.ts
index 4e6b624..a8fcc28 100644
--- a/gateway-admin-ui/admin-ui/app/resource/resource.service.ts
+++ b/gateway-admin-ui/admin-ui/app/resource/resource.service.ts
@@ -24,13 +24,13 @@ import {Descriptor} from '../resource-detail/descriptor';
@Injectable()
export class ResourceService {
- // TODO: PJZ: Get this list dynamically?
- private static discoveryTypes: Array<string> = ['ClouderaManager', 'Ambari'];
+ discoveryTypes: Array<string>;
apiUrl = window.location.pathname.replace(new RegExp('admin-ui/.*'), 'api/v1/');
providersUrl = this.apiUrl + 'providerconfig';
descriptorsUrl = this.apiUrl + 'descriptors';
topologiesUrl = this.apiUrl + 'topologies';
+ serviceDiscoveriesUrl = this.apiUrl + 'servicediscoveries';
selectedResourceTypeSource = new Subject<string>();
selectedResourceType$ = this.selectedResourceTypeSource.asObservable();
@@ -44,11 +44,30 @@ export class ResourceService {
changedProviderConfigurationSource = new Subject<Array<ProviderConfig>>();
changedProviderConfiguration$ = this.changedProviderConfigurationSource.asObservable();
+
constructor(private http: HttpClient) {
+ this.initSupportedDiscoveryTypes();
+ }
+
+ initSupportedDiscoveryTypes(): void {
+ if (this.discoveryTypes == null || this.discoveryTypes.length === 0) {
+ let headers = this.addJsonHeaders(new HttpHeaders());
+ this.getServiceDiscoveryResources()
+ .then(response => this.discoveryTypes = response.knoxServiceDiscoveries.knoxServiceDiscovery.map(sd => sd.type))
+ .catch((err: HttpErrorResponse) => {
+ console.debug('ResourceService --> getServiceDiscoveryResources() --> error: HTTP ' + err.status + ' ' + err.message);
+ if (err.status === 401) {
+ window.location.assign(document.location.pathname);
+ } else {
+ return this.handleError(err);
+ }
+ });
+ }
}
- getSupportedDiscoveryTypes(): string[] {
- return ResourceService.discoveryTypes;
+ getServiceDiscoveryResources(): Promise<any> {
+ let headers = this.addJsonHeaders(new HttpHeaders());
+ return this.http.get(this.serviceDiscoveriesUrl, {headers: headers}).toPromise();
}
getResources(resType: string): Promise<Resource[]> {
diff --git a/gateway-discovery-ambari/src/main/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariServiceDiscovery.java b/gateway-discovery-ambari/src/main/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariServiceDiscovery.java
index 6e891c7..cef6e9c 100644
--- a/gateway-discovery-ambari/src/main/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariServiceDiscovery.java
+++ b/gateway-discovery-ambari/src/main/java/org/apache/knox/gateway/topology/discovery/ambari/AmbariServiceDiscovery.java
@@ -47,7 +47,7 @@ import java.util.Properties;
class AmbariServiceDiscovery implements ServiceDiscovery {
- static final String TYPE = "AMBARI";
+ static final String TYPE = "Ambari";
static final String AMBARI_CLUSTERS_URI = AmbariClientCommon.AMBARI_CLUSTERS_URI;
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/topology/discovery/ServiceDiscoveryFactory.java b/gateway-server/src/main/java/org/apache/knox/gateway/topology/discovery/ServiceDiscoveryFactory.java
deleted file mode 100644
index f055f70..0000000
--- a/gateway-server/src/main/java/org/apache/knox/gateway/topology/discovery/ServiceDiscoveryFactory.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to you 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 org.apache.knox.gateway.topology.discovery;
-
-import org.apache.knox.gateway.services.Service;
-
-import java.lang.reflect.Field;
-import java.util.ServiceLoader;
-
-/**
- * Creates instances of ServiceDiscovery implementations.
- *
- * This factory uses the ServiceLoader mechanism to load ServiceDiscovery implementations as extensions.
- *
- */
-public abstract class ServiceDiscoveryFactory {
-
- private static final Service[] NO_GATEWAY_SERVICS = new Service[]{};
-
-
- public static ServiceDiscovery get(String type) {
- return get(type, NO_GATEWAY_SERVICS);
- }
-
-
- public static ServiceDiscovery get(String type, Service...gatewayServices) {
- ServiceDiscovery sd = null;
-
- // Look up the available ServiceDiscovery types
- ServiceLoader<ServiceDiscoveryType> loader = ServiceLoader.load(ServiceDiscoveryType.class);
- for (ServiceDiscoveryType sdt : loader) {
- if (sdt.getType().equalsIgnoreCase(type)) {
- try {
- ServiceDiscovery instance = sdt.newInstance();
- // Make sure the type reported by the instance matches the type declared by the factory
- // (is this necessary?)
- if (instance.getType().equalsIgnoreCase(type)) {
- sd = instance;
-
- // Inject any gateway services that were specified, and which are referenced in the impl
- if (gatewayServices != null && gatewayServices.length > 0) {
- for (Field field : sd.getClass().getDeclaredFields()) {
- if (field.getDeclaredAnnotation(GatewayService.class) != null) {
- for (Service s : gatewayServices) {
- if (s != null) {
- if (field.getType().isAssignableFrom(s.getClass())) {
- field.setAccessible(true);
- field.set(sd, s);
- }
- }
- }
- }
- }
- }
- break;
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
-
- return sd;
- }
-
-
-}
diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/topology/discovery/ServiceDiscoveryFactoryTest.java b/gateway-server/src/test/java/org/apache/knox/gateway/topology/discovery/ServiceDiscoveryFactoryTest.java
index dacc998..7aa712d 100644
--- a/gateway-server/src/test/java/org/apache/knox/gateway/topology/discovery/ServiceDiscoveryFactoryTest.java
+++ b/gateway-server/src/test/java/org/apache/knox/gateway/topology/discovery/ServiceDiscoveryFactoryTest.java
@@ -22,6 +22,7 @@ import org.junit.Test;
import java.lang.reflect.Field;
import java.util.Locale;
+import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -80,5 +81,16 @@ public class ServiceDiscoveryFactoryTest {
assertTrue(AliasService.class.isAssignableFrom(fieldValue.getClass()));
}
+ @Test
+ public void testGetAllServiceDiscoveries() {
+ final Set<ServiceDiscovery> serviceDiscoveries = ServiceDiscoveryFactory.getAllServiceDiscoveries();
+ assertEquals(3, serviceDiscoveries.size());
+ assertTrue(hasServiceDiscoveryWithType(serviceDiscoveries, "DUMMY"));
+ assertTrue(hasServiceDiscoveryWithType(serviceDiscoveries, "PROPERTIES_FILE"));
+ assertTrue(hasServiceDiscoveryWithType(serviceDiscoveries, "ActualType"));
+ }
+ private boolean hasServiceDiscoveryWithType(Set<ServiceDiscovery> serviceDiscoveries, String type) {
+ return serviceDiscoveries.stream().anyMatch(serviceDiscovery -> serviceDiscovery.getType().equalsIgnoreCase(type));
+ }
}
diff --git a/gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/ServiceDiscoveryCollectionMarshaller.java b/gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/ServiceDiscoveryCollectionMarshaller.java
new file mode 100644
index 0000000..6f93cd4
--- /dev/null
+++ b/gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/ServiceDiscoveryCollectionMarshaller.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.knox.gateway.service.admin;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+
+import org.apache.knox.gateway.service.admin.ServiceDiscoveryResource.ServiceDiscoveryWrapper;
+import org.eclipse.persistence.jaxb.JAXBContextProperties;
+
+@Provider
+@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
+public class ServiceDiscoveryCollectionMarshaller implements MessageBodyWriter<ServiceDiscoveryResource.ServiceDiscoveryWrapper> {
+
+ @Override
+ public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+ return ServiceDiscoveryResource.ServiceDiscoveryWrapper.class == type;
+ }
+
+ @Override
+ public long getSize(ServiceDiscoveryWrapper t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
+ return -1;
+ }
+
+ @Override
+ public void writeTo(ServiceDiscoveryResource.ServiceDiscoveryWrapper instance, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
+ MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
+ try {
+ final Map<String, Object> properties = new HashMap<>(1);
+ properties.put(JAXBContextProperties.MEDIA_TYPE, mediaType.toString());
+ final JAXBContext context = JAXBContext.newInstance(new Class[] { ServiceDiscoveryResource.ServiceDiscoveryWrapper.class }, properties);
+ final Marshaller m = context.createMarshaller();
+ m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+ m.marshal(instance, entityStream);
+ } catch (JAXBException e) {
+ throw new IOException(e);
+ }
+ }
+
+}
diff --git a/gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/ServiceDiscoveryResource.java b/gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/ServiceDiscoveryResource.java
new file mode 100644
index 0000000..5cbea74
--- /dev/null
+++ b/gateway-service-admin/src/main/java/org/apache/knox/gateway/service/admin/ServiceDiscoveryResource.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.knox.gateway.service.admin;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static javax.ws.rs.core.MediaType.APPLICATION_XML;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+
+import org.apache.knox.gateway.topology.discovery.ServiceDiscovery;
+import org.apache.knox.gateway.topology.discovery.ServiceDiscoveryFactory;
+
+@Path("/api/v1")
+public class ServiceDiscoveryResource {
+
+ @GET
+ @Produces({ APPLICATION_JSON, APPLICATION_XML })
+ @Path("servicediscoveries")
+ public ServiceDiscoveryWrapper getServiceDiscoveries() {
+ final ServiceDiscoveryWrapper serviceDiscoveryWrapper = new ServiceDiscoveryWrapper();
+ serviceDiscoveryWrapper.setKnoxServiceDiscoveries(ServiceDiscoveryFactory.getAllServiceDiscoveries());
+ return serviceDiscoveryWrapper;
+ }
+
+ @XmlAccessorType(XmlAccessType.NONE)
+ public static class KnoxServiceDiscovery {
+
+ @XmlElement
+ private final String type;
+
+ @XmlElement
+ private final String implementation;
+
+ // having a no-argument constructor is required by JAXB
+ public KnoxServiceDiscovery() {
+ this(null, null);
+ }
+
+ public KnoxServiceDiscovery(String type, String implementation) {
+ this.type = type;
+ this.implementation = implementation;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getImplementation() {
+ return implementation;
+ }
+
+ }
+
+ @XmlAccessorType(XmlAccessType.FIELD)
+ public static class ServiceDiscoveryWrapper {
+
+ @XmlElement(name = "knoxServiceDiscovery")
+ @XmlElementWrapper(name = "knoxServiceDiscoveries")
+ private Set<KnoxServiceDiscovery> knoxServiceDiscoveries = new HashSet<>();
+
+ public Set<KnoxServiceDiscovery> getKnoxServiceDiscoveries() {
+ return knoxServiceDiscoveries;
+ }
+
+ public void setKnoxServiceDiscoveries(Set<KnoxServiceDiscovery> knoxServiceDiscoveries) {
+ this.knoxServiceDiscoveries = knoxServiceDiscoveries;
+ }
+
+ void setKnoxServiceDiscoveries(Collection<ServiceDiscovery> serviceDiscoveries) {
+ serviceDiscoveries.forEach(serviceDiscovery -> {
+ this.knoxServiceDiscoveries.add(new KnoxServiceDiscovery(serviceDiscovery.getType(), serviceDiscovery.getClass().getCanonicalName()));
+
+ });
+ }
+ }
+}
diff --git a/gateway-spi/pom.xml b/gateway-spi/pom.xml
index c6dffab..3ddc691 100644
--- a/gateway-spi/pom.xml
+++ b/gateway-spi/pom.xml
@@ -118,6 +118,10 @@
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/topology/discovery/ServiceDiscoveryFactory.java b/gateway-spi/src/main/java/org/apache/knox/gateway/topology/discovery/ServiceDiscoveryFactory.java
new file mode 100644
index 0000000..05b3398
--- /dev/null
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/topology/discovery/ServiceDiscoveryFactory.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you 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 org.apache.knox.gateway.topology.discovery;
+
+import java.lang.reflect.Field;
+import java.util.HashSet;
+import java.util.ServiceLoader;
+import java.util.Set;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.knox.gateway.services.Service;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Creates instances of ServiceDiscovery implementations.
+ *
+ * This factory uses the ServiceLoader mechanism to load ServiceDiscovery
+ * implementations as extensions.
+ *
+ */
+public abstract class ServiceDiscoveryFactory {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ServiceDiscoveryFactory.class);
+
+ private static final Service[] NO_GATEWAY_SERVICS = new Service[] {};
+
+ public static ServiceDiscovery get(String type) {
+ return get(type, NO_GATEWAY_SERVICS);
+ }
+
+ public static Set<ServiceDiscovery> getAllServiceDiscoveries() {
+ final Set<ServiceDiscovery> serviceDiscoveries = new HashSet<>();
+ ServiceLoader.load(ServiceDiscoveryType.class).forEach((serviceDiscoveryType) -> {
+ serviceDiscoveries.add(serviceDiscoveryType.newInstance());
+ });
+ return serviceDiscoveries;
+ }
+
+ public static ServiceDiscovery get(String type, Service... gatewayServices) {
+ final ServiceDiscovery sd = getAllServiceDiscoveries().stream().filter(serviceDiscovery -> serviceDiscovery.getType().equalsIgnoreCase(type)).findFirst().orElse(null);
+ // Inject any gateway services that were specified, and which are referenced in the impl
+ injectGatewayServices(sd, gatewayServices);
+ return sd;
+ }
+
+ private static void injectGatewayServices(final ServiceDiscovery serviceDiscovery, Service... gatewayServices) {
+ if (ArrayUtils.isNotEmpty(gatewayServices)) {
+ try {
+ for (Field field : serviceDiscovery.getClass().getDeclaredFields()) {
+ if (field.getDeclaredAnnotation(GatewayService.class) != null) {
+ for (Service gatewayService : gatewayServices) {
+ if (gatewayService != null) {
+ if (field.getType().isAssignableFrom(gatewayService.getClass())) {
+ field.setAccessible(true);
+ field.set(serviceDiscovery, gatewayService);
+ }
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ LOG.error("Error while injecting Gateway Services in service discoveries", e);
+ }
+ }
+ }
+}