Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds the bookie cookie service for http api #4541

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public abstract class HttpRouter<Handler> {
public static final String BOOKIE_INFO = "/api/v1/bookie/info";
public static final String CLUSTER_INFO = "/api/v1/bookie/cluster_info";
public static final String ENTRY_LOCATION_COMPACT = "/api/v1/bookie/entry_location_compact";
public static final String BOOKIE_COOKIE = "/api/v1/bookie/cookie";
// autorecovery
public static final String AUTORECOVERY_STATUS = "/api/v1/autorecovery/status";
public static final String RECOVERY_BOOKIE = "/api/v1/autorecovery/bookie";
Expand Down Expand Up @@ -100,6 +101,7 @@ public HttpRouter(AbstractHttpHandlerFactory<Handler> handlerFactory) {
handlerFactory.newHandler(HttpServer.ApiType.RESUME_GC_COMPACTION));
this.endpointHandlers.put(ENTRY_LOCATION_COMPACT,
handlerFactory.newHandler(HttpServer.ApiType.TRIGGER_ENTRY_LOCATION_COMPACT));
this.endpointHandlers.put(BOOKIE_COOKIE, handlerFactory.newHandler(HttpServer.ApiType.BOOKIE_COOKIE));

// autorecovery
this.endpointHandlers.put(AUTORECOVERY_STATUS, handlerFactory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ enum ApiType {
RESUME_GC_COMPACTION,
SUSPEND_GC_COMPACTION,
TRIGGER_ENTRY_LOCATION_COMPACT,
BOOKIE_COOKIE,
// autorecovery
AUTORECOVERY_STATUS,
RECOVERY_BOOKIE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.apache.bookkeeper.replication.Auditor;
import org.apache.bookkeeper.replication.AutoRecoveryMain;
import org.apache.bookkeeper.server.http.service.AutoRecoveryStatusService;
import org.apache.bookkeeper.server.http.service.BookieCookieService;
import org.apache.bookkeeper.server.http.service.BookieInfoService;
import org.apache.bookkeeper.server.http.service.BookieIsReadyService;
import org.apache.bookkeeper.server.http.service.BookieSanityService;
Expand Down Expand Up @@ -238,6 +239,8 @@ public HttpEndpointService provideHttpEndpointService(ApiType type) {
return new ResumeCompactionService(bookieServer);
case TRIGGER_ENTRY_LOCATION_COMPACT:
return new TriggerLocationCompactService(bookieServer);
case BOOKIE_COOKIE:
return new BookieCookieService(configuration);

// autorecovery
case AUTORECOVERY_STATUS:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* 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.bookkeeper.server.http.service;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.net.UnknownHostException;
import java.util.Map;
import org.apache.bookkeeper.bookie.BookieException;
import org.apache.bookkeeper.bookie.Cookie;
import org.apache.bookkeeper.conf.ServerConfiguration;
import org.apache.bookkeeper.http.HttpServer;
import org.apache.bookkeeper.http.service.HttpEndpointService;
import org.apache.bookkeeper.http.service.HttpServiceRequest;
import org.apache.bookkeeper.http.service.HttpServiceResponse;
import org.apache.bookkeeper.meta.MetadataDrivers;
import org.apache.bookkeeper.net.BookieId;
import org.apache.bookkeeper.net.BookieSocketAddress;
import org.apache.bookkeeper.versioning.LongVersion;
import org.apache.bookkeeper.versioning.Version;
import org.apache.bookkeeper.versioning.Versioned;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BookieCookieService implements HttpEndpointService {
static final Logger LOG = LoggerFactory.getLogger(BookieCookieService.class);
private final ServerConfiguration conf;

public BookieCookieService(ServerConfiguration conf) {
this.conf = conf;
}

@Override
public HttpServiceResponse handle(HttpServiceRequest request) throws Exception {
Map<String, String> params = request.getParams();
if (params == null || !params.containsKey("bookie_id")) {
return new HttpServiceResponse("Not found bookie id. Should provide bookie_id=<ip:port>",
HttpServer.StatusCode.BAD_REQUEST);
}
if (request.getMethod() == HttpServer.Method.PUT && !params.containsKey("cookie")) {
return new HttpServiceResponse("Not found cookie. "
+ "Should provide cookie=<cookie string>", HttpServer.StatusCode.BAD_REQUEST);
}

String bookieIdStr = params.get("bookie_id");
try {
new BookieSocketAddress(bookieIdStr);
} catch (UnknownHostException nhe) {
return new HttpServiceResponse("Illegal bookie id. Should provide bookie_id=<ip:port>",
HttpServer.StatusCode.BAD_REQUEST);
}

BookieId bookieId = BookieId.parse(bookieIdStr);
return MetadataDrivers.runFunctionWithRegistrationManager(conf, registrationManager -> {
try {
switch (request.getMethod()) {
case PUT:
Versioned<byte[]> newCookieWithVersion = new Versioned<>(params.get("cookie").getBytes(UTF_8),
Version.NEW);
registrationManager.writeCookie(bookieId, newCookieWithVersion);
return new HttpServiceResponse("Added cookie: " + bookieId, HttpServer.StatusCode.OK);
case GET:
Versioned<Cookie> cookie = Cookie.readFromRegistrationManager(registrationManager, bookieId);
return new HttpServiceResponse(cookie.getValue().toString(), HttpServer.StatusCode.OK);
case DELETE:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is nothing wrong with adding new interfaces,

but I think deleting cookies is a bit risky and does not comply with data integrity specifications. Although this is available in the old Command command, I suggest you still go through the node offline process ./bin/bookkeeper shell decommissionbookie [-bookieid <bookieaddress>]

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, the caller should call this api only after bookie decommission is completed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not add a method to add a cookie while we have a method to delete a cookie.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already added

registrationManager.removeCookie(bookieId, new LongVersion(-1));
return new HttpServiceResponse("Deleted cookie: " + bookieId, HttpServer.StatusCode.OK);
default:
return new HttpServiceResponse("Method not allowed. Should be GET or DELETE method",
HttpServer.StatusCode.METHOD_NOT_ALLOWED);
}
} catch (BookieException.CookieNotFoundException e) {
return new HttpServiceResponse("Not found cookie: " + bookieId, HttpServer.StatusCode.NOT_FOUND);
} catch (BookieException.CookieExistException e) {
return new HttpServiceResponse("Cookie exist for bookieId: " + bookieId, HttpServer.StatusCode.OK);
} catch (BookieException e) {
LOG.error("Failed to op bookie cookie: ", e);
return new HttpServiceResponse("Request failed, e:" + e.getMessage(),
HttpServer.StatusCode.INTERNAL_ERROR);
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
*/
package org.apache.bookkeeper.server.http;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.bookkeeper.meta.MetadataDrivers.runFunctionWithLedgerManagerFactory;
import static org.apache.bookkeeper.meta.MetadataDrivers.runFunctionWithRegistrationManager;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
Expand All @@ -39,6 +41,7 @@
import java.util.concurrent.Future;
import lombok.Cleanup;
import org.apache.bookkeeper.bookie.BookieResources;
import org.apache.bookkeeper.bookie.Cookie;
import org.apache.bookkeeper.bookie.LedgerStorage;
import org.apache.bookkeeper.client.BookKeeper;
import org.apache.bookkeeper.client.ClientUtil;
Expand All @@ -55,6 +58,7 @@
import org.apache.bookkeeper.meta.LedgerManagerFactory;
import org.apache.bookkeeper.meta.LedgerUnderreplicationManager;
import org.apache.bookkeeper.meta.MetadataBookieDriver;
import org.apache.bookkeeper.net.BookieId;
import org.apache.bookkeeper.net.BookieSocketAddress;
import org.apache.bookkeeper.proto.BookieServer;
import org.apache.bookkeeper.replication.AuditorElector;
Expand All @@ -66,6 +70,7 @@
import org.apache.bookkeeper.server.http.service.ClusterInfoService;
import org.apache.bookkeeper.stats.NullStatsLogger;
import org.apache.bookkeeper.test.BookKeeperClusterTestCase;
import org.apache.bookkeeper.versioning.Versioned;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -1217,4 +1222,63 @@ public void testTriggerEntryLocationCompactService() throws Exception {
HttpServiceResponse response7 = triggerEntryLocationCompactService.handle(request7);
assertEquals(HttpServer.StatusCode.METHOD_NOT_ALLOWED.getValue(), response7.getStatusCode());
}

@SuppressWarnings("checkstyle:RegexpSingleline")
@Test
public void testBookieCookieService() throws Exception {
runFunctionWithRegistrationManager(baseConf, registrationManager -> {
try {
String bookieId = getBookie(0).toString();
Versioned<Cookie> cookieFromZk = Cookie.readFromRegistrationManager(registrationManager,
BookieId.parse(bookieId));
HttpEndpointService bookieCookieService = bkHttpServiceProvider.provideHttpEndpointService(
HttpServer.ApiType.BOOKIE_COOKIE);
Map<String, String> params = new HashMap<>();
// empty params
HttpServiceRequest request = new HttpServiceRequest(null, HttpServer.Method.GET, params);
HttpServiceResponse response = bookieCookieService.handle(request);
assertEquals(response.getStatusCode(), HttpServer.StatusCode.BAD_REQUEST.getValue());
assertEquals(response.getBody(), "Not found bookie id. Should provide bookie_id=<ip:port>");
// invalid params
params.put("bookie_id", "bookie_id");
response = bookieCookieService.handle(request);
assertEquals(response.getStatusCode(), HttpServer.StatusCode.BAD_REQUEST.getValue());
assertEquals(response.getBody(), "Illegal bookie id. Should provide bookie_id=<ip:port>");

// cookie not found
params.put("bookie_id", "127.2.1.0:3181");
response = bookieCookieService.handle(request);
assertEquals(response.getStatusCode(), HttpServer.StatusCode.NOT_FOUND.getValue());

params.put("bookie_id", bookieId);
// GET cookie
HttpServiceRequest request1 = new HttpServiceRequest(null, HttpServer.Method.GET, params);
HttpServiceResponse response1 = bookieCookieService.handle(request1);
assertEquals(response1.getStatusCode(), HttpServer.StatusCode.OK.getValue());
assertEquals(cookieFromZk.getValue().toString(), response1.getBody());
Cookie old = Cookie.parseFromBytes(response1.getBody().getBytes(UTF_8));
// DELETE cookie
HttpServiceRequest request2 = new HttpServiceRequest(null, HttpServer.Method.DELETE, params);
HttpServiceResponse response2 = bookieCookieService.handle(request2);
assertEquals(response2.getStatusCode(), HttpServer.StatusCode.OK.getValue());

// GET cookie
HttpServiceResponse response3 = bookieCookieService.handle(request1);
assertEquals(response3.getStatusCode(), HttpServer.StatusCode.NOT_FOUND.getValue());

// create cookie
params.put("cookie", old.toString());
HttpServiceRequest request3 = new HttpServiceRequest(null, HttpServer.Method.PUT, params);
HttpServiceResponse response4 = bookieCookieService.handle(request3);
assertEquals(response4.getStatusCode(), HttpServer.StatusCode.OK.getValue());

HttpServiceResponse response5 = bookieCookieService.handle(request1);
assertEquals(response5.getStatusCode(), HttpServer.StatusCode.OK.getValue());
assertEquals(cookieFromZk.getValue().toString(), response5.getBody());
return true;
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
}
Loading