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:
parent
8d6a8c3bf0
commit
8a3de1e05a
@ -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(
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user