2022-10-27 23:44:06 +08:00
|
|
|
/*
|
|
|
|
* BlobMetadataUtils.cpp
|
|
|
|
*
|
|
|
|
* This source file is part of the FoundationDB open source project
|
|
|
|
*
|
|
|
|
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "fdbclient/BlobMetadataUtils.h"
|
|
|
|
|
|
|
|
#include "fmt/format.h"
|
|
|
|
#include "flow/IRandom.h"
|
|
|
|
#include "flow/flow.h"
|
|
|
|
#include "fdbclient/Knobs.h"
|
2022-12-28 09:29:54 +08:00
|
|
|
#include "flow/IConnection.h"
|
2022-10-27 23:44:06 +08:00
|
|
|
#include "fdbclient/S3BlobStore.h"
|
|
|
|
|
|
|
|
std::string buildPartitionPath(const std::string& url, const std::string& partition) {
|
|
|
|
ASSERT(!partition.empty());
|
|
|
|
ASSERT(partition.front() != '/');
|
|
|
|
ASSERT(partition.back() == '/');
|
|
|
|
StringRef u(url);
|
|
|
|
if (u.startsWith("file://"_sr)) {
|
|
|
|
ASSERT(u.endsWith("/"_sr));
|
|
|
|
return url + partition;
|
|
|
|
} else if (u.startsWith("blobstore://"_sr)) {
|
|
|
|
std::string resource;
|
|
|
|
std::string lastOpenError;
|
|
|
|
S3BlobStoreEndpoint::ParametersT backupParams;
|
|
|
|
|
|
|
|
std::string urlCopy = url;
|
|
|
|
|
|
|
|
Reference<S3BlobStoreEndpoint> bstore =
|
|
|
|
S3BlobStoreEndpoint::fromString(url, {}, &resource, &lastOpenError, &backupParams);
|
|
|
|
|
|
|
|
ASSERT(!resource.empty());
|
|
|
|
ASSERT(resource.back() != '/');
|
|
|
|
size_t resourceStart = url.find(resource);
|
|
|
|
ASSERT(resourceStart != std::string::npos);
|
|
|
|
|
|
|
|
return urlCopy.insert(resourceStart + resource.size(), "/" + partition);
|
|
|
|
} else {
|
|
|
|
// FIXME: support azure
|
|
|
|
throw backup_invalid_url();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Standalone<BlobMetadataDetailsRef> createRandomTestBlobMetadata(const std::string& baseUrl,
|
[EAR]: Remove usage of EncryptDomainName for Encryption at-rest operations (#8715)
* [EAR]: Remove usage of EncryptDomainName for Encryption at-rest operations
Description
diff-1: Address review comments
EncryptDomainName is an auxillary information, given EAR encryption domain
matches with Tenants, EncryptDomainName maps to TenantName in the current
code. However, this mapping adds EAR depedency has multiple drawbacks:
1. In some scenarios obtaning consistent mapping of TenantId <-> TenantName
is difficult to maintain. For instance: StorageServer (SS) TLog mutation
pop loop, it is possible that same commit batch contains: TenantMap update
mutation as well as a Tenant user mutation. SS would parse TenantMap update
mutation (FDB System Keyspace encryption domain), process the mutation, but,
doesn't apply it to the process local TenantMap. SS then attempts to process,
Tenant user mutation and fails to decrypt the mutation given TenantMetadaMap
isn't updated yet.
2. FDB codebase uses EncryptDomainId matching TenantId, TenantName is used as
an auxillary information source and feels better to be handled by an
external KMS.
Major changes include:
1. EAR to remove TenantName dependency across all participating processes
such as: CommitProxy, Redwood, BlobGranule and Backup agent.
2. Update EKP and KmsConnector APIs to avoid relying on "domainName"
information being passed around to external KMS EAR endpoints.
Testing
devRunCorrectness - 100K
EncryptKeyProxyTest - 100K
EncryptionOps Test - 100K
2022-11-17 02:26:39 +08:00
|
|
|
BlobMetadataDomainId domainId) {
|
2022-10-27 23:44:06 +08:00
|
|
|
Standalone<BlobMetadataDetailsRef> metadata;
|
|
|
|
metadata.domainId = domainId;
|
|
|
|
// 0 == no partition, 1 == suffix partitioned, 2 == storage location partitioned
|
2023-05-24 00:24:13 +08:00
|
|
|
int type;
|
|
|
|
if (CLIENT_KNOBS->DETERMINISTIC_BLOB_METADATA) {
|
|
|
|
type = domainId % 3;
|
|
|
|
} else {
|
|
|
|
type = deterministicRandom()->randomInt(0, 3);
|
|
|
|
}
|
|
|
|
int partitionCount;
|
|
|
|
if (type == 0) {
|
|
|
|
partitionCount = 0;
|
|
|
|
} else if (CLIENT_KNOBS->DETERMINISTIC_BLOB_METADATA) {
|
|
|
|
partitionCount = 2 + domainId % 5;
|
|
|
|
} else {
|
|
|
|
partitionCount = deterministicRandom()->randomInt(2, 12);
|
|
|
|
}
|
2023-05-17 02:10:11 +08:00
|
|
|
// guarantee unique location for each domain for now
|
|
|
|
BlobMetadataLocationId locIdBase = domainId * 100;
|
2022-10-27 23:44:06 +08:00
|
|
|
TraceEvent ev(SevDebug, "SimBlobMetadata");
|
|
|
|
ev.detail("DomainId", domainId).detail("TypeNum", type).detail("PartitionCount", partitionCount);
|
|
|
|
if (type == 0) {
|
|
|
|
// single storage location
|
|
|
|
std::string partition = std::to_string(domainId) + "/";
|
2023-05-17 02:10:11 +08:00
|
|
|
metadata.locations.emplace_back_deep(metadata.arena(), locIdBase, buildPartitionPath(baseUrl, partition));
|
|
|
|
ev.detail("Location", metadata.locations.back().path);
|
2022-10-27 23:44:06 +08:00
|
|
|
}
|
|
|
|
if (type == 1) {
|
|
|
|
// simulate hash prefixing in s3
|
|
|
|
for (int i = 0; i < partitionCount; i++) {
|
2023-05-24 00:24:13 +08:00
|
|
|
std::string partitionName;
|
|
|
|
if (CLIENT_KNOBS->DETERMINISTIC_BLOB_METADATA) {
|
|
|
|
partitionName = std::to_string(i);
|
|
|
|
} else {
|
|
|
|
partitionName = deterministicRandom()->randomUniqueID().shortString();
|
|
|
|
}
|
|
|
|
std::string partition = partitionName + "-" + std::to_string(domainId) + "/";
|
2023-05-17 02:10:11 +08:00
|
|
|
metadata.locations.emplace_back_deep(
|
|
|
|
metadata.arena(), locIdBase + i, buildPartitionPath(baseUrl, partition));
|
|
|
|
ev.detail("P" + std::to_string(i), metadata.locations.back().path);
|
2022-10-27 23:44:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (type == 2) {
|
|
|
|
// simulate separate storage location per partition
|
|
|
|
for (int i = 0; i < partitionCount; i++) {
|
|
|
|
std::string partition = std::to_string(domainId) + "_" + std::to_string(i) + "/";
|
2023-05-17 02:10:11 +08:00
|
|
|
metadata.locations.emplace_back_deep(
|
|
|
|
metadata.arena(), locIdBase + i, buildPartitionPath(baseUrl, partition));
|
|
|
|
ev.detail("P" + std::to_string(i), metadata.locations.back().path);
|
2022-10-27 23:44:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// set random refresh + expire time
|
2023-05-24 00:24:13 +08:00
|
|
|
bool doExpire = CLIENT_KNOBS->DETERMINISTIC_BLOB_METADATA ? domainId % 2 : deterministicRandom()->coinflip();
|
|
|
|
if (doExpire) {
|
|
|
|
if (CLIENT_KNOBS->DETERMINISTIC_BLOB_METADATA) {
|
|
|
|
metadata.refreshAt = now() + CLIENT_KNOBS->BLOB_METADATA_REFRESH_INTERVAL;
|
|
|
|
metadata.expireAt = metadata.refreshAt + 0.2 * CLIENT_KNOBS->BLOB_METADATA_REFRESH_INTERVAL;
|
|
|
|
} else {
|
|
|
|
metadata.refreshAt =
|
|
|
|
now() + deterministicRandom()->random01() * CLIENT_KNOBS->BLOB_METADATA_REFRESH_INTERVAL;
|
|
|
|
metadata.expireAt =
|
|
|
|
metadata.refreshAt + deterministicRandom()->random01() * CLIENT_KNOBS->BLOB_METADATA_REFRESH_INTERVAL;
|
|
|
|
}
|
2022-10-27 23:44:06 +08:00
|
|
|
} else {
|
|
|
|
metadata.refreshAt = std::numeric_limits<double>::max();
|
|
|
|
metadata.expireAt = metadata.refreshAt;
|
|
|
|
}
|
|
|
|
|
|
|
|
return metadata;
|
|
|
|
}
|