knox-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From m...@apache.org
Subject [03/49] knox git commit: KNOX-1107 - Remote Configuration Registry Client Service (Phil Zampino via lmccay)
Date Thu, 14 Dec 2017 21:12:50 GMT
http://git-wip-us.apache.org/repos/asf/knox/blob/5af2413c/gateway-service-remoteconfig/src/test/java/org/apache/hadoop/gateway/service/config/remote/zk/RemoteConfigurationRegistryJAASConfigTest.java
----------------------------------------------------------------------
diff --git a/gateway-service-remoteconfig/src/test/java/org/apache/hadoop/gateway/service/config/remote/zk/RemoteConfigurationRegistryJAASConfigTest.java b/gateway-service-remoteconfig/src/test/java/org/apache/hadoop/gateway/service/config/remote/zk/RemoteConfigurationRegistryJAASConfigTest.java
new file mode 100644
index 0000000..6cbef9b
--- /dev/null
+++ b/gateway-service-remoteconfig/src/test/java/org/apache/hadoop/gateway/service/config/remote/zk/RemoteConfigurationRegistryJAASConfigTest.java
@@ -0,0 +1,255 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.hadoop.gateway.service.config.remote.zk;
+
+import org.apache.hadoop.gateway.service.config.remote.RemoteConfigurationRegistryConfig;
+import org.apache.hadoop.gateway.service.config.remote.zk.RemoteConfigurationRegistryJAASConfig;
+import org.apache.hadoop.gateway.services.security.AliasService;
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class RemoteConfigurationRegistryJAASConfigTest {
+
+    @Test
+    public void testZooKeeperDigestContextEntry() throws Exception {
+        List<RemoteConfigurationRegistryConfig> registryConfigs = new ArrayList<>();
+        final String ENTRY_NAME       = "my_digest_context";
+        final String DIGEST_PRINCIPAL = "myIdentity";
+        final String DIGEST_PWD_ALIAS = "myAlias";
+        final String DIGEST_PWD       = "mysecret";
+
+        AliasService aliasService = EasyMock.createNiceMock(AliasService.class);
+        EasyMock.expect(aliasService.getPasswordFromAliasForGateway(DIGEST_PWD_ALIAS)).andReturn(DIGEST_PWD.toCharArray()).anyTimes();
+        EasyMock.replay(aliasService);
+
+        registryConfigs.add(createDigestConfig(ENTRY_NAME, DIGEST_PRINCIPAL, DIGEST_PWD_ALIAS));
+
+        try {
+            RemoteConfigurationRegistryJAASConfig jaasConfig =
+                                    RemoteConfigurationRegistryJAASConfig.configure(registryConfigs, aliasService);
+
+            // Make sure there are no entries for an invalid context entry name
+            assertNull(jaasConfig.getAppConfigurationEntry("invalid"));
+
+            // Validate the intended context entry
+            validateDigestContext(jaasConfig,
+                                  ENTRY_NAME,
+                                  RemoteConfigurationRegistryJAASConfig.digestLoginModules.get("ZOOKEEPER"),
+                                  DIGEST_PRINCIPAL,
+                                  DIGEST_PWD);
+        } finally {
+            Configuration.setConfiguration(null);
+        }
+    }
+
+    @Test
+    public void testKerberosContextEntry() throws Exception {
+        List<RemoteConfigurationRegistryConfig> registryConfigs = new ArrayList<>();
+        final String ENTRY_NAME = "my_kerberos_context";
+        final String PRINCIPAL  = "myIdentity";
+
+        File dummyKeyTab = File.createTempFile("my_context", "keytab");
+        registryConfigs.add(createKerberosConfig(ENTRY_NAME, PRINCIPAL, dummyKeyTab.getAbsolutePath()));
+
+        try {
+            RemoteConfigurationRegistryJAASConfig jaasConfig =
+                                            RemoteConfigurationRegistryJAASConfig.configure(registryConfigs, null);
+
+            // Make sure there are no entries for an invalid context entry name
+            assertNull(jaasConfig.getAppConfigurationEntry("invalid"));
+
+            // Validate the intended context entry
+            validateKerberosContext(jaasConfig,
+                                    ENTRY_NAME,
+                                    PRINCIPAL,
+                                    dummyKeyTab.getAbsolutePath(),
+                                    true,
+                                    false);
+
+        } finally {
+            Configuration.setConfiguration(null);
+        }
+    }
+
+    @Test
+    public void testZooKeeperMultipleContextEntries() throws Exception {
+        List<RemoteConfigurationRegistryConfig> registryConfigs = new ArrayList<>();
+        final String KERBEROS_ENTRY_NAME = "my_kerberos_context";
+        final String KERBEROS_PRINCIPAL  = "myKerberosIdentity";
+        final String DIGEST_ENTRY_NAME   = "my_digest_context";
+        final String DIGEST_PRINCIPAL    = "myDigestIdentity";
+        final String DIGEST_PWD_ALIAS    = "myAlias";
+        final String DIGEST_PWD          = "mysecret";
+
+        AliasService aliasService = EasyMock.createNiceMock(AliasService.class);
+        EasyMock.expect(aliasService.getPasswordFromAliasForGateway(DIGEST_PWD_ALIAS)).andReturn(DIGEST_PWD.toCharArray()).anyTimes();
+        EasyMock.replay(aliasService);
+
+        File dummyKeyTab = File.createTempFile("my_context", "keytab");
+        registryConfigs.add(createKerberosConfig(KERBEROS_ENTRY_NAME, KERBEROS_PRINCIPAL, dummyKeyTab.getAbsolutePath()));
+        registryConfigs.add(createDigestConfig(DIGEST_ENTRY_NAME, DIGEST_PRINCIPAL, DIGEST_PWD_ALIAS));
+
+        try {
+            RemoteConfigurationRegistryJAASConfig jaasConfig =
+                                        RemoteConfigurationRegistryJAASConfig.configure(registryConfigs, aliasService);
+
+            // Make sure there are no entries for an invalid context entry name
+            assertNull(jaasConfig.getAppConfigurationEntry("invalid"));
+
+            // Validate the kerberos context entry
+            validateKerberosContext(jaasConfig,
+                                    KERBEROS_ENTRY_NAME,
+                                    KERBEROS_PRINCIPAL,
+                                    dummyKeyTab.getAbsolutePath(),
+                                    true,
+                                    false);
+
+            // Validate the digest context entry
+            validateDigestContext(jaasConfig,
+                                  DIGEST_ENTRY_NAME,
+                                  RemoteConfigurationRegistryJAASConfig.digestLoginModules.get("ZOOKEEPER"),
+                                  DIGEST_PRINCIPAL,
+                                  DIGEST_PWD);
+
+        } finally {
+            Configuration.setConfiguration(null);
+        }
+    }
+
+    @Test
+    public void testZooKeeperDigestContextEntryWithoutAliasService() throws Exception {
+        List<RemoteConfigurationRegistryConfig> registryConfigs = new ArrayList<>();
+        final String ENTRY_NAME       = "my_digest_context";
+        final String DIGEST_PRINCIPAL = "myIdentity";
+        final String DIGEST_PWD_ALIAS = "myAlias";
+
+        registryConfigs.add(createDigestConfig(ENTRY_NAME, DIGEST_PRINCIPAL, DIGEST_PWD_ALIAS));
+
+        try {
+            RemoteConfigurationRegistryJAASConfig jaasConfig =
+                                            RemoteConfigurationRegistryJAASConfig.configure(registryConfigs, null);
+            fail("Expected IllegalArgumentException because the AliasService is not available.");
+        } catch (IllegalArgumentException e) {
+            // Expected
+            assertTrue(e.getMessage().contains("AliasService"));
+        } catch (Throwable e) {
+            fail("Wrong exception encountered: " + e.getClass().getName() + ", " + e.getMessage());
+        } finally {
+            Configuration.setConfiguration(null);
+        }
+    }
+
+    private static RemoteConfigurationRegistryConfig createDigestConfig(String entryName,
+                                                                        String principal,
+                                                                        String credentialAlias) {
+        return createDigestConfig(entryName, principal, credentialAlias, "ZooKeeper");
+    }
+
+    private static RemoteConfigurationRegistryConfig createDigestConfig(String entryName,
+                                                                        String principal,
+                                                                        String credentialAlias,
+                                                                        String registryType) {
+        RemoteConfigurationRegistryConfig rc = EasyMock.createNiceMock(RemoteConfigurationRegistryConfig.class);
+        EasyMock.expect(rc.getRegistryType()).andReturn(registryType).anyTimes();
+        EasyMock.expect(rc.getName()).andReturn(entryName).anyTimes();
+        EasyMock.expect(rc.isSecureRegistry()).andReturn(true).anyTimes();
+        EasyMock.expect(rc.getAuthType()).andReturn("digest").anyTimes();
+        EasyMock.expect(rc.getPrincipal()).andReturn(principal).anyTimes();
+        EasyMock.expect(rc.getCredentialAlias()).andReturn(credentialAlias).anyTimes();
+        EasyMock.replay(rc);
+        return rc;
+    }
+
+
+    private static RemoteConfigurationRegistryConfig createKerberosConfig(String entryName,
+                                                                          String principal,
+                                                                          String keyTabPath) {
+        return createKerberosConfig(entryName, principal, keyTabPath, "ZooKeeper");
+    }
+
+    private static RemoteConfigurationRegistryConfig createKerberosConfig(String entryName,
+                                                                          String principal,
+                                                                          String keyTabPath,
+                                                                          String registryType) {
+        return createKerberosConfig(entryName, principal, keyTabPath, null, null, registryType);
+    }
+
+    private static RemoteConfigurationRegistryConfig createKerberosConfig(String entryName,
+                                                                          String principal,
+                                                                          String keyTabPath,
+                                                                          Boolean useKeyTab,
+                                                                          Boolean useTicketCache,
+                                                                          String registryType) {
+        RemoteConfigurationRegistryConfig rc = EasyMock.createNiceMock(RemoteConfigurationRegistryConfig.class);
+        EasyMock.expect(rc.getRegistryType()).andReturn(registryType).anyTimes();
+        EasyMock.expect(rc.getName()).andReturn(entryName).anyTimes();
+        EasyMock.expect(rc.isSecureRegistry()).andReturn(true).anyTimes();
+        EasyMock.expect(rc.getAuthType()).andReturn("kerberos").anyTimes();
+        EasyMock.expect(rc.getPrincipal()).andReturn(principal).anyTimes();
+        EasyMock.expect(rc.getKeytab()).andReturn(keyTabPath).anyTimes();
+        EasyMock.expect(rc.isUseKeyTab()).andReturn(useKeyTab != null ? useKeyTab : true).anyTimes();
+        EasyMock.expect(rc.isUseTicketCache()).andReturn(useTicketCache != null ? useTicketCache : false).anyTimes();
+        EasyMock.replay(rc);
+        return rc;
+    }
+
+    private static void validateDigestContext(RemoteConfigurationRegistryJAASConfig config,
+                                              String                                entryName,
+                                              String                                loginModule,
+                                              String                                principal,
+                                              String                                password) throws Exception {
+        AppConfigurationEntry[] myContextEntries = config.getAppConfigurationEntry(entryName);
+        assertNotNull(myContextEntries);
+        assertEquals(1, myContextEntries.length);
+        AppConfigurationEntry entry = myContextEntries[0];
+        assertTrue(entry.getLoginModuleName().equals(loginModule));
+        Map<String, ?> entryOpts = entry.getOptions();
+        assertEquals(principal, entryOpts.get("username"));
+        assertEquals(password, entryOpts.get("password"));
+    }
+
+    private static void validateKerberosContext(RemoteConfigurationRegistryJAASConfig config,
+                                                String                                entryName,
+                                                String                                principal,
+                                                String                                keyTab,
+                                                boolean                               useKeyTab,
+                                                boolean                               useTicketCache) throws Exception {
+        AppConfigurationEntry[] myContextEntries = config.getAppConfigurationEntry(entryName);
+        assertNotNull(myContextEntries);
+        assertEquals(1, myContextEntries.length);
+        AppConfigurationEntry entry = myContextEntries[0];
+        assertTrue(entry.getLoginModuleName().endsWith(".security.auth.module.Krb5LoginModule"));
+        Map<String, ?> entryOpts = entry.getOptions();
+        assertEquals(principal, entryOpts.get("principal"));
+        assertEquals(keyTab, entryOpts.get("keyTab"));
+        assertEquals(useKeyTab, Boolean.valueOf((String)entryOpts.get("isUseKeyTab")));
+        assertEquals(useTicketCache, Boolean.valueOf((String)entryOpts.get("isUseTicketCache")));
+    }
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/5af2413c/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java
index 66fb83c..5cfaf36 100644
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java
@@ -42,6 +42,16 @@ public interface GatewayConfig {
   public static final String SIGNING_KEYSTORE_NAME = "gateway.signing.keystore.name";
   public static final String SIGNING_KEY_ALIAS = "gateway.signing.key.alias";
 
+  String REMOTE_CONFIG_REGISTRY_TYPE = "type";
+  String REMOTE_CONFIG_REGISTRY_ADDRESS = "address";
+  String REMOTE_CONFIG_REGISTRY_NAMESPACE = "namespace";
+  String REMOTE_CONFIG_REGISTRY_AUTH_TYPE = "authType";
+  String REMOTE_CONFIG_REGISTRY_PRINCIPAL = "principal";
+  String REMOTE_CONFIG_REGISTRY_CREDENTIAL_ALIAS = "credentialAlias";
+  String REMOTE_CONFIG_REGISTRY_KEYTAB = "keytab";
+  String REMOTE_CONFIG_REGISTRY_USE_KEYTAB = "useKeytab";
+  String REMOTE_CONFIG_REGISTRY_USE_TICKET_CACHE = "useTicketCache";
+
   /**
    * The location of the gateway configuration.
    * Subdirectories will be: topologies
@@ -76,6 +86,10 @@ public interface GatewayConfig {
 
   String getGatewayPath();
 
+  String getGatewayProvidersConfigDir();
+
+  String getGatewayDescriptorsDir();
+
   String getGatewayTopologyDir();
 
   String getGatewaySecurityDir();
@@ -299,4 +313,24 @@ public interface GatewayConfig {
    * @return
    */
   boolean isGatewayServerHeaderEnabled();
+
+  /**
+   * @return The list of the names of any remote registry configurations defined herein.
+   */
+  List<String> getRemoteRegistryConfigurationNames();
+
+  /**
+   *
+   * @param name The name of the remote registry configuration
+   *
+   * @return The configuration associated with the specified name.
+   */
+  String getRemoteRegistryConfiguration(String name);
+
+  /**
+   *
+   * @return The name of a remote configuration registry client
+   */
+  String getRemoteConfigurationMonitorClientName();
+
 }

http://git-wip-us.apache.org/repos/asf/knox/blob/5af2413c/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/GatewayServices.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/GatewayServices.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/GatewayServices.java
index 2e6227c..2894bbc 100644
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/GatewayServices.java
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/GatewayServices.java
@@ -39,6 +39,8 @@ public interface GatewayServices extends Service, ProviderDeploymentContributor
   public static final String SERVICE_DEFINITION_REGISTRY = "ServiceDefinitionRegistry";
   public static final String METRICS_SERVICE = "MetricsService";
 
+  String REMOTE_REGISTRY_CLIENT_SERVICE = "RemoteConfigRegistryClientService";
+
   public abstract Collection<String> getServiceNames();
 
   public abstract <T> T getService( String serviceName );

http://git-wip-us.apache.org/repos/asf/knox/blob/5af2413c/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/config/client/RemoteConfigurationRegistryClient.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/config/client/RemoteConfigurationRegistryClient.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/config/client/RemoteConfigurationRegistryClient.java
new file mode 100644
index 0000000..6fbf410
--- /dev/null
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/config/client/RemoteConfigurationRegistryClient.java
@@ -0,0 +1,74 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.hadoop.gateway.services.config.client;
+
+import java.util.List;
+
+public interface RemoteConfigurationRegistryClient {
+
+    String getAddress();
+
+    boolean entryExists(String path);
+
+    List<EntryACL> getACL(String path);
+
+    List<String> listChildEntries(String path);
+
+    String getEntryData(String path);
+
+    String getEntryData(String path, String encoding);
+
+    void createEntry(String path);
+
+    void createEntry(String path, String data);
+
+    void createEntry(String path, String data, String encoding);
+
+    int setEntryData(String path, String data);
+
+    int setEntryData(String path, String data, String encoding);
+
+    void deleteEntry(String path);
+
+    void addChildEntryListener(String path, ChildEntryListener listener) throws Exception;
+
+    void addEntryListener(String path, EntryListener listener) throws Exception;
+
+    void removeEntryListener(String path) throws Exception;
+
+    interface ChildEntryListener {
+
+        enum Type {
+            ADDED,
+            REMOVED,
+            UPDATED
+        }
+
+        void childEvent(RemoteConfigurationRegistryClient client, ChildEntryListener.Type type, String path);
+    }
+
+    interface EntryListener {
+        void entryChanged(RemoteConfigurationRegistryClient client, String path, byte[] data);
+    }
+
+    interface EntryACL {
+        String getId();
+        String getType();
+        Object getPermissions();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/5af2413c/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/config/client/RemoteConfigurationRegistryClientService.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/config/client/RemoteConfigurationRegistryClientService.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/config/client/RemoteConfigurationRegistryClientService.java
new file mode 100644
index 0000000..1467f75
--- /dev/null
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/config/client/RemoteConfigurationRegistryClientService.java
@@ -0,0 +1,28 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.hadoop.gateway.services.config.client;
+
+import org.apache.hadoop.gateway.services.Service;
+import org.apache.hadoop.gateway.services.security.AliasService;
+
+public interface RemoteConfigurationRegistryClientService extends Service {
+
+    void setAliasService(AliasService aliasService);
+
+    RemoteConfigurationRegistryClient get(String l);
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/5af2413c/gateway-spi/src/main/java/org/apache/hadoop/gateway/topology/monitor/RemoteConfigurationMonitor.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/topology/monitor/RemoteConfigurationMonitor.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/topology/monitor/RemoteConfigurationMonitor.java
new file mode 100644
index 0000000..82c5809
--- /dev/null
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/topology/monitor/RemoteConfigurationMonitor.java
@@ -0,0 +1,24 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.hadoop.gateway.topology.monitor;
+
+public interface RemoteConfigurationMonitor {
+
+    void start() throws Exception;
+
+    void stop() throws Exception;
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/5af2413c/gateway-spi/src/main/java/org/apache/hadoop/gateway/topology/monitor/RemoteConfigurationMonitorProvider.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/topology/monitor/RemoteConfigurationMonitorProvider.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/topology/monitor/RemoteConfigurationMonitorProvider.java
new file mode 100644
index 0000000..d19dace
--- /dev/null
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/topology/monitor/RemoteConfigurationMonitorProvider.java
@@ -0,0 +1,34 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.hadoop.gateway.topology.monitor;
+
+
+import org.apache.hadoop.gateway.config.GatewayConfig;
+import org.apache.hadoop.gateway.services.config.client.RemoteConfigurationRegistryClientService;
+
+public interface RemoteConfigurationMonitorProvider {
+
+    /**
+     *
+     * @param config        The gateway configuration.
+     * @param clientService The RemoteConfigurationRegistryClientService for accessing the remote configuration.
+     *
+     * @return A RemoteConfigurationMonitor for keeping the local config in sync with the remote config
+     */
+    RemoteConfigurationMonitor newInstance(GatewayConfig config, RemoteConfigurationRegistryClientService clientService);
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/5af2413c/gateway-test-release-utils/src/main/java/org/apache/hadoop/gateway/GatewayTestConfig.java
----------------------------------------------------------------------
diff --git a/gateway-test-release-utils/src/main/java/org/apache/hadoop/gateway/GatewayTestConfig.java b/gateway-test-release-utils/src/main/java/org/apache/hadoop/gateway/GatewayTestConfig.java
index ff9a877..f7ea633 100644
--- a/gateway-test-release-utils/src/main/java/org/apache/hadoop/gateway/GatewayTestConfig.java
+++ b/gateway-test-release-utils/src/main/java/org/apache/hadoop/gateway/GatewayTestConfig.java
@@ -25,6 +25,7 @@ import java.io.File;
 import java.net.InetSocketAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
@@ -614,4 +615,29 @@ public class GatewayTestConfig extends Configuration implements GatewayConfig {
   public boolean isClientAuthWanted() {
     return false;
   }
+
+  @Override
+  public String getGatewayProvidersConfigDir() {
+    return null;
+  }
+
+  @Override
+  public String getGatewayDescriptorsDir() {
+    return null;
+  }
+
+  @Override
+  public List<String> getRemoteRegistryConfigurationNames() {
+    return Collections.emptyList();
+  }
+
+  @Override
+  public String getRemoteRegistryConfiguration(String s) {
+    return null;
+  }
+
+  @Override
+  public String getRemoteConfigurationMonitorClientName() {
+    return null;
+  }
 }

http://git-wip-us.apache.org/repos/asf/knox/blob/5af2413c/gateway-test-utils/src/main/java/org/apache/hadoop/test/TestUtils.java
----------------------------------------------------------------------
diff --git a/gateway-test-utils/src/main/java/org/apache/hadoop/test/TestUtils.java b/gateway-test-utils/src/main/java/org/apache/hadoop/test/TestUtils.java
index 076c312..cf446a3 100644
--- a/gateway-test-utils/src/main/java/org/apache/hadoop/test/TestUtils.java
+++ b/gateway-test-utils/src/main/java/org/apache/hadoop/test/TestUtils.java
@@ -50,7 +50,7 @@ public class TestUtils {
   private static Logger LOG = Logger.getLogger(TestUtils.class);
 
   public static final long SHORT_TIMEOUT = 1000L;
-  public static final long MEDIUM_TIMEOUT = 20 * 1000L;
+  public static final long MEDIUM_TIMEOUT = 30 * 1000L;
   public static final long LONG_TIMEOUT = 60 * 1000L;
 
   public static String getResourceName( Class clazz, String name ) {

http://git-wip-us.apache.org/repos/asf/knox/blob/5af2413c/gateway-test/pom.xml
----------------------------------------------------------------------
diff --git a/gateway-test/pom.xml b/gateway-test/pom.xml
index 3b622aa..4eb5093 100644
--- a/gateway-test/pom.xml
+++ b/gateway-test/pom.xml
@@ -132,6 +132,12 @@
             <scope>test</scope>
         </dependency>
 
+        <dependency>
+            <groupId>org.apache.curator</groupId>
+            <artifactId>curator-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
     </dependencies>
 
     <build>

http://git-wip-us.apache.org/repos/asf/knox/blob/5af2413c/gateway-test/src/test/java/org/apache/hadoop/gateway/topology/monitor/RemoteConfigurationMonitorTest.java
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/java/org/apache/hadoop/gateway/topology/monitor/RemoteConfigurationMonitorTest.java b/gateway-test/src/test/java/org/apache/hadoop/gateway/topology/monitor/RemoteConfigurationMonitorTest.java
new file mode 100644
index 0000000..14d98a9
--- /dev/null
+++ b/gateway-test/src/test/java/org/apache/hadoop/gateway/topology/monitor/RemoteConfigurationMonitorTest.java
@@ -0,0 +1,397 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.hadoop.gateway.topology.monitor;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.curator.test.InstanceSpec;
+import org.apache.curator.test.TestingCluster;
+import org.apache.hadoop.gateway.config.GatewayConfig;
+import org.apache.hadoop.gateway.service.config.remote.zk.ZooKeeperClientService;
+import org.apache.hadoop.gateway.service.config.remote.zk.ZooKeeperClientServiceProvider;
+import org.apache.hadoop.gateway.services.config.client.RemoteConfigurationRegistryClientService;
+import org.apache.hadoop.gateway.services.security.AliasService;
+import org.apache.hadoop.test.TestUtils;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Id;
+import org.easymock.EasyMock;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Test the RemoteConfigurationMonitor functionality with SASL configured, and znode ACLs applied.
+ *
+ * The expected implementation is org.apache.hadoop.gateway.topology.monitor.zk.ZooKeeperConfigMonitor
+ *
+ * Digest-based SASL is used for this test, but since that is dictated solely by the JAAS config, Kerberos-based SASL
+ * should work in exactly the same way, simply by modifying the SASL config.
+ */
+public class RemoteConfigurationMonitorTest {
+
+    private static final String PATH_KNOX = "/knox";
+    private static final String PATH_KNOX_CONFIG = PATH_KNOX + "/config";
+    private static final String PATH_KNOX_PROVIDERS = PATH_KNOX_CONFIG + "/shared-providers";
+    private static final String PATH_KNOX_DESCRIPTORS = PATH_KNOX_CONFIG + "/descriptors";
+
+    private static final String ZK_USERNAME = "testsasluser";
+    private static final String ZK_PASSWORD = "testsaslpwd";
+
+    private static File testTmp;
+    private static File providersDir;
+    private static File descriptorsDir;
+
+    private static TestingCluster zkCluster;
+
+    private static CuratorFramework client;
+
+    @BeforeClass
+    public static void setupSuite() throws Exception {
+        testTmp = TestUtils.createTempDir(RemoteConfigurationMonitorTest.class.getName());
+        File confDir   = TestUtils.createTempDir(testTmp + "/conf");
+        providersDir   = TestUtils.createTempDir(confDir + "/shared-providers");
+        descriptorsDir = TestUtils.createTempDir(confDir + "/descriptors");
+
+        configureAndStartZKCluster();
+    }
+
+    /**
+     * Create and persist a JAAS configuration file, defining the SASL config for both the ZooKeeper cluster instances
+     * and ZooKeeper clients.
+     *
+     * @param username The digest username
+     * @param password The digest password
+     *
+     * @return The JAAS configuration file
+     */
+    private static File setupDigestSaslConfig(String username, String password) throws Exception {
+        File saslConfigFile = new File(testTmp, "server-jaas.conf");
+        FileWriter fw = new FileWriter(saslConfigFile);
+        fw.write("Server {\n" +
+                "    org.apache.zookeeper.server.auth.DigestLoginModule required\n" +
+                "    user_" + username + " =\"" + password + "\";\n" +
+                "};\n"+
+                "Client {\n" +
+                "    org.apache.zookeeper.server.auth.DigestLoginModule required\n" +
+                "    username=\"" + username + "\"\n" +
+                "    password=\"" + password + "\";\n" +
+                "};\n");
+        fw.close();
+        return saslConfigFile;
+    }
+
+    /**
+     * Configure and start the ZooKeeper test cluster, and create the znodes monitored by the RemoteConfigurationMonitor.
+     */
+    private static void configureAndStartZKCluster() throws Exception {
+        // Configure security for the ZK cluster instances
+        Map<String, Object> customInstanceSpecProps = new HashMap<>();
+        customInstanceSpecProps.put("authProvider.1", "org.apache.zookeeper.server.auth.SASLAuthenticationProvider");
+        customInstanceSpecProps.put("requireClientAuthScheme", "sasl");
+
+        // Define the test cluster
+        List<InstanceSpec> instanceSpecs = new ArrayList<>();
+        for (int i = 0 ; i < 3 ; i++) {
+            InstanceSpec is = new InstanceSpec(null, -1, -1, -1, false, (i+1), -1, -1, customInstanceSpecProps);
+            instanceSpecs.add(is);
+        }
+        zkCluster = new TestingCluster(instanceSpecs);
+
+        // Configure auth for the ZooKeeper servers and the clients
+        File saslConfigFile = setupDigestSaslConfig(ZK_USERNAME, ZK_PASSWORD);
+
+        // This system property is used by the ZooKeeper cluster instances, the test driver client, and the
+        // RemoteConfigurationMonitor implementation for SASL authentication/authorization
+        System.setProperty("java.security.auth.login.config", saslConfigFile.getAbsolutePath());
+
+        // Start the cluster
+        zkCluster.start();
+
+        // Create the client for the test cluster
+        client = CuratorFrameworkFactory.builder()
+                .connectString(zkCluster.getConnectString())
+                .retryPolicy(new ExponentialBackoffRetry(100, 3))
+                .build();
+        assertNotNull(client);
+        client.start();
+
+        // Create the knox config paths with an ACL for the sasl user configured for the client
+        List<ACL> acls = new ArrayList<>();
+        acls.add(new ACL(ZooDefs.Perms.ALL, new Id("sasl", ZK_USERNAME)));
+
+        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_KNOX_DESCRIPTORS);
+        assertNotNull("Failed to create node:" + PATH_KNOX_DESCRIPTORS,
+                client.checkExists().forPath(PATH_KNOX_DESCRIPTORS));
+        client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).withACL(acls).forPath(PATH_KNOX_PROVIDERS);
+        assertNotNull("Failed to create node:" + PATH_KNOX_PROVIDERS,
+                client.checkExists().forPath(PATH_KNOX_PROVIDERS));
+    }
+
+    @AfterClass
+    public static void tearDownSuite() throws Exception {
+        // Clean up the ZK nodes, and close the client
+        if (client != null) {
+            client.delete().deletingChildrenIfNeeded().forPath(PATH_KNOX);
+            client.close();
+        }
+
+        // Shutdown the ZK cluster
+        zkCluster.close();
+
+        // Delete the working dir
+        testTmp.delete();
+    }
+
+    @Test
+    public void testZooKeeperConfigMonitorSASL() throws Exception {
+        final String configMonitorName = "zkConfigClient";
+
+        // Setup the base GatewayConfig mock
+        GatewayConfig gc = EasyMock.createNiceMock(GatewayConfig.class);
+        EasyMock.expect(gc.getGatewayProvidersConfigDir()).andReturn(providersDir.getAbsolutePath()).anyTimes();
+        EasyMock.expect(gc.getGatewayDescriptorsDir()).andReturn(descriptorsDir.getAbsolutePath()).anyTimes();
+        EasyMock.expect(gc.getRemoteRegistryConfigurationNames())
+                .andReturn(Collections.singletonList(configMonitorName))
+                .anyTimes();
+        final String registryConfig =
+                            GatewayConfig.REMOTE_CONFIG_REGISTRY_TYPE + "=" + ZooKeeperClientService.TYPE + ";" +
+                            GatewayConfig.REMOTE_CONFIG_REGISTRY_ADDRESS + "=" + zkCluster.getConnectString();
+        EasyMock.expect(gc.getRemoteRegistryConfiguration(configMonitorName))
+                .andReturn(registryConfig).anyTimes();
+        EasyMock.expect(gc.getRemoteConfigurationMonitorClientName()).andReturn(configMonitorName).anyTimes();
+        EasyMock.replay(gc);
+
+        AliasService aliasService = EasyMock.createNiceMock(AliasService.class);
+        EasyMock.replay(aliasService);
+
+        RemoteConfigurationRegistryClientService clientService = (new ZooKeeperClientServiceProvider()).newInstance();
+        clientService.setAliasService(aliasService);
+        clientService.init(gc, Collections.emptyMap());
+        clientService.start();
+
+        RemoteConfigurationMonitorFactory.setClientService(clientService);
+
+        RemoteConfigurationMonitor cm = RemoteConfigurationMonitorFactory.get(gc);
+        assertNotNull("Failed to load RemoteConfigurationMonitor", cm);
+
+        try {
+            cm.start();
+        } catch (Exception e) {
+            fail("Failed to start monitor: " + e.getMessage());
+        }
+
+        try {
+            final String pc_one_znode = getProviderPath("providers-config1.xml");
+            final File pc_one         = new File(providersDir, "providers-config1.xml");
+            final String pc_two_znode = getProviderPath("providers-config2.xml");
+            final File pc_two         = new File(providersDir, "providers-config2.xml");
+
+            client.create().withMode(CreateMode.PERSISTENT).forPath(pc_one_znode, TEST_PROVIDERS_CONFIG_1.getBytes());
+            Thread.sleep(100);
+            assertTrue(pc_one.exists());
+            assertEquals(TEST_PROVIDERS_CONFIG_1, FileUtils.readFileToString(pc_one));
+
+            client.create().withMode(CreateMode.PERSISTENT).forPath(getProviderPath("providers-config2.xml"), TEST_PROVIDERS_CONFIG_2.getBytes());
+            Thread.sleep(100);
+            assertTrue(pc_two.exists());
+            assertEquals(TEST_PROVIDERS_CONFIG_2, FileUtils.readFileToString(pc_two));
+
+            client.setData().forPath(pc_two_znode, TEST_PROVIDERS_CONFIG_1.getBytes());
+            Thread.sleep(100);
+            assertTrue(pc_two.exists());
+            assertEquals(TEST_PROVIDERS_CONFIG_1, FileUtils.readFileToString(pc_two));
+
+            client.delete().forPath(pc_two_znode);
+            Thread.sleep(100);
+            assertFalse(pc_two.exists());
+
+            client.delete().forPath(pc_one_znode);
+            Thread.sleep(100);
+            assertFalse(pc_one.exists());
+
+            final String desc_one_znode   = getDescriptorPath("test1.json");
+            final String desc_two_znode   = getDescriptorPath("test2.json");
+            final String desc_three_znode = getDescriptorPath("test3.json");
+            final File desc_one           = new File(descriptorsDir, "test1.json");
+            final File desc_two           = new File(descriptorsDir, "test2.json");
+            final File desc_three         = new File(descriptorsDir, "test3.json");
+
+            client.create().withMode(CreateMode.PERSISTENT).forPath(desc_one_znode, TEST_DESCRIPTOR_1.getBytes());
+            Thread.sleep(100);
+            assertTrue(desc_one.exists());
+            assertEquals(TEST_DESCRIPTOR_1, FileUtils.readFileToString(desc_one));
+
+            client.create().withMode(CreateMode.PERSISTENT).forPath(desc_two_znode, TEST_DESCRIPTOR_1.getBytes());
+            Thread.sleep(100);
+            assertTrue(desc_two.exists());
+            assertEquals(TEST_DESCRIPTOR_1, FileUtils.readFileToString(desc_two));
+
+            client.setData().forPath(desc_two_znode, TEST_DESCRIPTOR_2.getBytes());
+            Thread.sleep(100);
+            assertTrue(desc_two.exists());
+            assertEquals(TEST_DESCRIPTOR_2, FileUtils.readFileToString(desc_two));
+
+            client.create().withMode(CreateMode.PERSISTENT).forPath(desc_three_znode, TEST_DESCRIPTOR_1.getBytes());
+            Thread.sleep(100);
+            assertTrue(desc_three.exists());
+            assertEquals(TEST_DESCRIPTOR_1, FileUtils.readFileToString(desc_three));
+
+            client.delete().forPath(desc_two_znode);
+            Thread.sleep(100);
+            assertFalse("Expected test2.json to have been deleted.", desc_two.exists());
+
+            client.delete().forPath(desc_three_znode);
+            Thread.sleep(100);
+            assertFalse(desc_three.exists());
+
+            client.delete().forPath(desc_one_znode);
+            Thread.sleep(100);
+            assertFalse(desc_one.exists());
+        } finally {
+            cm.stop();
+        }
+    }
+
+    private static String getDescriptorPath(String descriptorName) {
+        return PATH_KNOX_DESCRIPTORS + "/" + descriptorName;
+    }
+
+    private static String getProviderPath(String providerConfigName) {
+        return PATH_KNOX_PROVIDERS + "/" + providerConfigName;
+    }
+
+
+    private static final String TEST_PROVIDERS_CONFIG_1 =
+                    "<gateway>\n" +
+                    "    <provider>\n" +
+                    "        <role>identity-assertion</role>\n" +
+                    "        <name>Default</name>\n" +
+                    "        <enabled>true</enabled>\n" +
+                    "    </provider>\n" +
+                    "    <provider>\n" +
+                    "        <role>hostmap</role>\n" +
+                    "        <name>static</name>\n" +
+                    "        <enabled>true</enabled>\n" +
+                    "        <param><name>localhost</name><value>sandbox,sandbox.hortonworks.com</value></param>\n" +
+                    "    </provider>\n" +
+                    "</gateway>\n";
+
+    private static final String TEST_PROVIDERS_CONFIG_2 =
+                    "<gateway>\n" +
+                    "    <provider>\n" +
+                    "        <role>authentication</role>\n" +
+                    "        <name>ShiroProvider</name>\n" +
+                    "        <enabled>true</enabled>\n" +
+                    "        <param>\n" +
+                    "            <name>sessionTimeout</name>\n" +
+                    "            <value>30</value>\n" +
+                    "        </param>\n" +
+                    "        <param>\n" +
+                    "            <name>main.ldapRealm</name>\n" +
+                    "            <value>org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm</value>\n" +
+                    "        </param>\n" +
+                    "        <param>\n" +
+                    "            <name>main.ldapContextFactory</name>\n" +
+                    "            <value>org.apache.hadoop.gateway.shirorealm.KnoxLdapContextFactory</value>\n" +
+                    "        </param>\n" +
+                    "        <param>\n" +
+                    "            <name>main.ldapRealm.contextFactory</name>\n" +
+                    "            <value>$ldapContextFactory</value>\n" +
+                    "        </param>\n" +
+                    "        <param>\n" +
+                    "            <name>main.ldapRealm.userDnTemplate</name>\n" +
+                    "            <value>uid={0},ou=people,dc=hadoop,dc=apache,dc=org</value>\n" +
+                    "        </param>\n" +
+                    "        <param>\n" +
+                    "            <name>main.ldapRealm.contextFactory.url</name>\n" +
+                    "            <value>ldap://localhost:33389</value>\n" +
+                    "        </param>\n" +
+                    "        <param>\n" +
+                    "            <name>main.ldapRealm.contextFactory.authenticationMechanism</name>\n" +
+                    "            <value>simple</value>\n" +
+                    "        </param>\n" +
+                    "        <param>\n" +
+                    "            <name>urls./**</name>\n" +
+                    "            <value>authcBasic</value>\n" +
+                    "        </param>\n" +
+                    "    </provider>\n" +
+                    "</gateway>\n";
+
+    private static final String TEST_DESCRIPTOR_1 =
+                    "{\n" +
+                    "  \"discovery-type\":\"AMBARI\",\n" +
+                    "  \"discovery-address\":\"http://sandbox.hortonworks.com:8080\",\n" +
+                    "  \"discovery-user\":\"maria_dev\",\n" +
+                    "  \"discovery-pwd-alias\":\"sandbox.ambari.discovery.password\",\n" +
+                    "  \"provider-config-ref\":\"sandbox-providers.xml\",\n" +
+                    "  \"cluster\":\"Sandbox\",\n" +
+                    "  \"services\":[\n" +
+                    "    {\"name\":\"NODEUI\"},\n" +
+                    "    {\"name\":\"YARNUI\"},\n" +
+                    "    {\"name\":\"HDFSUI\"},\n" +
+                    "    {\"name\":\"OOZIEUI\"},\n" +
+                    "    {\"name\":\"HBASEUI\"},\n" +
+                    "    {\"name\":\"NAMENODE\"},\n" +
+                    "    {\"name\":\"JOBTRACKER\"},\n" +
+                    "    {\"name\":\"WEBHDFS\"},\n" +
+                    "    {\"name\":\"WEBHCAT\"},\n" +
+                    "    {\"name\":\"OOZIE\"},\n" +
+                    "    {\"name\":\"WEBHBASE\"},\n" +
+                    "    {\"name\":\"RESOURCEMANAGER\"},\n" +
+                    "    {\"name\":\"AMBARI\", \"urls\":[\"http://c6401.ambari.apache.org:8080\"]},\n" +
+                    "    {\"name\":\"AMBARIUI\", \"urls\":[\"http://c6401.ambari.apache.org:8080\"]}\n" +
+                    "  ]\n" +
+                    "}\n";
+
+    private static final String TEST_DESCRIPTOR_2 =
+                    "{\n" +
+                    "  \"discovery-type\":\"AMBARI\",\n" +
+                    "  \"discovery-address\":\"http://sandbox.hortonworks.com:8080\",\n" +
+                    "  \"discovery-user\":\"maria_dev\",\n" +
+                    "  \"discovery-pwd-alias\":\"sandbox.ambari.discovery.password\",\n" +
+                    "  \"provider-config-ref\":\"sandbox-providers.xml\",\n" +
+                    "  \"cluster\":\"Sandbox\",\n" +
+                    "  \"services\":[\n" +
+                    "    {\"name\":\"NAMENODE\"},\n" +
+                    "    {\"name\":\"JOBTRACKER\"},\n" +
+                    "    {\"name\":\"WEBHDFS\"},\n" +
+                    "    {\"name\":\"WEBHCAT\"},\n" +
+                    "    {\"name\":\"OOZIE\"},\n" +
+                    "    {\"name\":\"WEBHBASE\"},\n" +
+                    "    {\"name\":\"RESOURCEMANAGER\"}\n" +
+                    "  ]\n" +
+                    "}\n";
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/5af2413c/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 51ff1a4..a55acd6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -83,6 +83,7 @@
         <module>gateway-service-tgs</module>
         <module>gateway-service-rm</module>
         <module>gateway-service-storm</module>
+        <module>gateway-service-remoteconfig</module>
         <module>gateway-service-definitions</module>
         <module>gateway-shell</module>
         <module>gateway-shell-launcher</module>
@@ -629,6 +630,11 @@
             </dependency>
             <dependency>
                 <groupId>${gateway-group}</groupId>
+                <artifactId>gateway-service-remoteconfig</artifactId>
+                <version>${gateway-version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${gateway-group}</groupId>
                 <artifactId>gateway-service-test</artifactId>
                 <version>${gateway-version}</version>
             </dependency>
@@ -1136,12 +1142,17 @@
             <dependency>
                 <groupId>org.apache.zookeeper</groupId>
                 <artifactId>zookeeper</artifactId>
-                <version>3.4.6</version>
+                <version>3.4.10</version>
             </dependency>
             <dependency>
                 <groupId>org.apache.curator</groupId>
                 <artifactId>curator-framework</artifactId>
-                <version>2.6.0</version>
+                <version>4.0.0</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.curator</groupId>
+                <artifactId>curator-client</artifactId>
+                <version>4.0.0</version>
             </dependency>
 
             <!-- Html pull parser.  EPLv1 license -->
@@ -1333,11 +1344,10 @@
             <dependency>
                 <groupId>org.apache.curator</groupId>
                 <artifactId>curator-test</artifactId>
-                <version>2.6.0</version>
+                <version>2.12.0</version>
                 <scope>test</scope>
             </dependency>
 
-
         </dependencies>
     </dependencyManagement>
 


Mime
View raw message