Repository: knox Updated Branches: refs/heads/master 591e0bd3c -> a6d2f5240
KNOX-1224 - Knox Proxy HADispatcher to support Atlas in HA (Nixon Rodrigues via Phil Zampino) Project: http://git-wip-us.apache.org/repos/asf/knox/repo Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/a6d2f524 Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/a6d2f524 Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/a6d2f524 Branch: refs/heads/master Commit: a6d2f52409e1b9cf497d76cd799ad73f17579092 Parents: 591e0bd Author: Phil Zampino <[email protected]> Authored: Fri Mar 23 14:10:21 2018 -0400 Committer: Phil Zampino <[email protected]> Committed: Fri Mar 23 14:10:41 2018 -0400 ---------------------------------------------------------------------- .../gateway/ha/dispatch/AtlasApiHaDispatch.java | 82 ++++++++++++ .../gateway/ha/dispatch/AtlasHaDispatch.java | 81 ++++++++++++ .../provider/impl/AtlasZookeeperURLManager.java | 95 ++++++++++++++ ...g.apache.knox.gateway.ha.provider.URLManager | 3 +- .../impl/AtlasZookeeperURLManagerTest.java | 129 +++++++++++++++++++ .../services/atlas-api/0.8.0/service.xml | 2 +- .../resources/services/atlas/0.8.0/service.xml | 4 +- 7 files changed, 392 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/knox/blob/a6d2f524/gateway-provider-ha/src/main/java/org/apache/knox/gateway/ha/dispatch/AtlasApiHaDispatch.java ---------------------------------------------------------------------- diff --git a/gateway-provider-ha/src/main/java/org/apache/knox/gateway/ha/dispatch/AtlasApiHaDispatch.java b/gateway-provider-ha/src/main/java/org/apache/knox/gateway/ha/dispatch/AtlasApiHaDispatch.java new file mode 100644 index 0000000..59fbb9e --- /dev/null +++ b/gateway-provider-ha/src/main/java/org/apache/knox/gateway/ha/dispatch/AtlasApiHaDispatch.java @@ -0,0 +1,82 @@ +/* + * 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.ha.dispatch; + +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpUriRequest; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class AtlasApiHaDispatch extends DefaultHaDispatch { + + private static Set<String> REQUEST_EXCLUDE_HEADERS = new HashSet<>(); + + static { + REQUEST_EXCLUDE_HEADERS.add("Content-Length"); + } + + public AtlasApiHaDispatch() { + setServiceRole("ATLAS-API"); + } + + @Override + public void init() { + super.init(); + } + + @Override + public Set<String> getOutboundResponseExcludeHeaders() { + return Collections.emptySet(); + } + + @Override + public Set<String> getOutboundRequestExcludeHeaders() { + return REQUEST_EXCLUDE_HEADERS; + } + + + @Override + protected void executeRequest(HttpUriRequest outboundRequest, HttpServletRequest inboundRequest, HttpServletResponse outboundResponse) throws IOException { + HttpResponse inboundResponse = null; + try { + inboundResponse = executeOutboundRequest(outboundRequest); + int statusCode = inboundResponse.getStatusLine().getStatusCode(); + Header originalLocationHeader = inboundResponse.getFirstHeader("Location"); + + + if ((statusCode == HttpServletResponse.SC_MOVED_TEMPORARILY || statusCode == HttpServletResponse.SC_TEMPORARY_REDIRECT) && originalLocationHeader != null) { + inboundResponse.removeHeaders("Location"); + failoverRequest(outboundRequest, inboundRequest, outboundResponse, inboundResponse, new Exception("Atlas HA redirection")); + } + + writeOutboundResponse(outboundRequest, inboundRequest, outboundResponse, inboundResponse); + + } catch (IOException e) { + LOG.errorConnectingToServer(outboundRequest.getURI().toString(), e); + failoverRequest(outboundRequest, inboundRequest, outboundResponse, inboundResponse, e); + } + } + +} http://git-wip-us.apache.org/repos/asf/knox/blob/a6d2f524/gateway-provider-ha/src/main/java/org/apache/knox/gateway/ha/dispatch/AtlasHaDispatch.java ---------------------------------------------------------------------- diff --git a/gateway-provider-ha/src/main/java/org/apache/knox/gateway/ha/dispatch/AtlasHaDispatch.java b/gateway-provider-ha/src/main/java/org/apache/knox/gateway/ha/dispatch/AtlasHaDispatch.java new file mode 100644 index 0000000..8d2490d --- /dev/null +++ b/gateway-provider-ha/src/main/java/org/apache/knox/gateway/ha/dispatch/AtlasHaDispatch.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.ha.dispatch; + +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpUriRequest; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class AtlasHaDispatch extends DefaultHaDispatch { + private static Set<String> REQUEST_EXCLUDE_HEADERS = new HashSet<>(); + + static { + REQUEST_EXCLUDE_HEADERS.add("Content-Length"); + } + + public AtlasHaDispatch() { + setServiceRole("ATLAS"); + } + + @Override + public void init() { + super.init(); + } + + @Override + public Set<String> getOutboundResponseExcludeHeaders() { + return Collections.emptySet(); + } + + @Override + public Set<String> getOutboundRequestExcludeHeaders() { + return REQUEST_EXCLUDE_HEADERS; + } + + @Override + protected void executeRequest(HttpUriRequest outboundRequest, HttpServletRequest inboundRequest, HttpServletResponse outboundResponse) throws IOException { + HttpResponse inboundResponse = null; + try { + inboundResponse = executeOutboundRequest(outboundRequest); + + int statusCode = inboundResponse.getStatusLine().getStatusCode(); + Header originalLocationHeader = inboundResponse.getFirstHeader("Location"); + + if((statusCode == HttpServletResponse.SC_MOVED_TEMPORARILY || statusCode == HttpServletResponse.SC_TEMPORARY_REDIRECT) + && originalLocationHeader != null && !originalLocationHeader.getValue().endsWith("login.jsp")){ + inboundResponse.removeHeaders("Location"); + failoverRequest(outboundRequest, inboundRequest, outboundResponse, inboundResponse, new Exception("Atlas HA redirection")); + } + + writeOutboundResponse(outboundRequest, inboundRequest, outboundResponse, inboundResponse); + + } catch (IOException e) { + LOG.errorConnectingToServer(outboundRequest.getURI().toString(), e); + failoverRequest(outboundRequest, inboundRequest, outboundResponse, inboundResponse, e); + } + } + +} http://git-wip-us.apache.org/repos/asf/knox/blob/a6d2f524/gateway-provider-ha/src/main/java/org/apache/knox/gateway/ha/provider/impl/AtlasZookeeperURLManager.java ---------------------------------------------------------------------- diff --git a/gateway-provider-ha/src/main/java/org/apache/knox/gateway/ha/provider/impl/AtlasZookeeperURLManager.java b/gateway-provider-ha/src/main/java/org/apache/knox/gateway/ha/provider/impl/AtlasZookeeperURLManager.java new file mode 100644 index 0000000..0f28c37 --- /dev/null +++ b/gateway-provider-ha/src/main/java/org/apache/knox/gateway/ha/provider/impl/AtlasZookeeperURLManager.java @@ -0,0 +1,95 @@ +/* + * 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.ha.provider.impl; + +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.retry.ExponentialBackoffRetry; +import org.apache.knox.gateway.ha.provider.HaServiceConfig; +import org.apache.knox.gateway.ha.provider.impl.i18n.HaMessages; +import org.apache.knox.gateway.i18n.messages.MessagesFactory; + +import java.nio.charset.Charset; +import java.util.*; + +public class AtlasZookeeperURLManager extends DefaultURLManager { + private static final HaMessages LOG = MessagesFactory.get(HaMessages.class); + + private String zooKeeperEnsemble; + + private String zooKeeperNamespace; + + public static final String APACHE_ATLAS_ACTIVE_SERVER_INFO = "/active_server_info"; + + + @Override + public boolean supportsConfig(HaServiceConfig config) { + if (!( config.getServiceName().equalsIgnoreCase("ATLAS") || config.getServiceName().equalsIgnoreCase("ATLAS-API"))) { + return false; + } + String zookeeperEnsemble = config.getZookeeperEnsemble(); + String zookeeperNamespace = config.getZookeeperNamespace(); + return zookeeperEnsemble != null && zookeeperNamespace != null && !zookeeperEnsemble.trim().isEmpty() && !zookeeperNamespace.trim().isEmpty(); + } + + @Override + public void setConfig(HaServiceConfig config) { + zooKeeperEnsemble = config.getZookeeperEnsemble(); + zooKeeperNamespace = config.getZookeeperNamespace(); + setURLs(lookupURLs()); + } + + public List<String> lookupURLs() { + + List<String> serverHosts = new ArrayList<>(); + CuratorFramework zooKeeperClient = + CuratorFrameworkFactory.builder().connectString(zooKeeperEnsemble) + .retryPolicy(new ExponentialBackoffRetry(1000, 3)) + .build(); + try { + + zooKeeperClient.start(); + + byte[] bytes = zooKeeperClient.getData().forPath("/" + zooKeeperNamespace + APACHE_ATLAS_ACTIVE_SERVER_INFO); + + String activeURL = new String(bytes, Charset.forName("UTF-8")); + + serverHosts.add(activeURL); + + } catch (Exception e) { + + LOG.failedToGetZookeeperUrls(e); + throw new RuntimeException(e); + } finally { + // Close the client connection with ZooKeeper + if (zooKeeperClient != null) { + zooKeeperClient.close(); + } + } + return serverHosts; + } + + + + @Override + public synchronized void markFailed(String url) { + setURLs(lookupURLs()); + super.markFailed(url); + } +} http://git-wip-us.apache.org/repos/asf/knox/blob/a6d2f524/gateway-provider-ha/src/main/resources/META-INF/services/org.apache.knox.gateway.ha.provider.URLManager ---------------------------------------------------------------------- diff --git a/gateway-provider-ha/src/main/resources/META-INF/services/org.apache.knox.gateway.ha.provider.URLManager b/gateway-provider-ha/src/main/resources/META-INF/services/org.apache.knox.gateway.ha.provider.URLManager index d6b9608..2c74b77 100644 --- a/gateway-provider-ha/src/main/resources/META-INF/services/org.apache.knox.gateway.ha.provider.URLManager +++ b/gateway-provider-ha/src/main/resources/META-INF/services/org.apache.knox.gateway.ha.provider.URLManager @@ -19,4 +19,5 @@ org.apache.knox.gateway.ha.provider.impl.HS2ZookeeperURLManager org.apache.knox.gateway.ha.provider.impl.SOLRZookeeperURLManager org.apache.knox.gateway.ha.provider.impl.KafkaZookeeperURLManager -org.apache.knox.gateway.ha.provider.impl.HBaseZookeeperURLManager \ No newline at end of file +org.apache.knox.gateway.ha.provider.impl.HBaseZookeeperURLManager +org.apache.knox.gateway.ha.provider.impl.AtlasZookeeperURLManager http://git-wip-us.apache.org/repos/asf/knox/blob/a6d2f524/gateway-provider-ha/src/test/java/org/apache/knox/gateway/ha/provider/impl/AtlasZookeeperURLManagerTest.java ---------------------------------------------------------------------- diff --git a/gateway-provider-ha/src/test/java/org/apache/knox/gateway/ha/provider/impl/AtlasZookeeperURLManagerTest.java b/gateway-provider-ha/src/test/java/org/apache/knox/gateway/ha/provider/impl/AtlasZookeeperURLManagerTest.java new file mode 100644 index 0000000..56f4061 --- /dev/null +++ b/gateway-provider-ha/src/test/java/org/apache/knox/gateway/ha/provider/impl/AtlasZookeeperURLManagerTest.java @@ -0,0 +1,129 @@ +/** + * 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.ha.provider.impl; + +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.retry.ExponentialBackoffRetry; +import org.apache.curator.test.TestingCluster; +import org.apache.knox.gateway.ha.provider.HaServiceConfig; +import org.apache.knox.gateway.ha.provider.URLManager; +import org.apache.knox.gateway.ha.provider.URLManagerLoader; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +public class AtlasZookeeperURLManagerTest { + + private TestingCluster cluster; + private AtlasZookeeperURLManager manager; + private static String atlasNode1 = "http://atlas.node1:21000"; + private static String atlasNode2 = "http://atlas.node2:21000"; + + @Before + public void setup() throws Exception { + cluster = new TestingCluster(3); + cluster.start(); + + CuratorFramework zooKeeperClient = + CuratorFrameworkFactory.builder().connectString(cluster.getConnectString()) + .retryPolicy(new ExponentialBackoffRetry(1000, 3)) + .build(); + + zooKeeperClient.start(); + zooKeeperClient.create().forPath("/apache_atlas"); + zooKeeperClient.create().forPath("/apache_atlas/active_server_info"); + zooKeeperClient.setData().forPath("/apache_atlas/active_server_info", + atlasNode1.getBytes(Charset.forName("UTF-8"))); + zooKeeperClient.close(); + setAtlasActiveHostURLInZookeeper(atlasNode1); + + manager = new AtlasZookeeperURLManager(); + HaServiceConfig config = new DefaultHaServiceConfig("ATLAS-API"); + config.setEnabled(true); + config.setZookeeperEnsemble(cluster.getConnectString()); + config.setZookeeperNamespace("apache_atlas"); + manager.setConfig(config); + } + + @After + public void teardown() throws IOException { + cluster.stop(); + } + + @Test + public void testAtlasActiveUrlIsSetCorrectlyAfterLookUpFromZK() { + manager.lookupURLs(); + List<String> urls = manager.getURLs(); + assertEquals(atlasNode1, urls.get(0)); + } + + @Test + public void testMarkFailedCorrectlyResetTheEarlierUrl() throws Exception { + setAtlasActiveHostURLInZookeeper(atlasNode2); + + manager.markFailed("http://atlas.node1:21000"); + List<String> urls = manager.getURLs(); + assertNotEquals(atlasNode1, urls.get(0)); + assertEquals(atlasNode2, urls.get(0)); + } + + @Test + public void testAtlasURLManagerLoadingForAtlasApiService() { + HaServiceConfig config = new DefaultHaServiceConfig("ATLAS-API"); + config.setEnabled(true); + config.setZookeeperEnsemble(cluster.getConnectString()); + config.setZookeeperNamespace("apache_atlas"); + URLManager manager = URLManagerLoader.loadURLManager(config); + Assert.assertNotNull(manager); + Assert.assertTrue(manager instanceof AtlasZookeeperURLManager); + } + + @Test + public void testAtlasURLManagerLoadingForAtlasUIService() { + HaServiceConfig config = new DefaultHaServiceConfig("ATLAS"); + config.setEnabled(true); + config.setZookeeperEnsemble(cluster.getConnectString()); + config.setZookeeperNamespace("apache_atlas"); + URLManager manager = URLManagerLoader.loadURLManager(config); + Assert.assertNotNull(manager); + Assert.assertTrue(manager instanceof AtlasZookeeperURLManager); + } + + void setAtlasActiveHostURLInZookeeper(String activeURL) throws Exception { + + CuratorFramework zooKeeperClient = + CuratorFrameworkFactory.builder().connectString(cluster.getConnectString()) + .retryPolicy(new ExponentialBackoffRetry(1000, 3)) + .build(); + + zooKeeperClient.start(); + zooKeeperClient.setData().forPath("/apache_atlas/active_server_info", + activeURL.getBytes(Charset.forName("UTF-8"))); + zooKeeperClient.close(); + } + +} http://git-wip-us.apache.org/repos/asf/knox/blob/a6d2f524/gateway-service-definitions/src/main/resources/services/atlas-api/0.8.0/service.xml ---------------------------------------------------------------------- diff --git a/gateway-service-definitions/src/main/resources/services/atlas-api/0.8.0/service.xml b/gateway-service-definitions/src/main/resources/services/atlas-api/0.8.0/service.xml index 3ae7b24..20662ac 100644 --- a/gateway-service-definitions/src/main/resources/services/atlas-api/0.8.0/service.xml +++ b/gateway-service-definitions/src/main/resources/services/atlas-api/0.8.0/service.xml @@ -28,5 +28,5 @@ <route path="/atlas/api/**"/> </routes> - <dispatch classname="org.apache.knox.gateway.dispatch.PassAllHeadersDispatch"/> + <dispatch classname="org.apache.knox.gateway.dispatch.PassAllHeadersDispatch" ha-classname="org.apache.knox.gateway.ha.dispatch.AtlasApiHaDispatch"/> </service> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/a6d2f524/gateway-service-definitions/src/main/resources/services/atlas/0.8.0/service.xml ---------------------------------------------------------------------- diff --git a/gateway-service-definitions/src/main/resources/services/atlas/0.8.0/service.xml b/gateway-service-definitions/src/main/resources/services/atlas/0.8.0/service.xml index 6d99f30..8899248 100644 --- a/gateway-service-definitions/src/main/resources/services/atlas/0.8.0/service.xml +++ b/gateway-service-definitions/src/main/resources/services/atlas/0.8.0/service.xml @@ -46,6 +46,6 @@ </routes> - <dispatch classname="org.apache.knox.gateway.dispatch.PassAllHeadersDispatch"/> + <dispatch classname="org.apache.knox.gateway.dispatch.PassAllHeadersDispatch" ha-classname="org.apache.knox.gateway.ha.dispatch.AtlasHaDispatch" /> -</service> \ No newline at end of file +</service>
