This is an automated email from the ASF dual-hosted git repository.
more 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 0ec8ce1 KNOX-2287 KnoxCLI convert topology to provider and descriptor
(#292)
0ec8ce1 is described below
commit 0ec8ce1c9a160281cf5381611a10a20f8c394602
Author: Sandeep Moré <[email protected]>
AuthorDate: Wed Mar 18 13:57:02 2020 -0400
KNOX-2287 KnoxCLI convert topology to provider and descriptor (#292)
---
.../org/apache/knox/gateway/GatewayMessages.java | 12 +
.../gateway/model/DescriptorConfiguration.java | 126 ++++++
.../knox/gateway/model/ProviderConfiguration.java | 41 ++
.../org/apache/knox/gateway/model/Topology.java | 477 +++++++++++++++++++++
.../java/org/apache/knox/gateway/util/KnoxCLI.java | 175 +++++++-
.../knox/gateway/util/TopologyToDescriptor.java | 270 ++++++++++++
.../org/apache/knox/gateway/util/KnoxCLITest.java | 82 ++++
gateway-server/src/test/resources/token-test.xml | 70 +++
8 files changed, 1249 insertions(+), 4 deletions(-)
diff --git
a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java
b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java
index 46f6ba8..e37f40a 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java
@@ -682,4 +682,16 @@ public interface GatewayMessages {
@Message(level = MessageLevel.ERROR, text = "Failed to save gateway status")
void failedToSaveGatewayStatus();
+
+ @Message(level = MessageLevel.ERROR, text = "Error validating topology {0}")
+ void errorValidatingTopology(String topologyName);
+
+ @Message(level = MessageLevel.ERROR, text = "Error parsing topology {0}")
+ void errorParsingTopology(String topologyName);
+
+ @Message(level = MessageLevel.ERROR, text = "Error creating provider
configuration {0} from topology {1}, cause: {2}")
+ void errorSavingProviderConfiguration(String providerPath, String
topologyName, String message);
+
+ @Message(level = MessageLevel.ERROR, text = "Error creating provider
descriptor {0} from topology {1}, cause: {2}")
+ void errorSavingDescriptorConfiguration(String providerPath, String
topologyName, String message);
}
diff --git
a/gateway-server/src/main/java/org/apache/knox/gateway/model/DescriptorConfiguration.java
b/gateway-server/src/main/java/org/apache/knox/gateway/model/DescriptorConfiguration.java
new file mode 100644
index 0000000..74b1d87
--- /dev/null
+++
b/gateway-server/src/main/java/org/apache/knox/gateway/model/DescriptorConfiguration.java
@@ -0,0 +1,126 @@
+/*
+ * 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.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+public class DescriptorConfiguration {
+ @JsonProperty("discovery-type")
+ private String discoveryType;
+
+ @JsonProperty("discovery-address")
+ private String discoveryAddress;
+
+ @JsonProperty("discovery-user")
+ private String discoveryUser;
+
+ @JsonProperty("discovery-pwd-alias")
+ private String discoveryPasswordAlias;
+
+ @JsonProperty("provider-config-ref")
+ private String providerConfig;
+
+ @JsonProperty("cluster")
+ private String cluster;
+
+ @JsonProperty("services")
+ private List<Topology.Service> services;
+
+ @JsonProperty("applications")
+ private List<Topology.Application> applications;
+
+ private String name;
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public DescriptorConfiguration() {
+ super();
+ }
+
+ public String getDiscoveryType() {
+ return discoveryType;
+ }
+
+ public void setDiscoveryType(String discoveryType) {
+ this.discoveryType = discoveryType;
+ }
+
+ public String getDiscoveryAddress() {
+ return discoveryAddress;
+ }
+
+ public void setDiscoveryAddress(String discoveryAddress) {
+ this.discoveryAddress = discoveryAddress;
+ }
+
+ public String getDiscoveryUser() {
+ return discoveryUser;
+ }
+
+ public void setDiscoveryUser(String discoveryUser) {
+ this.discoveryUser = discoveryUser;
+ }
+
+ public String getDiscoveryPasswordAlias() {
+ return discoveryPasswordAlias;
+ }
+
+ public void setDiscoveryPasswordAlias(String discoveryPasswordAlias) {
+ this.discoveryPasswordAlias = discoveryPasswordAlias;
+ }
+
+ public String getProviderConfig() {
+ return providerConfig;
+ }
+
+ public void setProviderConfig(String providerConfig) {
+ this.providerConfig = providerConfig;
+ }
+
+ public String getCluster() {
+ return cluster;
+ }
+
+ public void setCluster(String cluster) {
+ this.cluster = cluster;
+ }
+
+ public List<Topology.Service> getServices() {
+ return services;
+ }
+
+ public void setServices(List<Topology.Service> services) {
+ this.services = services;
+ }
+
+ public List<Topology.Application> getApplications() {
+ return applications;
+ }
+
+ public void setApplications(List<Topology.Application> applications) {
+ this.applications = applications;
+ }
+}
diff --git
a/gateway-server/src/main/java/org/apache/knox/gateway/model/ProviderConfiguration.java
b/gateway-server/src/main/java/org/apache/knox/gateway/model/ProviderConfiguration.java
new file mode 100644
index 0000000..f05a358
--- /dev/null
+++
b/gateway-server/src/main/java/org/apache/knox/gateway/model/ProviderConfiguration.java
@@ -0,0 +1,41 @@
+/*
+ * 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.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+public class ProviderConfiguration {
+
+ @JsonProperty("providers")
+ private List<Topology.Provider> providers;
+
+ public ProviderConfiguration() {
+ super();
+ }
+
+ public List<Topology.Provider> getProviders() {
+ return providers;
+ }
+
+ public void setProviders(List<Topology.Provider> providers) {
+ this.providers = providers;
+ }
+
+}
diff --git
a/gateway-server/src/main/java/org/apache/knox/gateway/model/Topology.java
b/gateway-server/src/main/java/org/apache/knox/gateway/model/Topology.java
new file mode 100644
index 0000000..419b652
--- /dev/null
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/model/Topology.java
@@ -0,0 +1,477 @@
+/*
+ * 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.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+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 javax.xml.bind.annotation.XmlRootElement;
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+@XmlRootElement(name = "topology")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class Topology {
+
+ @XmlElement(name = "provider")
+ @XmlElementWrapper(name = "gateway")
+ public List<Provider> providers;
+ @XmlElement(name = "service")
+ public List<Service> services;
+ @XmlElement
+ private URI uri;
+ @XmlElement
+ private String name;
+ @XmlElement
+ private String path;
+ @XmlElement
+ private long timestamp;
+ @XmlElement(name = "generated")
+ private boolean isGenerated;
+ @XmlElement(name = "application")
+ private List<Application> applications;
+
+ public Topology() {
+ }
+
+ public URI getUri() {
+ return uri;
+ }
+
+ public void setUri(URI uri) {
+ this.uri = uri;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(long timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String defaultServicePath) {
+ this.path = defaultServicePath;
+ }
+
+ public boolean isGenerated() {
+ return isGenerated;
+ }
+
+ public void setGenerated(boolean isGenerated) {
+ this.isGenerated = isGenerated;
+ }
+
+ public List<Service> getServices() {
+ if (services == null) {
+ services = new ArrayList<>();
+ }
+ return services;
+ }
+
+ public void setServices(List<Service> services) {
+ this.services = services;
+ }
+
+ public List<Application> getApplications() {
+ if (applications == null) {
+ applications = new ArrayList<>();
+ }
+ return applications;
+ }
+
+ public void setApplications(List<Application> applications) {
+ this.applications = applications;
+ }
+
+ public List<Provider> getProviders() {
+ if (providers == null) {
+ providers = new ArrayList<>();
+ }
+ return providers;
+ }
+
+ public void setProviders(List<Provider> providers) {
+ this.providers = providers;
+ }
+
+ @XmlAccessorType(XmlAccessType.NONE)
+ public static class Service {
+ @XmlElement
+ private String role;
+
+ @XmlElement
+ private String name;
+
+ @JsonProperty("version")
+ @XmlElement
+ private String version;
+
+ @XmlElement(name = "param")
+ @JsonDeserialize(using = ParamDeserializer.class)
+ private List<Param> params;
+
+ @JsonProperty("urls")
+ @XmlElement(name = "url")
+ private List<String> urls;
+
+ public Service() {
+ }
+
+ @JsonProperty("name")
+ public String getRole() {
+ return role;
+ }
+
+ public void setRole(String role) {
+ this.role = role;
+ }
+
+ @JsonIgnore
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public List<String> getUrls() {
+ if (urls == null) {
+ urls = new ArrayList<>();
+ }
+ return urls;
+ }
+
+ public void setUrls(List<String> urls) {
+ this.urls = urls;
+ }
+
+ public List<Param> getParams() {
+ if (params == null) {
+ params = new ArrayList<>();
+ }
+ return params;
+ }
+
+ public void setParams(List<Param> params) {
+ this.params = params;
+ }
+
+ @JsonProperty("params")
+ public Map<String, String> getJsonParams() {
+ Map<String, String> result = new LinkedHashMap<>();
+ this.getParams().forEach(p -> result.put(p.getName(), p.getValue()));
+ return result;
+ }
+ }
+
+ @XmlAccessorType(XmlAccessType.NONE)
+ @JsonPropertyOrder({ "role", "name", "enabled", "param" })
+ public static class Provider {
+
+ private static Map<String, Integer> SHIRO_PROVIDER_PARAM_ORDER = new
HashMap<>();
+
+ static {
+ SHIRO_PROVIDER_PARAM_ORDER.put("sessionTimeout", 0);
+ SHIRO_PROVIDER_PARAM_ORDER.put("main.ldapRealm", 1);
+ SHIRO_PROVIDER_PARAM_ORDER.put("main.ldapContextFactory", 2);
+ SHIRO_PROVIDER_PARAM_ORDER.put("main.ldapGroupContextFactory", 3);
+ SHIRO_PROVIDER_PARAM_ORDER.put("main.ldapRealm.contextFactory", 4);
+ SHIRO_PROVIDER_PARAM_ORDER.put("main.ldapRealm.userDnTemplate", 5);
+ SHIRO_PROVIDER_PARAM_ORDER.put("main.ldapRealm.contextFactory.url", 6);
+ SHIRO_PROVIDER_PARAM_ORDER
+ .put("main.ldapRealm.contextFactory.authenticationMechanism", 7);
+ SHIRO_PROVIDER_PARAM_ORDER.put("urls./**", 8);
+ }
+
+ @JsonProperty("role")
+ @XmlElement
+ private String role;
+ @JsonProperty("name")
+ @XmlElement
+ private String name;
+ @XmlElement
+ private boolean enabled;
+ @XmlElement(name = "param")
+ @JsonDeserialize(using = ParamDeserializer.class)
+ private List<Param> params;
+
+ public Provider() {
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @JsonProperty("enabled")
+ public String isEnabled() {
+ return Boolean.toString(enabled);
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public String getRole() {
+ return role;
+ }
+
+ public void setRole(String role) {
+ this.role = role;
+ }
+
+ public List<Param> getParams() {
+ /* for Shiro provider order is important */
+ if ("ShiroProvider".equalsIgnoreCase(name) && params != null &&
!params.isEmpty()) {
+ params.sort(Comparator.comparing((Param p) ->
SHIRO_PROVIDER_PARAM_ORDER
+ .getOrDefault(p.getName(), Integer.MAX_VALUE)).thenComparing(
+ (Param p) -> SHIRO_PROVIDER_PARAM_ORDER.getOrDefault(p.getName(),
Integer.MAX_VALUE)));
+ }
+ if (params == null) {
+ params = new ArrayList<>();
+ }
+ return params;
+ }
+
+ public void setParams(List<Param> params) {
+ this.params = params;
+ }
+
+ @JsonProperty("params")
+ public Map<String, String> getJsonParams() {
+ Map<String, String> result = new LinkedHashMap<>();
+ this.getParams().forEach(p -> result.put(p.getName(), p.getValue()));
+ return result;
+ }
+ }
+
+ public static class Descriptor {
+ @JsonProperty("discovery-type")
+ private String discoveryType;
+
+ @JsonProperty("discovery-address")
+ private String discoveryAddress;
+
+ @JsonProperty("discovery-user")
+ private String discoveryUser;
+
+ @JsonProperty("discovery-pwd-alias")
+ private String discoveryPasswordAlias;
+
+ @JsonProperty("provider-config-ref")
+ private String providerConfig;
+
+ @JsonProperty("cluster")
+ private String cluster;
+
+ @JsonProperty("services")
+ private List<Service> services;
+
+ @JsonProperty("applications")
+ private List<Application> applications;
+
+ private String name;
+
+ public Descriptor() {
+ super();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDiscoveryType() {
+ return discoveryType;
+ }
+
+ public void setDiscoveryType(String discoveryType) {
+ this.discoveryType = discoveryType;
+ }
+
+ public String getDiscoveryAddress() {
+ return discoveryAddress;
+ }
+
+ public void setDiscoveryAddress(String discoveryAddress) {
+ this.discoveryAddress = discoveryAddress;
+ }
+
+ public String getDiscoveryUser() {
+ return discoveryUser;
+ }
+
+ public void setDiscoveryUser(String discoveryUser) {
+ this.discoveryUser = discoveryUser;
+ }
+
+ public String getDiscoveryPasswordAlias() {
+ return discoveryPasswordAlias;
+ }
+
+ public void setDiscoveryPasswordAlias(String discoveryPasswordAlias) {
+ this.discoveryPasswordAlias = discoveryPasswordAlias;
+ }
+
+ public String getProviderConfig() {
+ return providerConfig;
+ }
+
+ public void setProviderConfig(String providerConfig) {
+ this.providerConfig = providerConfig;
+ }
+
+ public String getCluster() {
+ return cluster;
+ }
+
+ public void setCluster(String cluster) {
+ this.cluster = cluster;
+ }
+
+ public List<Service> getServices() {
+ return services;
+ }
+
+ public void setServices(List<Service> services) {
+ this.services = services;
+ }
+
+ public List<Application> getApplications() {
+ return applications;
+ }
+
+ public void setApplications(List<Application> applications) {
+ this.applications = applications;
+ }
+ }
+
+ @XmlAccessorType(XmlAccessType.NONE)
+ public static class Application extends Service {
+
+ @Override
+ public String getRole() {
+ return getName();
+ }
+
+ @Override
+ public void setRole(String role) {
+ setName(role);
+ }
+
+ }
+
+ @XmlAccessorType(XmlAccessType.NONE)
+ public static class Param {
+
+ @XmlElement
+ private String name;
+ @XmlElement
+ private String value;
+
+ public Param() {
+ }
+
+ public Param(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+ }
+
+ /* custom deserializer for params field */
+ public static class ParamDeserializer extends JsonDeserializer<List> {
+ final ObjectMapper mapper = new ObjectMapper();
+ @Override
+ public List<Topology.Param> deserialize(final JsonParser jsonParser,
+ final DeserializationContext deserializationContext)
+ throws IOException {
+
+ final JsonNode node = jsonParser.getCodec().readTree(jsonParser);
+ final Map<String, String> result = mapper
+ .convertValue(node, new TypeReference<Map<String, String>>() {
+ });
+ final List<Topology.Param> params = new ArrayList();
+ result.forEach((k,v) -> params.add(new Param(k, v)));
+ return params;
+ }
+ }
+
+}
+
diff --git
a/gateway-server/src/main/java/org/apache/knox/gateway/util/KnoxCLI.java
b/gateway-server/src/main/java/org/apache/knox/gateway/util/KnoxCLI.java
index 7b69a15..9e5ab5d 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/util/KnoxCLI.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/util/KnoxCLI.java
@@ -19,6 +19,8 @@ package org.apache.knox.gateway.util;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.util.Tool;
@@ -33,10 +35,10 @@ import org.apache.knox.gateway.config.GatewayConfig;
import org.apache.knox.gateway.config.impl.GatewayConfigImpl;
import org.apache.knox.gateway.deploy.DeploymentFactory;
import org.apache.knox.gateway.services.CLIGatewayServices;
-import org.apache.knox.gateway.services.ServiceType;
import org.apache.knox.gateway.services.GatewayServices;
import org.apache.knox.gateway.services.Service;
import org.apache.knox.gateway.services.ServiceLifecycleException;
+import org.apache.knox.gateway.services.ServiceType;
import
org.apache.knox.gateway.services.config.client.RemoteConfigurationRegistryClient;
import
org.apache.knox.gateway.services.config.client.RemoteConfigurationRegistryClientService;
import org.apache.knox.gateway.services.security.AliasService;
@@ -112,7 +114,8 @@ public class KnoxCLI extends Configured implements Tool {
" [" + RemoteRegistryUploadDescriptorCommand.USAGE + "]\n" +
" [" + RemoteRegistryDeleteProviderConfigCommand.USAGE + "]\n" +
" [" + RemoteRegistryDeleteDescriptorCommand.USAGE + "]\n" +
- " [" + RemoteRegistryGetACLCommand.USAGE + "]\n";
+ " [" + RemoteRegistryGetACLCommand.USAGE + "]\n" +
+ " [" + TopologyConverter.USAGE + "]\n";
/** allows stdout to be captured if necessary */
public PrintStream out = System.out;
@@ -136,9 +139,18 @@ public class KnoxCLI extends Configured implements Tool {
private String remoteRegistryClient;
private String remoteRegistryEntryName;
+ private String type;
+ private String topologyName;
+ private String providerName;
+ private String descriptorName;
+ private String outputDir;
+ private String discoveryUrl;
+ private String discoveryUser;
+ private String discoveryPasswordAlias;
+ private String discoveryType;
+
// For testing only
private String master;
- private String type;
@Override
public int run(String[] args) throws Exception {
@@ -316,6 +328,54 @@ public class KnoxCLI extends Configured implements Tool {
return -1;
}
this.port = args[++i];
+ } else if (args[i].equals("--provider-name")) {
+ if( i+1 >= args.length || args[i+1].startsWith( "-" ) ) {
+ printKnoxShellUsage();
+ return -1;
+ }
+ this.providerName = args[++i];
+ } else if (args[i].equals("--topology-name")) {
+ if( i+1 >= args.length || args[i+1].startsWith( "-" ) ) {
+ printKnoxShellUsage();
+ return -1;
+ }
+ this.topologyName = args[++i];
+ } else if (args[i].equals("--descriptor-name")) {
+ if( i+1 >= args.length || args[i+1].startsWith( "-" ) ) {
+ printKnoxShellUsage();
+ return -1;
+ }
+ this.descriptorName = args[++i];
+ } else if (args[i].equals("--output-dir")) {
+ if( i+1 >= args.length || args[i+1].startsWith( "-" ) ) {
+ printKnoxShellUsage();
+ return -1;
+ }
+ this.outputDir = args[++i];
+ } else if (args[i].equals("--discovery-url")) {
+ if( i+1 >= args.length || args[i+1].startsWith( "-" ) ) {
+ printKnoxShellUsage();
+ return -1;
+ }
+ this.discoveryUrl = args[++i];
+ } else if (args[i].equals("--discovery-user")) {
+ if( i+1 >= args.length || args[i+1].startsWith( "-" ) ) {
+ printKnoxShellUsage();
+ return -1;
+ }
+ this.discoveryUser = args[++i];
+ } else if (args[i].equals("--discovery-pwd-alias")) {
+ if( i+1 >= args.length || args[i+1].startsWith( "-" ) ) {
+ printKnoxShellUsage();
+ return -1;
+ }
+ this.discoveryPasswordAlias = args[++i];
+ } else if (args[i].equals("--discovery-type")) {
+ if( i+1 >= args.length || args[i+1].startsWith( "-" ) ) {
+ printKnoxShellUsage();
+ return -1;
+ }
+ this.discoveryType = args[++i];
} else if (args[i].equals("--master")) {
// For testing only
if( i+1 >= args.length || args[i+1].startsWith( "-" ) ) {
@@ -407,9 +467,16 @@ public class KnoxCLI extends Configured implements Tool {
printKnoxShellUsage();
return -1;
}
+ } else if (args[i].equalsIgnoreCase("convert-topology")) {
+ if (args.length >= 5) {
+ command = new TopologyConverter();
+ }
+ else {
+ printKnoxShellUsage();
+ return -1;
+ }
} else {
printKnoxShellUsage();
- //ToolRunner.printGenericCommandUsage(System.err);
return -1;
}
}
@@ -489,6 +556,9 @@ public class KnoxCLI extends Configured implements Tool {
out.println(RemoteRegistryDeleteDescriptorCommand.USAGE + "\n\n" +
RemoteRegistryDeleteDescriptorCommand.DESC);
out.println();
out.println( div );
+ out.println(TopologyConverter.USAGE + "\n\n" + TopologyConverter.DESC);
+ out.println();
+ out.println( div );
}
}
@@ -2090,6 +2160,103 @@ public class KnoxCLI extends Configured implements Tool
{
}
}
+ public class TopologyConverter extends Command {
+
+ public static final String USAGE =
+ "convert-topology --path \"path/to/topology.xml\" --provider-name
my-provider.json [--descriptor-name my-descriptor.json] "
+ + "[--topology-name topologyName] [--output-path
\"path/to/configs/\"] [--force] [--cluster clusterName] [--discovery-url url] "
+ + "[--discovery-user discoveryUser] [--discovery-pwd-alias
discoveryPasswordAlias] [--discovery-type discoveryType]";
+ public static final String DESC =
+ "Convert Knox topology file to provider and descriptor config files \n"
+ + "Options are as follows: \n"
+ + "--path (required) path to topology xml file \n"
+ + "--provider-name (required) name of the provider json config
file (including .json extension) \n"
+ + "--descriptor-name (optional) name of descriptor json config
file (including .json extension) \n"
+ + "--topology-name (optional) topology-name can be use instead of
--path option, if used, KnoxCLI will attempt to find topology from deployed
topologies.\n"
+ + "\t if not provided topology name will be used as descriptor
name \n"
+ + "--output-path (optional) output directory to save provider and
descriptor config files \n"
+ + "\t if not provided config files will be saved in appropriate
Knox config directory \n"
+ + "--force (optional) force rewriting of existing files, if not
used, command will fail when the configs files with same name already exist. \n"
+ + "--cluster (optional) cluster name, required for service
discovery \n"
+ + "--discovery-url (optional) service discovery URL, required for
service discovery \n"
+ + "--discovery-user (optional) service discovery user, required
for service discovery \n"
+ + "--discovery-pwd-alias (optional) password alias for service
discovery user, required for service discovery \n"
+ + "--discovery-type (optional) service discovery type, required
for service discovery \n";
+
+ public TopologyConverter() {
+ super();
+ }
+
+ @Override
+ public void execute() throws Exception {
+ if (StringUtils.isBlank(FilenameUtils.getExtension(providerName))
+ || StringUtils.isBlank(FilenameUtils.getExtension(descriptorName))) {
+ throw new IllegalArgumentException(
+ " JSON extension is required for provider and descriptor file
names");
+ }
+
+ final TopologyToDescriptor converter = new TopologyToDescriptor();
+
+ converter.setForce(force);
+ if (!StringUtils.isBlank(topologyName)) {
+ converter.setTopologyPath(
+ getGatewayConfig().getGatewayTopologyDir() + File.separator
+ + topologyName);
+ } else if (!StringUtils.isBlank(path)) {
+ converter.setTopologyPath(path);
+ } else {
+ throw new IllegalArgumentException(
+ "Please specify either --path or --topology-name option");
+ }
+ if (!StringUtils.isBlank(providerName)) {
+ converter.setProviderName(providerName);
+ }
+ if (!StringUtils.isBlank(descriptorName)) {
+ converter.setDescriptorName(descriptorName);
+ }
+ /* if output location is provided then use it */
+ if (!StringUtils.isBlank(outputDir)) {
+ converter.setProviderConfigDir(outputDir);
+ converter.setDescriptorConfigDir(outputDir);
+ } else {
+ converter.setProviderConfigDir(
+ getGatewayConfig().getGatewayProvidersConfigDir());
+ converter.setDescriptorConfigDir(
+ getGatewayConfig().getGatewayDescriptorsDir());
+ }
+ /* set discovery params */
+ if (!StringUtils.isBlank(cluster)) {
+ converter.setCluster(cluster);
+ }
+ if (!StringUtils.isBlank(discoveryUrl)) {
+ converter.setDiscoveryUrl(discoveryUrl);
+ }
+ if (!StringUtils.isBlank(discoveryUser)) {
+ converter.setDiscoveryUser(discoveryUser);
+ }
+ if (!StringUtils.isBlank(discoveryPasswordAlias)) {
+ converter.setDiscoveryPasswordAlias(discoveryPasswordAlias);
+ }
+ if (!StringUtils.isBlank(discoveryType)) {
+ converter.setDiscoveryType(discoveryType);
+ }
+
+ converter.validate();
+ converter.convert();
+
+ final String topoName = StringUtils.isBlank(topologyName) ?
FilenameUtils.getBaseName(path) : topologyName;
+ out.println(
+ "Provider " + providerName + " and descriptor " + descriptorName
+ + " generated for topology " + topoName
+ + "\n");
+ }
+
+ @Override
+ public String getUsage() {
+ return USAGE + ":\n\n" + DESC;
+ }
+
+ }
private static Properties loadBuildProperties() {
Properties properties = new Properties();
diff --git
a/gateway-server/src/main/java/org/apache/knox/gateway/util/TopologyToDescriptor.java
b/gateway-server/src/main/java/org/apache/knox/gateway/util/TopologyToDescriptor.java
new file mode 100644
index 0000000..852d937
--- /dev/null
+++
b/gateway-server/src/main/java/org/apache/knox/gateway/util/TopologyToDescriptor.java
@@ -0,0 +1,270 @@
+/*
+ * 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.util;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.knox.gateway.GatewayMessages;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.knox.gateway.model.DescriptorConfiguration;
+import org.apache.knox.gateway.model.ProviderConfiguration;
+import org.apache.knox.gateway.model.Topology;
+import org.xml.sax.SAXException;
+
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Validator;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Locale;
+
+/**
+ * A helper class to convert topology xml file to descriptor and provider
+ * model.
+ */
+public class TopologyToDescriptor {
+
+ private static final GatewayMessages LOG = MessagesFactory
+ .get(GatewayMessages.class);
+ private static final ObjectMapper mapper = new ObjectMapper();
+ private static final String SCHEMA_FILE = "/conf/topology-v1.xsd";
+
+ static {
+ /* skip printing out null fields */
+ mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ }
+
+ private String topologyPath;
+ private String providerName;
+ private String descriptorName;
+ private String discoveryUrl;
+ private String discoveryUser;
+ private String discoveryPasswordAlias;
+ private String discoveryType;
+ private String cluster;
+ private String providerConfigDir;
+ private String descriptorConfigDir;
+ private boolean force;
+
+ public TopologyToDescriptor() {
+ super();
+ }
+
+ /**
+ * A function that validates the given topology
+ */
+ private void validateTopology(final String xsd, final String topologyFile)
+ throws IOException, SAXException {
+ try (InputStream topologyFileStream = Files
+ .newInputStream(Paths.get(topologyFile))) {
+ final Source xmlSource = new StreamSource(topologyFileStream);
+ final Schema schema = getSchema(xsd);
+ final Validator validator = schema.newValidator();
+ validator.validate(xmlSource);
+ } catch (IOException | SAXException e) {
+ LOG.errorValidatingTopology(topologyFile);
+ throw e;
+ }
+ }
+
+ private Topology parseTopology(final String xsd, final String topologyFile)
+ throws JAXBException, SAXException, IOException {
+ try (InputStream topologyFileStream = Files
+ .newInputStream(Paths.get(topologyFile))) {
+ final Schema schema = getSchema(xsd);
+ final JAXBContext jc = JAXBContext.newInstance(Topology.class);
+ final Unmarshaller unmarshaller = jc.createUnmarshaller();
+ unmarshaller.setSchema(schema);
+ return (Topology) unmarshaller.unmarshal(topologyFileStream);
+ } catch (SAXException | JAXBException | IOException e) {
+ LOG.errorParsingTopology(topologyFile);
+ throw e;
+ }
+ }
+
+ private Schema getSchema(final String xsd) throws SAXException {
+ final SchemaFactory schemaFactory = SchemaFactory
+ .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ final URL schemaUrl = TopologyToDescriptor.class.getResource(xsd);
+ return schemaFactory.newSchema(schemaUrl);
+ }
+
+ /**
+ * Validate topology
+ */
+ public void validate() throws IOException, SAXException {
+ validateTopology(SCHEMA_FILE, topologyPath);
+ }
+
+ /**
+ * Convert topology to provider and descriptor
+ */
+ public void convert() throws IOException, JAXBException, SAXException {
+ final Topology topology = parseTopology(SCHEMA_FILE, topologyPath);
+ saveProvider(topology);
+ saveDescriptor(topology, providerName);
+ }
+
+ private void saveProvider(final Topology topology) throws IOException {
+ try {
+ final ProviderConfiguration provider = new ProviderConfiguration();
+ if (topology.getProviders() != null) {
+ provider.setProviders(topology.getProviders());
+ }
+
+ final File providerFile = new File(
+ providerConfigDir + File.separator + providerName);
+
+ fileCheck(providerFile);
+
+ mapper.writerWithDefaultPrettyPrinter()
+ .writeValue(providerFile, provider);
+ } catch (final IOException e) {
+ LOG.errorSavingProviderConfiguration(providerName, topologyPath,
+ e.toString());
+ throw e;
+ }
+ }
+
+ private void saveDescriptor(final Topology topology,
+ final String providerName) throws IOException {
+ final DescriptorConfiguration descriptorConfiguration = new
DescriptorConfiguration();
+
+ descriptorConfiguration
+ .setProviderConfig(FilenameUtils.removeExtension(providerName));
+
+ if (!StringUtils.isBlank(discoveryUrl)) {
+ descriptorConfiguration.setDiscoveryAddress(discoveryUrl);
+ }
+
+ if (!StringUtils.isBlank(discoveryUser)) {
+ descriptorConfiguration.setDiscoveryUser(discoveryUser);
+ }
+
+ if (!StringUtils.isBlank(discoveryPasswordAlias)) {
+
descriptorConfiguration.setDiscoveryPasswordAlias(discoveryPasswordAlias);
+ }
+
+ if (!StringUtils.isBlank(discoveryType)) {
+ descriptorConfiguration.setDiscoveryType(discoveryType);
+ }
+
+ if (!StringUtils.isBlank(cluster)) {
+ descriptorConfiguration.setCluster(cluster);
+ }
+
+ if (topology.getName() != null) {
+ descriptorConfiguration.setName(topology.getName());
+ }
+
+ if (topology.getApplications() != null) {
+ descriptorConfiguration.setApplications(topology.getApplications());
+ }
+
+ if (topology.getServices() != null) {
+ descriptorConfiguration.setServices(topology.getServices());
+ }
+
+ final File descriptorFile = new File(
+ descriptorConfigDir + File.separator + descriptorName);
+
+ fileCheck(descriptorFile);
+
+ try {
+ mapper.writerWithDefaultPrettyPrinter()
+ .writeValue(descriptorFile, descriptorConfiguration);
+ } catch (IOException e) {
+ LOG.errorSavingDescriptorConfiguration(descriptorName, topologyPath,
+ e.toString());
+ throw e;
+ }
+ }
+
+ /**
+ * Check whether the file exists and can be overwritten.
+ *
+ * @param file
+ * @throws IOException
+ */
+ private void fileCheck(final File file) throws IOException {
+ if (!force && file.exists()) {
+ throw new IOException(String
+ .format(Locale.ROOT, "File %s already exist, use --force option to
overwrite.",
+ file.getAbsolutePath()));
+ }
+ /* make sure file and directories are in place */
+ Files.createDirectories(file.toPath().getParent());
+ file.createNewFile();
+ }
+
+ public void setDiscoveryUrl(String discoveryUrl) {
+ this.discoveryUrl = discoveryUrl;
+ }
+
+ public void setDiscoveryUser(String discoveryUser) {
+ this.discoveryUser = discoveryUser;
+ }
+
+ public void setDiscoveryPasswordAlias(String discoveryPasswordAlias) {
+ this.discoveryPasswordAlias = discoveryPasswordAlias;
+ }
+
+ public void setDiscoveryType(String discoveryType) {
+ this.discoveryType = discoveryType;
+ }
+
+ public void setCluster(String cluster) {
+ this.cluster = cluster;
+ }
+
+ public void setTopologyPath(String topologyPath) {
+ this.topologyPath = topologyPath;
+ }
+
+ public void setProviderName(String providerName) {
+ this.providerName = providerName;
+ }
+
+ public void setDescriptorName(String descriptorName) {
+ this.descriptorName = descriptorName;
+ }
+
+ public void setProviderConfigDir(String providerConfigDir) {
+ this.providerConfigDir = providerConfigDir;
+ }
+
+ public void setDescriptorConfigDir(String descriptorConfigDir) {
+ this.descriptorConfigDir = descriptorConfigDir;
+ }
+
+ public void setForce(boolean force) {
+ this.force = force;
+ }
+}
diff --git
a/gateway-server/src/test/java/org/apache/knox/gateway/util/KnoxCLITest.java
b/gateway-server/src/test/java/org/apache/knox/gateway/util/KnoxCLITest.java
index f2ce5f3..8e5995c 100644
--- a/gateway-server/src/test/java/org/apache/knox/gateway/util/KnoxCLITest.java
+++ b/gateway-server/src/test/java/org/apache/knox/gateway/util/KnoxCLITest.java
@@ -17,11 +17,14 @@
*/
package org.apache.knox.gateway.util;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.mycila.xmltool.XMLDoc;
import com.mycila.xmltool.XMLTag;
import org.apache.commons.io.FileUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.knox.gateway.config.impl.GatewayConfigImpl;
+import org.apache.knox.gateway.model.DescriptorConfiguration;
+import org.apache.knox.gateway.model.ProviderConfiguration;
import org.apache.knox.gateway.services.ServiceType;
import
org.apache.knox.gateway.services.config.client.RemoteConfigurationRegistryClient;
import
org.apache.knox.gateway.services.config.client.RemoteConfigurationRegistryClientService;
@@ -39,6 +42,7 @@ import java.io.PrintStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.UUID;
import static org.hamcrest.CoreMatchers.containsString;
@@ -1047,6 +1051,84 @@ public class KnoxCLITest {
}
+ /* Test cli command to convert topology to providers and descriptors */
+ @Test
+ public void testConvertTopology() throws Exception {
+ outContent.reset();
+ Configuration config = new GatewayConfigImpl();
+ URL topologyFileURL = ClassLoader.getSystemResource("token-test.xml");
+ final File topologyFile = Paths.get(topologyFileURL.toURI()).toFile();
+ final File outputDir = createDir();
+ final String providerConfigFileName = "my-provider.json";
+ final String descriptorConfigFileName = "my-descriptor.json";
+ final String clusterName = "myCluster";
+ final String discoveryUrl = "https://localhost:7183";
+ final String discoveryUser = "discoveryUser";
+ final String discoveryType = "ClouderaManager";
+ final String discoveryPwdAlias = "discovery";
+ final ObjectMapper mapper = new ObjectMapper();
+
+ try {
+ KnoxCLI cli = new KnoxCLI();
+ cli.setConf(config);
+
+ // This is only to get the gateway services initialized
+ cli.run(new String[]{"convert-topology", "--master", "master",
+ "--path", topologyFile.getAbsolutePath(),
+ "--provider-name", providerConfigFileName,
+ "--descriptor-name", descriptorConfigFileName,
+ "--output-dir", outputDir.getAbsolutePath(),
+ "--force",
+ "--cluster", clusterName,
+ "--discovery-url", discoveryUrl,
+ "--discovery-user", discoveryUser,
+ "--discovery-pwd-alias", discoveryPwdAlias,
+ "--discovery-type", discoveryType});
+
+ final File providerConfigFile = new
File(outputDir+File.separator+providerConfigFileName);
+ final File descriptorConfigFile = new
File(outputDir+File.separator+descriptorConfigFileName);
+
+ assertTrue("Provider config file not created",
providerConfigFile.exists());
+ assertTrue("Descriptor config file not created",
descriptorConfigFile.exists());
+
+ final ProviderConfiguration providerJson =
mapper.readValue(providerConfigFile, ProviderConfiguration.class);
+ final DescriptorConfiguration descriptorJson =
mapper.readValue(descriptorConfigFile, DescriptorConfiguration.class);
+
+ assertNotNull("Provider config could not be deserialized", providerJson);
+ assertNotNull("Descriptor config could not be deserialized",
descriptorJson);
+
+ assertEquals(providerJson.getProviders().size(), 1);
+ assertEquals(providerJson.getProviders().get(0).getParams().size(), 8);
+ assertEquals(providerJson.getProviders().get(0).getName(),
"ShiroProvider");
+ assertEquals(providerJson.getProviders().get(0).getRole(),
"authentication");
+ assertEquals(providerJson.getProviders().get(0).isEnabled(), "true");
+
+ /* test param order */
+
assertEquals(providerJson.getProviders().get(0).getParams().get(0).getName(),
"sessionTimeout");
+
assertEquals(providerJson.getProviders().get(0).getParams().get(3).getName(),
"main.ldapRealm.contextFactory");
+
assertEquals(providerJson.getProviders().get(0).getParams().get(3).getName(),
"main.ldapRealm.contextFactory");
+
assertEquals(providerJson.getProviders().get(0).getParams().get(5).getValue(),
"ldap://localhost:33389");
+
assertEquals(providerJson.getProviders().get(0).getParams().get(7).getValue(),
"authcBasic");
+
+ assertEquals(descriptorJson.getDiscoveryType(), discoveryType);
+ assertEquals(descriptorJson.getDiscoveryAddress(), discoveryUrl);
+ assertEquals(descriptorJson.getDiscoveryPasswordAlias(),
discoveryPwdAlias);
+ assertEquals(descriptorJson.getDiscoveryUser(), discoveryUser);
+ assertEquals(descriptorJson.getCluster(), clusterName);
+ assertEquals(descriptorJson.getServices().size(), 1);
+ assertEquals(descriptorJson.getServices().get(0).getRole(), "KNOXTOKEN");
+ assertEquals(descriptorJson.getServices().get(0).getParams().size(), 5);
+
+ } finally {
+ FileUtils.deleteQuietly(outputDir);
+ }
+ }
+
+ private File createDir() throws IOException {
+ return TestUtils
+ .createTempDir(this.getClass().getSimpleName() + "-");
+ }
+
private static final String testDescriptorContentJSON = "{\n" +
"
\"discovery-address\":\"http://localhost:8080\",\n" +
"
\"discovery-user\":\"maria_dev\",\n" +
diff --git a/gateway-server/src/test/resources/token-test.xml
b/gateway-server/src/test/resources/token-test.xml
new file mode 100644
index 0000000..f3b6920
--- /dev/null
+++ b/gateway-server/src/test/resources/token-test.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--==============================================-->
+<!-- DO NOT EDIT. This is an auto-generated file. -->
+<!--==============================================-->
+<topology>
+ <generated>true</generated>
+ <gateway>
+ <provider>
+ <role>authentication</role>
+ <name>ShiroProvider</name>
+ <enabled>true</enabled>
+ <param>
+ <name>sessionTimeout</name>
+ <value>20</value>
+ </param>
+ <param>
+ <name>main.ldapRealm</name>
+ <value>org.apache.knox.gateway.shirorealm.KnoxLdapRealm</value>
+ </param>
+ <param>
+ <name>main.ldapContextFactory</name>
+
<value>org.apache.knox.gateway.shirorealm.KnoxLdapContextFactory</value>
+ </param>
+ <param>
+ <name>main.ldapRealm.contextFactory</name>
+ <value>$ldapContextFactory</value>
+ </param>
+ <param>
+ <name>main.ldapRealm.userDnTemplate</name>
+ <value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>
+ </param>
+ <param>
+ <name>main.ldapRealm.contextFactory.url</name>
+ <value>ldap://localhost:33389</value>
+ </param>
+ <param>
+
<name>main.ldapRealm.contextFactory.authenticationMechanism</name>
+ <value>simple</value>
+ </param>
+ <param>
+ <name>urls./**</name>
+ <value>authcBasic</value>
+ </param>
+ </provider>
+ </gateway>
+
+ <service>
+ <role>KNOXTOKEN</role>
+ <param>
+ <name>knox.token.ttl</name>
+ <value>30000</value>
+ </param>
+ <param>
+ <name>knox.token.audiences</name>
+ <value>idbroker</value>
+ </param>
+ <param>
+ <name>knox.token.exp.server-managed</name>
+ <value>true</value>
+ </param>
+ <param>
+ <name>knox.token.exp.renew-interval</name>
+ <value>60000</value>
+ </param>
+ <param>
+ <name>knox.token.renewer.whitelist</name>
+ <value>admin</value>
+ </param>
+ </service>
+</topology>