/**
* Copyright 2016 Yahoo Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.yahoo.pulsar.broker.lookup.http;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.testng.Assert.assertEquals;
import java.lang.reflect.Field;
import java.net.URI;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicReference;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.google.common.collect.Lists;
import com.yahoo.pulsar.broker.PulsarService;
import com.yahoo.pulsar.broker.ServiceConfiguration;
import com.yahoo.pulsar.broker.admin.AdminResource;
import com.yahoo.pulsar.broker.authorization.AuthorizationManager;
import com.yahoo.pulsar.broker.cache.ConfigurationCacheService;
import com.yahoo.pulsar.broker.lookup.DestinationLookup;
import com.yahoo.pulsar.broker.lookup.NamespaceData;
import com.yahoo.pulsar.broker.lookup.RedirectData;
import com.yahoo.pulsar.broker.namespace.NamespaceService;
import com.yahoo.pulsar.broker.service.BrokerService;
import com.yahoo.pulsar.broker.web.PulsarWebResource;
import com.yahoo.pulsar.broker.web.RestException;
import com.yahoo.pulsar.common.policies.data.ClusterData;
import com.yahoo.pulsar.common.policies.data.Policies;
import com.yahoo.pulsar.zookeeper.ZooKeeperChildrenCache;
import com.yahoo.pulsar.zookeeper.ZooKeeperDataCache;
/**
* HTTP lookup unit tests.
*
*
*/
public class HttpDestinationLookupv2Test {
private PulsarService pulsar;
private NamespaceService ns;
private AuthorizationManager auth;
private ServiceConfiguration config;
private ConfigurationCacheService mockConfigCache;
private ZooKeeperChildrenCache clustersListCache;
private ZooKeeperDataCache<ClusterData> clustersCache;
private ZooKeeperDataCache<Policies> policiesCache;
private Set<String> clusters;
@SuppressWarnings("unchecked")
@BeforeMethod
public void setUp() throws Exception {
pulsar = mock(PulsarService.class);
ns = mock(NamespaceService.class);
auth = mock(AuthorizationManager.class);
mockConfigCache = mock(ConfigurationCacheService.class);
clustersListCache = mock(ZooKeeperChildrenCache.class);
clustersCache = mock(ZooKeeperDataCache.class);
policiesCache = mock(ZooKeeperDataCache.class);
config = spy(new ServiceConfiguration());
config.setClusterName("use");
clusters = new TreeSet<String>();
clusters.add("use");
clusters.add("usc");
clusters.add("usw");
ClusterData useData = new ClusterData("http://broker.messaging.use.example.com:8080");
ClusterData uscData = new ClusterData("http://broker.messaging.usc.example.com:8080");
ClusterData uswData = new ClusterData("http://broker.messaging.usw.example.com:8080");
doReturn(config).when(pulsar).getConfiguration();
doReturn(mockConfigCache).when(pulsar).getConfigurationCache();
doReturn(clustersListCache).when(mockConfigCache).clustersListCache();
doReturn(clustersCache).when(mockConfigCache).clustersCache();
doReturn(policiesCache).when(mockConfigCache).policiesCache();
doReturn(Optional.of(useData)).when(clustersCache).get(AdminResource.path("clusters", "use"));
doReturn(Optional.of(uscData)).when(clustersCache).get(AdminResource.path("clusters", "usc"));
doReturn(Optional.of(uswData)).when(clustersCache).get(AdminResource.path("clusters", "usw"));
doReturn(CompletableFuture.completedFuture(Optional.of(useData))).when(clustersCache).getAsync(AdminResource.path("clusters", "use"));
doReturn(CompletableFuture.completedFuture(Optional.of(uscData))).when(clustersCache).getAsync(AdminResource.path("clusters", "usc"));
doReturn(CompletableFuture.completedFuture(Optional.of(uswData))).when(clustersCache).getAsync(AdminResource.path("clusters", "usw"));
doReturn(clusters).when(clustersListCache).get();
doReturn(ns).when(pulsar).getNamespaceService();
BrokerService brokerService = mock(BrokerService.class);
doReturn(brokerService).when(pulsar).getBrokerService();
doReturn(auth).when(brokerService).getAuthorizationManager();
doReturn(new Semaphore(1000)).when(brokerService).getLookupRequestSemaphore();
}
@Test
public void crossColoLookup() throws Exception {
DestinationLookup destLookup = spy(new DestinationLookup());
doReturn(false).when(destLookup).isRequestHttps();
destLookup.setPulsar(pulsar);
doReturn("null").when(destLookup).clientAppId();
Field uriField = PulsarWebResource.class.getDeclaredField("uri");
uriField.setAccessible(true);
UriInfo uriInfo = mock(UriInfo.class);
uriField.set(destLookup, uriInfo);
URI uri = URI.create("http://localhost:8080/lookup/v2/destination/topic/myprop/usc/ns2/topic1");
doReturn(uri).when(uriInfo).getRequestUri();
doReturn(true).when(config).isAuthorizationEnabled();
AsyncResponse asyncResponse = mock(AsyncResponse.class);
destLookup.lookupDestinationAsync("myprop", "usc", "ns2", "topic1", false, asyncResponse);
ArgumentCaptor<Throwable> arg = ArgumentCaptor.forClass(Throwable.class);
verify(asyncResponse).resume(arg.capture());
assertEquals(arg.getValue().getClass(), WebApplicationException.class);
WebApplicationException wae = (WebApplicationException) arg.getValue();
assertEquals(wae.getResponse().getStatus(), Status.TEMPORARY_REDIRECT.getStatusCode());
}
@Test
public void testNotEnoughLookupPermits() throws Exception {
BrokerService brokerService = pulsar.getBrokerService();
doReturn(new Semaphore(0)).when(brokerService).getLookupRequestSemaphore();
DestinationLookup destLookup = spy(new DestinationLookup());
doReturn(false).when(destLookup).isRequestHttps();
destLookup.setPulsar(pulsar);
doReturn("null").when(destLookup).clientAppId();
Field uriField = PulsarWebResource.class.getDeclaredField("uri");
uriField.setAccessible(true);
UriInfo uriInfo = mock(UriInfo.class);
uriField.set(destLookup, uriInfo);
URI uri = URI.create("http://localhost:8080/lookup/v2/destination/topic/myprop/usc/ns2/topic1");
doReturn(uri).when(uriInfo).getRequestUri();
doReturn(true).when(config).isAuthorizationEnabled();
AsyncResponse asyncResponse1 = mock(AsyncResponse.class);
destLookup.lookupDestinationAsync("myprop", "usc", "ns2", "topic1", false, asyncResponse1);
ArgumentCaptor<Throwable> arg = ArgumentCaptor.forClass(Throwable.class);
verify(asyncResponse1).resume(arg.capture());
assertEquals(arg.getValue().getClass(), WebApplicationException.class);
WebApplicationException wae = (WebApplicationException) arg.getValue();
assertEquals(wae.getResponse().getStatus(), Status.SERVICE_UNAVAILABLE.getStatusCode());
}
@Test
public void testValidateReplicationSettingsOnNamespace() throws Exception {
final String property = "my-prop";
final String cluster = "global";
final String ns1 = "ns1";
final String ns2 = "ns2";
Policies policies1 = new Policies();
doReturn(Optional.of(policies1)).when(policiesCache)
.get(AdminResource.path("policies", property, cluster, ns1));
Policies policies2 = new Policies();
policies2.replication_clusters = Lists.newArrayList("invalid-localCluster");
doReturn(Optional.of(policies2)).when(policiesCache)
.get(AdminResource.path("policies", property, cluster, ns2));
DestinationLookup destLookup = spy(new DestinationLookup());
doReturn(false).when(destLookup).isRequestHttps();
destLookup.setPulsar(pulsar);
doReturn("null").when(destLookup).clientAppId();
Field uriField = PulsarWebResource.class.getDeclaredField("uri");
uriField.setAccessible(true);
UriInfo uriInfo = mock(UriInfo.class);
uriField.set(destLookup, uriInfo);
doReturn(false).when(config).isAuthorizationEnabled();
AsyncResponse asyncResponse = mock(AsyncResponse.class);
destLookup.lookupDestinationAsync(property, cluster, ns1, "empty-cluster", false, asyncResponse);
ArgumentCaptor<Throwable> arg = ArgumentCaptor.forClass(Throwable.class);
verify(asyncResponse).resume(arg.capture());
assertEquals(arg.getValue().getClass(), RestException.class);
AsyncResponse asyncResponse2 = mock(AsyncResponse.class);
destLookup.lookupDestinationAsync(property, cluster, ns2, "invalid-localCluster", false, asyncResponse2);
ArgumentCaptor<Throwable> arg2 = ArgumentCaptor.forClass(Throwable.class);
verify(asyncResponse2).resume(arg2.capture());
// Should have raised exception for invalid cluster
assertEquals(arg2.getValue().getClass(), RestException.class);
}
@Test
public void testDataPojo() {
final String url = "localhost:8080";
NamespaceData data1 = new NamespaceData(url);
assertEquals(data1.getBrokerUrl(), url);
RedirectData data2 = new RedirectData(url);
assertEquals(data2.getRedirectLookupAddress(), url);
}
}