use of org.apache.pulsar.broker.namespace.LookupOptions in project pulsar by apache.
the class PulsarWebResource method validateTopicOwnershipAsync.
protected CompletableFuture<Void> validateTopicOwnershipAsync(TopicName topicName, boolean authoritative) {
NamespaceService nsService = pulsar().getNamespaceService();
LookupOptions options = LookupOptions.builder().authoritative(authoritative).requestHttps(isRequestHttps()).readOnly(false).loadTopicsInBundle(false).build();
return nsService.getWebServiceUrlAsync(topicName, options).thenApply(webUrl -> {
// Ensure we get a url
if (webUrl == null || !webUrl.isPresent()) {
log.info("Unable to get web service url");
throw new RestException(Status.PRECONDITION_FAILED, "Failed to find ownership for topic:" + topicName);
}
return webUrl.get();
}).thenCompose(webUrl -> nsService.isServiceUnitOwnedAsync(topicName).thenApply(isTopicOwned -> Pair.of(webUrl, isTopicOwned))).thenAccept(pair -> {
URL webUrl = pair.getLeft();
boolean isTopicOwned = pair.getRight();
if (!isTopicOwned) {
boolean newAuthoritative = isLeaderBroker(pulsar());
// Replace the host and port of the current request and redirect
URI redirect = UriBuilder.fromUri(uri.getRequestUri()).host(webUrl.getHost()).port(webUrl.getPort()).replaceQueryParam("authoritative", newAuthoritative).build();
// Redirect
if (log.isDebugEnabled()) {
log.debug("Redirecting the rest call to {}", redirect);
}
throw new WebApplicationException(Response.temporaryRedirect(redirect).build());
}
}).exceptionally(ex -> {
if (ex.getCause() instanceof IllegalArgumentException || ex.getCause() instanceof IllegalStateException) {
if (log.isDebugEnabled()) {
log.debug("Failed to find owner for topic: {}", topicName, ex);
}
throw new RestException(Status.PRECONDITION_FAILED, "Can't find owner for topic " + topicName);
} else if (ex.getCause() instanceof WebApplicationException) {
throw (WebApplicationException) ex.getCause();
} else {
throw new RestException(ex.getCause());
}
});
}
use of org.apache.pulsar.broker.namespace.LookupOptions in project pulsar by apache.
the class PulsarWebResource method validateBundleOwnership.
public void validateBundleOwnership(NamespaceBundle bundle, boolean authoritative, boolean readOnly) throws Exception {
NamespaceService nsService = pulsar().getNamespaceService();
try {
// Call getWebServiceUrl() to acquire or redirect the request
// Get web service URL of owning broker.
// 1: If namespace is assigned to this broker, continue
// 2: If namespace is assigned to another broker, redirect to the webservice URL of another broker
// authoritative flag is ignored
// 3: If namespace is unassigned and readOnly is true, return 412
// 4: If namespace is unassigned and readOnly is false:
// - If authoritative is false and this broker is not leader, forward to leader
// - If authoritative is false and this broker is leader, determine owner and forward w/ authoritative=true
// - If authoritative is true, own the namespace and continue
LookupOptions options = LookupOptions.builder().authoritative(authoritative).requestHttps(isRequestHttps()).readOnly(readOnly).loadTopicsInBundle(false).build();
Optional<URL> webUrl = nsService.getWebServiceUrl(bundle, options);
// Ensure we get a url
if (webUrl == null || !webUrl.isPresent()) {
log.warn("Unable to get web service url");
throw new RestException(Status.PRECONDITION_FAILED, "Failed to find ownership for ServiceUnit:" + bundle.toString());
}
if (!nsService.isServiceUnitOwned(bundle)) {
boolean newAuthoritative = this.isLeaderBroker();
// Replace the host and port of the current request and redirect
URI redirect = UriBuilder.fromUri(uri.getRequestUri()).host(webUrl.get().getHost()).port(webUrl.get().getPort()).replaceQueryParam("authoritative", newAuthoritative).build();
log.debug("{} is not a service unit owned", bundle);
// Redirect
log.debug("Redirecting the rest call to {}", redirect);
throw new WebApplicationException(Response.temporaryRedirect(redirect).build());
}
} catch (TimeoutException te) {
String msg = String.format("Finding owner for ServiceUnit %s timed out", bundle);
log.error(msg, te);
throw new RestException(Status.INTERNAL_SERVER_ERROR, msg);
} catch (IllegalArgumentException iae) {
// namespace format is not valid
log.debug("Failed to find owner for ServiceUnit {}", bundle, iae);
throw new RestException(Status.PRECONDITION_FAILED, "ServiceUnit format is not expected. ServiceUnit " + bundle);
} catch (IllegalStateException ise) {
log.debug("Failed to find owner for ServiceUnit {}", bundle, ise);
throw new RestException(Status.PRECONDITION_FAILED, "ServiceUnit bundle is actived. ServiceUnit " + bundle);
} catch (NullPointerException e) {
log.warn("Unable to get web service url");
throw new RestException(Status.PRECONDITION_FAILED, "Failed to find ownership for ServiceUnit:" + bundle);
} catch (WebApplicationException wae) {
throw wae;
}
}
use of org.apache.pulsar.broker.namespace.LookupOptions in project pulsar by apache.
the class NamespacesTest method testDeleteNamespaceWithBundles.
@Test
public void testDeleteNamespaceWithBundles() throws Exception {
URL localWebServiceUrl = new URL(pulsar.getSafeWebServiceAddress());
String bundledNsLocal = "test-delete-namespace-with-bundles";
List<String> boundaries = Lists.newArrayList("0x00000000", "0x80000000", "0xffffffff");
BundlesData bundleData = BundlesData.builder().boundaries(boundaries).numBundles(boundaries.size() - 1).build();
createBundledTestNamespaces(this.testTenant, this.testLocalCluster, bundledNsLocal, bundleData);
final NamespaceName testNs = NamespaceName.get(this.testTenant, this.testLocalCluster, bundledNsLocal);
org.apache.pulsar.client.admin.Namespaces namespacesAdmin = mock(org.apache.pulsar.client.admin.Namespaces.class);
doReturn(namespacesAdmin).when(admin).namespaces();
doReturn(null).when(nsSvc).getWebServiceUrl(Mockito.argThat(new ArgumentMatcher<NamespaceBundle>() {
@Override
public boolean matches(NamespaceBundle bundle) {
return bundle.getNamespaceObject().equals(testNs);
}
}), Mockito.any());
doReturn(false).when(nsSvc).isServiceUnitOwned(Mockito.argThat(new ArgumentMatcher<NamespaceBundle>() {
@Override
public boolean matches(NamespaceBundle bundle) {
return bundle.getNamespaceObject().equals(testNs);
}
}));
doReturn(CompletableFuture.completedFuture(Optional.of(mock(NamespaceEphemeralData.class)))).when(nsSvc).getOwnerAsync(Mockito.argThat(new ArgumentMatcher<NamespaceBundle>() {
@Override
public boolean matches(NamespaceBundle bundle) {
return bundle.getNamespaceObject().equals(testNs);
}
}));
CompletableFuture<Void> preconditionFailed = new CompletableFuture<>();
ClientErrorException cee = new ClientErrorException(Status.PRECONDITION_FAILED);
int statusCode = cee.getResponse().getStatus();
String httpError = BaseResource.getReasonFromServer(cee);
preconditionFailed.completeExceptionally(new PulsarAdminException.PreconditionFailedException(cee, httpError, statusCode));
doReturn(preconditionFailed).when(namespacesAdmin).deleteNamespaceBundleAsync(Mockito.anyString(), Mockito.anyString());
try {
namespaces.deleteNamespaceBundle(testTenant, testLocalCluster, bundledNsLocal, "0x00000000_0x80000000", false, false);
fail("Should have failed");
} catch (RestException re) {
assertEquals(re.getResponse().getStatus(), Status.PRECONDITION_FAILED.getStatusCode());
}
NamespaceBundles nsBundles = nsSvc.getNamespaceBundleFactory().getBundles(testNs, bundleData);
doReturn(Optional.empty()).when(nsSvc).getWebServiceUrl(any(NamespaceBundle.class), any(LookupOptions.class));
AsyncResponse response = mock(AsyncResponse.class);
namespaces.deleteNamespace(response, testTenant, testLocalCluster, bundledNsLocal, false, false);
ArgumentCaptor<RestException> captor = ArgumentCaptor.forClass(RestException.class);
verify(response, timeout(5000).times(1)).resume(captor.capture());
assertEquals(captor.getValue().getResponse().getStatus(), Status.PRECONDITION_FAILED.getStatusCode());
// make one bundle owned
LookupOptions optionsHttps = LookupOptions.builder().authoritative(false).requestHttps(true).readOnly(false).build();
doReturn(Optional.of(localWebServiceUrl)).when(nsSvc).getWebServiceUrl(nsBundles.getBundles().get(0), optionsHttps);
doReturn(true).when(nsSvc).isServiceUnitOwned(nsBundles.getBundles().get(0));
doReturn(CompletableFuture.completedFuture(null)).when(namespacesAdmin).deleteNamespaceBundleAsync(testTenant + "/" + testLocalCluster + "/" + bundledNsLocal, "0x00000000_0x80000000");
try {
namespaces.deleteNamespaceBundle(testTenant, testLocalCluster, bundledNsLocal, "0x80000000_0xffffffff", false, false);
fail("Should have failed");
} catch (RestException re) {
assertEquals(re.getResponse().getStatus(), Status.PRECONDITION_FAILED.getStatusCode());
}
response = mock(AsyncResponse.class);
doReturn(Optional.of(localWebServiceUrl)).when(nsSvc).getWebServiceUrl(any(NamespaceBundle.class), any(LookupOptions.class));
for (NamespaceBundle bundle : nsBundles.getBundles()) {
doReturn(true).when(nsSvc).isServiceUnitOwned(bundle);
}
namespaces.deleteNamespace(response, testTenant, testLocalCluster, bundledNsLocal, false, false);
ArgumentCaptor<Response> captor2 = ArgumentCaptor.forClass(Response.class);
verify(response, timeout(5000).times(1)).resume(captor2.capture());
assertEquals(captor2.getValue().getStatus(), Status.NO_CONTENT.getStatusCode());
}
use of org.apache.pulsar.broker.namespace.LookupOptions in project pulsar by apache.
the class TopicLookupBase method lookupTopicAsync.
/**
* Lookup broker-service address for a given namespace-bundle which contains given topic.
*
* a. Returns broker-address if namespace-bundle is already owned by any broker
* b. If current-broker receives lookup-request and if it's not a leader then current broker redirects request
* to leader by returning leader-service address.
* c. If current-broker is leader then it finds out least-loaded broker
* to own namespace bundle and redirects request
* by returning least-loaded broker.
* d. If current-broker receives request to own the namespace-bundle then
* it owns a bundle and returns success(connect)
* response to client.
*
* @param pulsarService
* @param topicName
* @param authoritative
* @param clientAppId
* @param requestId
* @param advertisedListenerName
* @return
*/
public static CompletableFuture<ByteBuf> lookupTopicAsync(PulsarService pulsarService, TopicName topicName, boolean authoritative, String clientAppId, AuthenticationDataSource authenticationData, long requestId, final String advertisedListenerName) {
final CompletableFuture<ByteBuf> validationFuture = new CompletableFuture<>();
final CompletableFuture<ByteBuf> lookupfuture = new CompletableFuture<>();
final String cluster = topicName.getCluster();
// (1) validate cluster
getClusterDataIfDifferentCluster(pulsarService, cluster, clientAppId).thenAccept(differentClusterData -> {
if (differentClusterData != null) {
if (log.isDebugEnabled()) {
log.debug("[{}] Redirecting the lookup call to {}/{} cluster={}", clientAppId, differentClusterData.getBrokerServiceUrl(), differentClusterData.getBrokerServiceUrlTls(), cluster);
}
validationFuture.complete(newLookupResponse(differentClusterData.getBrokerServiceUrl(), differentClusterData.getBrokerServiceUrlTls(), true, LookupType.Redirect, requestId, false));
} else {
// (2) authorize client
try {
checkAuthorization(pulsarService, topicName, clientAppId, authenticationData);
} catch (RestException authException) {
log.warn("Failed to authorized {} on cluster {}", clientAppId, topicName.toString());
validationFuture.complete(newLookupErrorResponse(ServerError.AuthorizationError, authException.getMessage(), requestId));
return;
} catch (Exception e) {
log.warn("Unknown error while authorizing {} on cluster {}", clientAppId, topicName.toString());
validationFuture.completeExceptionally(e);
return;
}
// (3) validate global namespace
checkLocalOrGetPeerReplicationCluster(pulsarService, topicName.getNamespaceObject()).thenAccept(peerClusterData -> {
if (peerClusterData == null) {
// (4) all validation passed: initiate lookup
validationFuture.complete(null);
return;
}
// request should be redirect to the peer-cluster
if (StringUtils.isBlank(peerClusterData.getBrokerServiceUrl()) && StringUtils.isBlank(peerClusterData.getBrokerServiceUrlTls())) {
validationFuture.complete(newLookupErrorResponse(ServerError.MetadataError, "Redirected cluster's brokerService url is not configured", requestId));
return;
}
validationFuture.complete(newLookupResponse(peerClusterData.getBrokerServiceUrl(), peerClusterData.getBrokerServiceUrlTls(), true, LookupType.Redirect, requestId, false));
}).exceptionally(ex -> {
validationFuture.complete(newLookupErrorResponse(ServerError.MetadataError, ex.getMessage(), requestId));
return null;
});
}
}).exceptionally(ex -> {
validationFuture.completeExceptionally(ex);
return null;
});
// Initiate lookup once validation completes
validationFuture.thenAccept(validationFailureResponse -> {
if (validationFailureResponse != null) {
lookupfuture.complete(validationFailureResponse);
} else {
LookupOptions options = LookupOptions.builder().authoritative(authoritative).advertisedListenerName(advertisedListenerName).loadTopicsInBundle(true).build();
pulsarService.getNamespaceService().getBrokerServiceUrlAsync(topicName, options).thenAccept(lookupResult -> {
if (log.isDebugEnabled()) {
log.debug("[{}] Lookup result {}", topicName.toString(), lookupResult);
}
if (!lookupResult.isPresent()) {
lookupfuture.complete(newLookupErrorResponse(ServerError.ServiceNotReady, "No broker was available to own " + topicName, requestId));
return;
}
LookupData lookupData = lookupResult.get().getLookupData();
if (lookupResult.get().isRedirect()) {
boolean newAuthoritative = lookupResult.get().isAuthoritativeRedirect();
lookupfuture.complete(newLookupResponse(lookupData.getBrokerUrl(), lookupData.getBrokerUrlTls(), newAuthoritative, LookupType.Redirect, requestId, false));
} else {
ServiceConfiguration conf = pulsarService.getConfiguration();
lookupfuture.complete(newLookupResponse(lookupData.getBrokerUrl(), lookupData.getBrokerUrlTls(), true, /* authoritative */
LookupType.Connect, requestId, shouldRedirectThroughServiceUrl(conf, lookupData)));
}
}).exceptionally(ex -> {
if (ex instanceof CompletionException && ex.getCause() instanceof IllegalStateException) {
log.info("Failed to lookup {} for topic {} with error {}", clientAppId, topicName.toString(), ex.getCause().getMessage());
} else {
log.warn("Failed to lookup {} for topic {} with error {}", clientAppId, topicName.toString(), ex.getMessage(), ex);
}
lookupfuture.complete(newLookupErrorResponse(ServerError.ServiceNotReady, ex.getMessage(), requestId));
return null;
});
}
}).exceptionally(ex -> {
if (ex instanceof CompletionException && ex.getCause() instanceof IllegalStateException) {
log.info("Failed to lookup {} for topic {} with error {}", clientAppId, topicName.toString(), ex.getCause().getMessage());
} else {
log.warn("Failed to lookup {} for topic {} with error {}", clientAppId, topicName.toString(), ex.getMessage(), ex);
}
lookupfuture.complete(newLookupErrorResponse(ServerError.ServiceNotReady, ex.getMessage(), requestId));
return null;
});
return lookupfuture;
}
use of org.apache.pulsar.broker.namespace.LookupOptions in project pulsar by yahoo.
the class PulsarWebResource method isBundleOwnedByAnyBroker.
/**
* Checks whether a given bundle is currently loaded by any broker.
*/
protected CompletableFuture<Boolean> isBundleOwnedByAnyBroker(NamespaceName fqnn, BundlesData bundles, String bundleRange) {
NamespaceBundle nsBundle = validateNamespaceBundleRange(fqnn, bundles, bundleRange);
NamespaceService nsService = pulsar().getNamespaceService();
LookupOptions options = LookupOptions.builder().authoritative(false).requestHttps(isRequestHttps()).readOnly(true).loadTopicsInBundle(false).build();
try {
return nsService.getWebServiceUrlAsync(nsBundle, options).thenApply(optionUrl -> optionUrl.isPresent());
} catch (Exception e) {
log.error("Failed to check whether namespace bundle is owned {}/{}", fqnn.toString(), bundleRange, e);
throw new RestException(e);
}
}
Aggregations