1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-01-30 05:39:12 +02:00

Add storageInfo() support to remote storage driver.

This commit is contained in:
David Steele 2019-11-16 17:30:08 -05:00
parent 8d6a8c3bf0
commit 8a3de1e05a
4 changed files with 327 additions and 12 deletions

View File

@ -26,6 +26,7 @@ Constants
***********************************************************************************************************************************/
STRING_EXTERN(PROTOCOL_COMMAND_STORAGE_EXISTS_STR, PROTOCOL_COMMAND_STORAGE_EXISTS);
STRING_EXTERN(PROTOCOL_COMMAND_STORAGE_FEATURE_STR, PROTOCOL_COMMAND_STORAGE_FEATURE);
STRING_EXTERN(PROTOCOL_COMMAND_STORAGE_INFO_STR, PROTOCOL_COMMAND_STORAGE_INFO);
STRING_EXTERN(PROTOCOL_COMMAND_STORAGE_LIST_STR, PROTOCOL_COMMAND_STORAGE_LIST);
STRING_EXTERN(PROTOCOL_COMMAND_STORAGE_OPEN_READ_STR, PROTOCOL_COMMAND_STORAGE_OPEN_READ);
STRING_EXTERN(PROTOCOL_COMMAND_STORAGE_OPEN_WRITE_STR, PROTOCOL_COMMAND_STORAGE_OPEN_WRITE);
@ -90,6 +91,73 @@ storageRemoteFilterGroup(IoFilterGroup *filterGroup, const Variant *filterList)
FUNCTION_TEST_RETURN_VOID();
}
/***********************************************************************************************************************************
Write storage info into the protocol
***********************************************************************************************************************************/
// Helper to write storage type into the protocol
static void
storageRemoteInfoWriteType(ProtocolServer *server, StorageType type)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(PROTOCOL_SERVER, server);
FUNCTION_TEST_PARAM(ENUM, type);
FUNCTION_TEST_END();
switch (type)
{
case storageTypeFile:
{
protocolServerWriteLine(server, STRDEF("f"));
break;
}
case storageTypePath:
{
protocolServerWriteLine(server, STRDEF("p"));
break;
}
case storageTypeLink:
{
protocolServerWriteLine(server, STRDEF("l"));
break;
}
case storageTypeSpecial:
{
protocolServerWriteLine(server, STRDEF("s"));
break;
}
}
FUNCTION_TEST_RETURN_VOID();
}
static void
storageRemoteInfoWrite(ProtocolServer *server, const StorageInfo *info)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(PROTOCOL_SERVER, server);
FUNCTION_TEST_PARAM(STORAGE_INFO, info);
FUNCTION_TEST_END();
storageRemoteInfoWriteType(server, info->type);
protocolServerWriteLine(server, jsonFromUInt(info->userId));
protocolServerWriteLine(server, jsonFromStr(info->user));
protocolServerWriteLine(server, jsonFromUInt(info->groupId));
protocolServerWriteLine(server, jsonFromStr(info->group));
protocolServerWriteLine(server, jsonFromUInt(info->mode));
protocolServerWriteLine(server, jsonFromInt64(info->timeModified));
if (info->type == storageTypeFile)
protocolServerWriteLine(server, jsonFromUInt64(info->size));
if (info->type == storageTypeLink)
protocolServerWriteLine(server, jsonFromStr(info->linkDestination));
FUNCTION_TEST_RETURN_VOID();
}
/***********************************************************************************************************************************
Process storage protocol requests
***********************************************************************************************************************************/
@ -125,6 +193,18 @@ storageRemoteProtocol(const String *command, const VariantList *paramList, Proto
protocolServerResponse(server, NULL);
}
else if (strEq(command, PROTOCOL_COMMAND_STORAGE_INFO_STR))
{
StorageInfo info = interface.info(driver, varStr(varLstGet(paramList, 0)), varBool(varLstGet(paramList, 1)));
protocolServerResponse(server, VARBOOL(info.exists));
if (info.exists)
{
storageRemoteInfoWrite(server, &info);
protocolServerResponse(server, NULL);
}
}
else if (strEq(command, PROTOCOL_COMMAND_STORAGE_LIST_STR))
{
protocolServerResponse(

View File

@ -17,6 +17,8 @@ Constants
STRING_DECLARE(PROTOCOL_COMMAND_STORAGE_EXISTS_STR);
#define PROTOCOL_COMMAND_STORAGE_FEATURE "storageFeature"
STRING_DECLARE(PROTOCOL_COMMAND_STORAGE_FEATURE_STR);
#define PROTOCOL_COMMAND_STORAGE_INFO "storageInfo"
STRING_DECLARE(PROTOCOL_COMMAND_STORAGE_INFO_STR);
#define PROTOCOL_COMMAND_STORAGE_LIST "storageList"
STRING_DECLARE(PROTOCOL_COMMAND_STORAGE_LIST_STR);
#define PROTOCOL_COMMAND_STORAGE_OPEN_READ "storageOpenRead"

View File

@ -61,6 +61,58 @@ storageRemoteExists(THIS_VOID, const String *file)
/***********************************************************************************************************************************
File/path info
***********************************************************************************************************************************/
// Helper to convert protocol storage type to an enum
static StorageType
storageRemoteInfoParseType(const char type)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(CHAR, type);
FUNCTION_TEST_END();
switch (type)
{
case 'f':
FUNCTION_TEST_RETURN(storageTypeFile);
case 'p':
FUNCTION_TEST_RETURN(storageTypePath);
case 'l':
FUNCTION_TEST_RETURN(storageTypeLink);
case 's':
FUNCTION_TEST_RETURN(storageTypeSpecial);
}
THROW_FMT(AssertError, "unknown storage type '%c'", type);
}
// Helper to parse storage info from the protocol output
static void
storageRemoteInfoParse(ProtocolClient *client, StorageInfo *info)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(PROTOCOL_CLIENT, client);
FUNCTION_TEST_PARAM(STORAGE_INFO, info);
FUNCTION_TEST_END();
info->type = storageRemoteInfoParseType(strPtr(protocolClientReadLine(client))[0]);
info->userId = jsonToUInt(protocolClientReadLine(client));
info->user = jsonToStr(protocolClientReadLine(client));
info->groupId = jsonToUInt(protocolClientReadLine(client));
info->group = jsonToStr(protocolClientReadLine(client));
info->mode = jsonToUInt(protocolClientReadLine(client));
info->timeModified = (time_t)jsonToUInt64(protocolClientReadLine(client));
if (info->type == storageTypeFile)
info->size = jsonToUInt64(protocolClientReadLine(client));
if (info->type == storageTypeLink)
info->linkDestination = jsonToStr(protocolClientReadLine(client));
FUNCTION_TEST_RETURN_VOID();
}
static StorageInfo
storageRemoteInfo(THIS_VOID, const String *file, bool followLink)
{
@ -73,11 +125,37 @@ storageRemoteInfo(THIS_VOID, const String *file, bool followLink)
FUNCTION_LOG_END();
ASSERT(this != NULL);
ASSERT(file != NULL);
THROW(AssertError, "NOT YET IMPLEMENTED");
StorageInfo result = {.exists = false};
FUNCTION_LOG_RETURN(STORAGE_INFO, (StorageInfo){0});
MEM_CONTEXT_TEMP_BEGIN()
{
ProtocolCommand *command = protocolCommandNew(PROTOCOL_COMMAND_STORAGE_INFO_STR);
protocolCommandParamAdd(command, VARSTR(file));
protocolCommandParamAdd(command, VARBOOL(followLink));
result.exists = varBool(protocolClientExecute(this->client, command, true));
if (result.exists)
{
// Read info from protocol
storageRemoteInfoParse(this->client, &result);
// Acknowledge command completed
protocolClientReadOutput(this->client, false);
// Duplicate strings into the calling context
memContextSwitch(MEM_CONTEXT_OLD());
result.name = strDup(result.name);
result.linkDestination = strDup(result.linkDestination);
result.user = strDup(result.user);
result.group = strDup(result.group);
memContextSwitch(MEM_CONTEXT_TEMP());
}
}
MEM_CONTEXT_TEMP_END();
FUNCTION_LOG_RETURN(STORAGE_INFO, result);
}
/***********************************************************************************************************************************

View File

@ -1,6 +1,8 @@
/***********************************************************************************************************************************
Test Remote Storage
***********************************************************************************************************************************/
#include <utime.h>
#include "command/backup/pageChecksum.h"
#include "common/crypto/cipherBlock.h"
#include "common/io/bufferRead.h"
@ -111,6 +113,168 @@ testRun(void)
cfgOptionValidSet(cfgOptType, true);
}
// *****************************************************************************************************************************
if (testBegin("storageInfo()"))
{
Storage *storageRemote = NULL;
TEST_ASSIGN(storageRemote, storageRepoGet(strNew(STORAGE_TYPE_POSIX), true), "get remote repo storage");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("storage types that are not tested elsewhere");
TEST_RESULT_UINT(storageRemoteInfoParseType('s'), storageTypeSpecial, "read special type");
TEST_ERROR(storageRemoteInfoParseType('z'), AssertError, "unknown storage type 'z'");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("missing file/path");
TEST_ERROR(
storageInfoNP(storageRemote, strNew(BOGUS_STR)), FileOpenError,
hrnReplaceKey("unable to get info for missing path/file '{[path]}/repo/BOGUS'"));
TEST_RESULT_BOOL(storageInfoP(storageRemote, strNew(BOGUS_STR), .ignoreMissing = true).exists, false, "missing file/path");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("path info");
storagePathCreateNP(storageTest, strNew("repo"));
struct utimbuf utimeTest = {.actime = 1000000000, .modtime = 1555160000};
THROW_ON_SYS_ERROR(
utime(strPtr(storagePath(storageTest, strNew("repo"))), &utimeTest) != 0, FileWriteError, "unable to set time");
StorageInfo info = {.exists = false};
TEST_ASSIGN(info, storageInfoP(storageRemote, NULL), "valid path");
TEST_RESULT_PTR(info.name, NULL, " name is not set");
TEST_RESULT_BOOL(info.exists, true, " check exists");
TEST_RESULT_INT(info.type, storageTypePath, " check type");
TEST_RESULT_INT(info.size, 0, " check size");
TEST_RESULT_INT(info.mode, 0750, " check mode");
TEST_RESULT_UINT(info.timeModified, 1555160000, " check mod time");
TEST_RESULT_PTR(info.linkDestination, NULL, " no link destination");
TEST_RESULT_UINT(info.userId, getuid(), " check user id");
TEST_RESULT_STR(strPtr(info.user), testUser(), " check user");
TEST_RESULT_UINT(info.groupId, getgid(), " check group id");
TEST_RESULT_STR(strPtr(info.group), testGroup(), " check group");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("file info");
storagePutNP(storageNewWriteP(storageRemote, strNew("test"), .timeModified = 1555160001), BUFSTRDEF("TESTME"));
TEST_ASSIGN(info, storageInfoNP(storageRemote, strNew("test")), "valid file");
TEST_RESULT_PTR(info.name, NULL, " name is not set");
TEST_RESULT_BOOL(info.exists, true, " check exists");
TEST_RESULT_INT(info.type, storageTypeFile, " check type");
TEST_RESULT_INT(info.size, 6, " check size");
TEST_RESULT_INT(info.mode, 0640, " check mode");
TEST_RESULT_UINT(info.timeModified, 1555160001, " check mod time");
TEST_RESULT_PTR(info.linkDestination, NULL, " no link destination");
TEST_RESULT_UINT(info.userId, getuid(), " check user id");
TEST_RESULT_STR(strPtr(info.user), testUser(), " check user");
TEST_RESULT_UINT(info.groupId, getgid(), " check group id");
TEST_RESULT_STR(strPtr(info.group), testGroup(), " check group");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("special info");
TEST_SYSTEM_FMT("mkfifo -m 666 %s", strPtr(storagePath(storageTest, strNew("repo/fifo"))));
TEST_ASSIGN(info, storageInfoNP(storageRemote, strNew("fifo")), "valid fifo");
TEST_RESULT_PTR(info.name, NULL, " name is not set");
TEST_RESULT_BOOL(info.exists, true, " check exists");
TEST_RESULT_INT(info.type, storageTypeSpecial, " check type");
TEST_RESULT_INT(info.size, 0, " check size");
TEST_RESULT_INT(info.mode, 0666, " check mode");
TEST_RESULT_PTR(info.linkDestination, NULL, " no link destination");
TEST_RESULT_UINT(info.userId, getuid(), " check user id");
TEST_RESULT_STR(strPtr(info.user), testUser(), " check user");
TEST_RESULT_UINT(info.groupId, getgid(), " check group id");
TEST_RESULT_STR(strPtr(info.group), testGroup(), " check group");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("link info");
TEST_SYSTEM_FMT("ln -s ../repo/test %s", strPtr(storagePath(storageTest, strNew("repo/link"))));
TEST_ASSIGN(info, storageInfoNP(storageRemote, strNew("link")), "valid link");
TEST_RESULT_PTR(info.name, NULL, " name is not set");
TEST_RESULT_BOOL(info.exists, true, " check exists");
TEST_RESULT_INT(info.type, storageTypeLink, " check type");
TEST_RESULT_INT(info.size, 0, " check size");
TEST_RESULT_INT(info.mode, 0777, " check mode");
TEST_RESULT_STR_Z(info.linkDestination, "../repo/test", " check link destination");
TEST_RESULT_UINT(info.userId, getuid(), " check user id");
TEST_RESULT_STR(strPtr(info.user), testUser(), " check user");
TEST_RESULT_UINT(info.groupId, getgid(), " check group id");
TEST_RESULT_STR(strPtr(info.group), testGroup(), " check group");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("link info follow");
TEST_ASSIGN(info, storageInfoP(storageRemote, strNew("link"), .followLink = true), "valid link follow");
TEST_RESULT_PTR(info.name, NULL, " name is not set");
TEST_RESULT_BOOL(info.exists, true, " check exists");
TEST_RESULT_INT(info.type, storageTypeFile, " check type");
TEST_RESULT_INT(info.size, 6, " check size");
TEST_RESULT_INT(info.mode, 0640, " check mode");
TEST_RESULT_PTR(info.linkDestination, NULL, " no link destination");
TEST_RESULT_UINT(info.userId, getuid(), " check user id");
TEST_RESULT_STR(strPtr(info.user), testUser(), " check user");
TEST_RESULT_UINT(info.groupId, getgid(), " check group id");
TEST_RESULT_STR(strPtr(info.group), testGroup(), " check group");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("protocol storage types that are not tested elsewhere");
TEST_RESULT_VOID(storageRemoteInfoWriteType(server, storageTypePath), "write path type");
TEST_RESULT_VOID(storageRemoteInfoWriteType(server, storageTypeSpecial), "write special type");
ioWriteFlush(serverWriteIo);
TEST_RESULT_STR(strPtr(strNewBuf(serverWrite)), ".p\n.s\n", "check result");
bufUsedSet(serverWrite, 0);
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("protocol output that is not tested elsewhere");
info = (StorageInfo){.type = storageTypeLink, .linkDestination = STRDEF("../")};
TEST_RESULT_VOID(storageRemoteInfoWrite(server, &info), "write link info");
ioWriteFlush(serverWriteIo);
TEST_RESULT_STR(strPtr(strNewBuf(serverWrite)), ".l\n.0\n.null\n.0\n.null\n.0\n.0\n.\"../\"\n", "check result");
bufUsedSet(serverWrite, 0);
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("check protocol function directly with missing path/file");
VariantList *paramList = varLstNew();
varLstAdd(paramList, varNewStrZ(BOGUS_STR));
varLstAdd(paramList, varNewBool(false));
TEST_RESULT_BOOL(storageRemoteProtocol(PROTOCOL_COMMAND_STORAGE_INFO_STR, paramList, server), true, "protocol list");
TEST_RESULT_STR(strPtr(strNewBuf(serverWrite)), "{\"out\":false}\n", "check result");
bufUsedSet(serverWrite, 0);
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("check protocol function directly with a file");
paramList = varLstNew();
varLstAdd(paramList, varNewStrZ(hrnReplaceKey("{[path]}/repo/test")));
varLstAdd(paramList, varNewBool(false));
TEST_RESULT_BOOL(storageRemoteProtocol(PROTOCOL_COMMAND_STORAGE_INFO_STR, paramList, server), true, "protocol list");
TEST_RESULT_STR(
strPtr(strNewBuf(serverWrite)),
hrnReplaceKey(
"{\"out\":true}\n"
".f\n.{[user-id]}\n.\"{[user]}\"\n.{[group-id]}\n.\"{[group]}\"\n.416\n.1555160001\n.6\n"
"{}\n"),
"check result");
bufUsedSet(serverWrite, 0);
}
// *****************************************************************************************************************************
if (testBegin("storageList()"))
{
@ -621,15 +785,6 @@ testRun(void)
strPtr(strNewFmt("%s/repo/anewpath", testPath())));
}
// *****************************************************************************************************************************
if (testBegin("UNIMPLEMENTED"))
{
Storage *storageRemote = NULL;
TEST_ASSIGN(storageRemote, storageRepoGet(strNew(STORAGE_TYPE_POSIX), true), "get remote repo storage");
TEST_ERROR(storageInfoNP(storageRemote, strNew("file.txt")), AssertError, "NOT YET IMPLEMENTED");
}
protocolFree();
FUNCTION_HARNESS_RESULT_VOID();