diff --git a/README.md b/README.md index 46d84cb4cf..7a56b8f724 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,27 @@ We are now ready to store and retrieve data from Ambry. Let us start by storing The CUrl command creates a `POST` request that contains the binary data in demo.gif. Along with the file data, we provide headers that act as blob properties. These include the size of the blob, the service ID, the owner ID and the content type. In addition to these properties, Ambry also has a provision for arbitrary user defined metadata. We provide `x-ambry-um-description` as user metadata. Ambry does not interpret this data and it is purely for user annotation. The `Location` header in the response is the blob ID of the blob we just uploaded. + +###### Named Blob Upload +Similarly, for a named blob upload, we can provide name(key) for the blob as shown below. "named-blob-sandbox and container-a" are the Account and container names and "test-key/sample" is the blob name + + $ curl -X PUT http://localhost:1174/named/named-blob-sandbox/container-a/test-key/sample \ + -H x-li-ambry-client:Internal \ + -H x-ambry-content-type:image/gif \ + -H x-ambry-ttl:3600 \ + -H x-ambry-service-id:CurlUpload \ + --data-binary @demo.gif -i + + HTTP/1.1 201 Created + x-ambry-blob-size: 15 + Location: AmbryID + Date: Thu, 16 Jan 2025 18:13:45 GMT + Content-Length: 0 + x-ambry-creation-time: Thu, 16 Jan 2025 18:13:45 GMT + x-ambry-request-cost: WRITE_CAPACITY_UNIT=1.0; STORAGE_IN_GB=1.3969838619232178E-8 + x-ambry-datacenter: Datacenter + x-ambry-frontend: localhost + ###### GET - Blob Info Now that we stored a blob, let us verify some properties of the blob we uploaded. @@ -75,6 +96,13 @@ Now that we have verified that Ambry returns properties correctly, let us obtain $ diff demo.gif demo-downloaded.gif $ This confirms that the data that was sent in the `POST` request matches what we received in the `GET`. If you would like to see the image, simply point your browser to `http://localhost:1174/AmbryID` and you should see the image that was uploaded ! + +###### GET - Named Blob +For a named blob which allows to use name (key) for a blob, the download command is: + + $ curl -X GET http://localhost:1174/named/named-blob-sandbox/container-a/test-key/sample + This is a demo + ###### DELETE Ambry is an immutable store and blobs cannot be updated but they can be deleted in order to make them irretrievable. Let us go ahead and delete the blob we just created. diff --git a/ambry-account/src/main/java/com/github/ambry/account/InMemoryUnknownAccountService.java b/ambry-account/src/main/java/com/github/ambry/account/InMemoryUnknownAccountService.java index c870b02a47..560ca8a321 100644 --- a/ambry-account/src/main/java/com/github/ambry/account/InMemoryUnknownAccountService.java +++ b/ambry-account/src/main/java/com/github/ambry/account/InMemoryUnknownAccountService.java @@ -26,19 +26,30 @@ * account service is in memory, and does not talk to any persistent storage service. */ class InMemoryUnknownAccountService implements AccountService { + public static final short NAMED_BLOB_ACCOUNT_ID = 101; + public static final String NAMED_BLOB_ACCOUNT_NAME = "named-blob-sandbox"; static final Account UNKNOWN_ACCOUNT = new Account(Account.UNKNOWN_ACCOUNT_ID, Account.UNKNOWN_ACCOUNT_NAME, Account.AccountStatus.ACTIVE, Account.ACL_INHERITED_BY_CONTAINER_DEFAULT_VALUE, Account.SNAPSHOT_VERSION_DEFAULT_VALUE, Arrays.asList(Container.UNKNOWN_CONTAINER, Container.DEFAULT_PUBLIC_CONTAINER, Container.DEFAULT_PRIVATE_CONTAINER), Account.QUOTA_RESOURCE_TYPE_DEFAULT_VALUE); + // Create a hardcoded Account "named-blob-account" which will be used for s3 prototype tests + static final Account NAMED_BLOB_ACCOUNT = new Account(NAMED_BLOB_ACCOUNT_ID, NAMED_BLOB_ACCOUNT_NAME, Account.AccountStatus.ACTIVE, + Account.ACL_INHERITED_BY_CONTAINER_DEFAULT_VALUE, Account.SNAPSHOT_VERSION_DEFAULT_VALUE, + Collections.singletonList(Container.NAMED_BLOB_CONTAINER), Account.QUOTA_RESOURCE_TYPE_DEFAULT_VALUE); private static final Collection accounts = - Collections.unmodifiableCollection(Collections.singletonList(UNKNOWN_ACCOUNT)); + Collections.unmodifiableCollection(Arrays.asList(UNKNOWN_ACCOUNT, NAMED_BLOB_ACCOUNT)); private volatile boolean isOpen = true; @Override public Account getAccountById(short accountId) { checkOpen(); - return accountId == Account.UNKNOWN_ACCOUNT_ID ? UNKNOWN_ACCOUNT : null; + if(accountId == Account.UNKNOWN_ACCOUNT_ID){ + return UNKNOWN_ACCOUNT; + } else if(accountId == NAMED_BLOB_ACCOUNT_ID){ + return NAMED_BLOB_ACCOUNT; + } + return null; } @Override @@ -59,7 +70,11 @@ public boolean removeAccountUpdateConsumer(Consumer> account public Account getAccountByName(String accountName) { checkOpen(); Objects.requireNonNull(accountName, "accountName cannot be null."); - return UNKNOWN_ACCOUNT; + if(accountName.equals(NAMED_BLOB_ACCOUNT_NAME)){ + return NAMED_BLOB_ACCOUNT; + } else { + return UNKNOWN_ACCOUNT; + } } @Override diff --git a/ambry-api/src/main/java/com/github/ambry/account/Container.java b/ambry-api/src/main/java/com/github/ambry/account/Container.java index 3a496806a3..9947e727df 100644 --- a/ambry-api/src/main/java/com/github/ambry/account/Container.java +++ b/ambry-api/src/main/java/com/github/ambry/account/Container.java @@ -126,6 +126,12 @@ public class Container { */ public static final String DEFAULT_PRIVATE_CONTAINER_NAME = "default-private-container"; + /** + * Default name for the containers associated with S3 APIs. Since, S3 requests on client side only take Account + * (i.e. Bucket) name, we use a default name for containers. + */ + public static final String DEFAULT_S3_CONTAINER_NAME = "container-a"; + /** * The status of {@link #UNKNOWN_CONTAINER}. */ @@ -345,6 +351,18 @@ public class Container { LAST_MODIFIED_TIME_DEFAULT_VALUE, SNAPSHOT_VERSION_DEFAULT_VALUE, ACCESS_CONTROL_ALLOW_ORIGIN_DEFAULT_VALUE, CACHE_TTL_IN_SECOND_DEFAULT_VALUE, USER_METADATA_KEYS_TO_NOT_PREFIX_IN_RESPONSE_DEFAULT_VALUE); + // Create a container 'container-a' which will be used for s3 prototype tests + public static final Container NAMED_BLOB_CONTAINER = + new Container((short) 8, "container-a", UNKNOWN_CONTAINER_STATUS, UNKNOWN_CONTAINER_DESCRIPTION, + UNKNOWN_CONTAINER_ENCRYPTED_SETTING, UNKNOWN_CONTAINER_PREVIOUSLY_ENCRYPTED_SETTING, + UNKNOWN_CONTAINER_CACHEABLE_SETTING, UNKNOWN_CONTAINER_MEDIA_SCAN_DISABLED_SETTING, + DEFAULT_PRIVATE_CONTAINER_PARANOID_DURABILITY_SETTING, null, UNKNOWN_CONTAINER_TTL_REQUIRED_SETTING, + SECURE_PATH_REQUIRED_DEFAULT_VALUE, CONTENT_TYPE_WHITELIST_FOR_FILENAMES_ON_DOWNLOAD_DEFAULT_VALUE, + BACKUP_ENABLED_DEFAULT_VALUE, OVERRIDE_ACCOUNT_ACL_DEFAULT_VALUE, NamedBlobMode.OPTIONAL, (short) 101, + UNKNOWN_CONTAINER_DELETE_TRIGGER_TIME, LAST_MODIFIED_TIME_DEFAULT_VALUE, SNAPSHOT_VERSION_DEFAULT_VALUE, + ACCESS_CONTROL_ALLOW_ORIGIN_DEFAULT_VALUE, CACHE_TTL_IN_SECOND_DEFAULT_VALUE, + USER_METADATA_KEYS_TO_NOT_PREFIX_IN_RESPONSE_DEFAULT_VALUE); + // container field variables @JsonProperty(CONTAINER_ID_KEY) private final short id; diff --git a/config/frontend.properties b/config/frontend.properties index 8a9d024719..a4ee6a1dcf 100644 --- a/config/frontend.properties +++ b/config/frontend.properties @@ -14,6 +14,9 @@ rest.server.rest.request.service.factory=com.github.ambry.frontend.FrontendRestRequestServiceFactory # rest.server.account.service.factory=com.github.ambry.account.HelixAccountServiceFactory +mysql.named.blob.db.info=[{"url":"jdbc:mysql://localhost/AmbryNamedBlobs?serverTimezone=UTC","datacenter":"Datacenter","isWriteable":"true","username":"root","password":"password"}] +frontend.named.blob.db.factory=com.github.ambry.named.MySqlNamedBlobDbFactory + # router router.hostname=localhost router.datacenter.name=Datacenter