void* store_data(void* args)
{
    write_args* thread_args = (write_args*)args;
    KineticEntry* entry = &(thread_args->entry);
    int32_t objIndex;
    for (objIndex = 0; ByteBuffer_BytesRemaining(thread_args->data) > 0; objIndex++) {

        // Configure meta-data
        char keySuffix[8];
        snprintf(keySuffix, sizeof(keySuffix), "%02d", objIndex);
        entry->key.bytesUsed = strlen(thread_args->keyPrefix);
        ByteBuffer_AppendCString(&entry->key, keySuffix);
        entry->synchronization = KINETIC_SYNCHRONIZATION_WRITEBACK;

        // Prepare the next chunk of data to store
        ByteBuffer_AppendArray(&entry->value, ByteBuffer_Consume(&thread_args->data, KINETIC_OBJ_SIZE));

        // Store the data slice
        KineticStatus status = KineticClient_Put(thread_args->session, entry, NULL);
        if (status != KINETIC_STATUS_SUCCESS) {
            fprintf(stderr, "Failed writing entry %d to disk w/status: %s",
                objIndex+1, Kinetic_GetStatusDescription(status));
            return (void*)NULL;
        }
    }
    printf("File stored successfully to Kinetic device across %d entries!\n", objIndex);
    return (void*)NULL;
}
void setUp(void)
{
    SystemTestSetup(1, false);
    uint8_t version_data[DEFAULT_SIZE], tag_data[DEFAULT_SIZE];
    ByteBuffer version_buffer, tag_buffer;
    version_buffer = ByteBuffer_CreateAndAppendCString(version_data, sizeof(version_data), "v1.0");
    tag_buffer = ByteBuffer_CreateAndAppendCString(tag_data, sizeof(tag_data), "SomeTagValue");

    ByteBuffer key_buffers[TOTAL_PUT_KEYS];
    ByteBuffer value_buffers[TOTAL_PUT_KEYS];
    unsigned int i;
    for (i=0; i<TOTAL_PUT_KEYS; i++)
    {
    	key_buffers[i] = generate_entry_key_by_index(i);

    	printf("key: %s", (char *)key_buffers[i].array.data);
    	value_buffers[i] = generate_entry_value_by_index(i);

        KineticEntry putEntry = {
   	       .key = key_buffers[i],
   	       .tag = tag_buffer,
   	       .newVersion = version_buffer,
           .algorithm = KINETIC_ALGORITHM_SHA1,
           .value = value_buffers[i],
           .force = true,
           .synchronization = KINETIC_SYNCHRONIZATION_WRITETHROUGH,
        };

        KineticStatus status = KineticClient_Put(Fixture.session, &putEntry, NULL);
        TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);

        ByteBuffer_Free(key_buffers[i]);
        ByteBuffer_Free(value_buffers[i]);
    }
}

void tearDown(void)
{
    SystemTestShutDown();
}

void test_media_scan_should_succeed_for_existing_key_range(void)
{
	KineticMediaScan_Operation mediascan_operation = {"my_key         1","my_key         5", true, true};
	KineticCommand_Priority priority = PRIORITY_NORMAL;

	KineticStatus status = KineticAdminClient_MediaScan(Fixture.session, &mediascan_operation, priority);
	TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
}
void test_KineticClient_Put_should_execute_PUT_operation(void)
{
    ByteArray value = ByteArray_CreateWithCString("Four score, and seven years ago");
    KineticEntry entry = {.value = ByteBuffer_CreateWithArray(value)};
    KineticOperation operation;
    operation.session = &Session;
    
    KineticAllocator_NewOperation_ExpectAndReturn(&Session, &operation);
    KineticBuilder_BuildPut_ExpectAndReturn(&operation, &entry, KINETIC_STATUS_SUCCESS);
    KineticController_ExecuteOperation_ExpectAndReturn(&operation, NULL, KINETIC_STATUS_VERSION_MISMATCH);

    KineticStatus status = KineticClient_Put(&Session, &entry, NULL);

    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_VERSION_MISMATCH, status);
}
void* kinetic_put(void* kinetic_arg)
{
    struct kinetic_thread_arg* arg = kinetic_arg;
    KineticEntry* entry = &(arg->entry);
    int32_t objIndex = 0;

    while (ByteBuffer_BytesRemaining(arg->data) > 0) {

        // Configure meta-data
        char keySuffix[8];
        snprintf(keySuffix, sizeof(keySuffix), "_%02d", objIndex);
        entry->key.bytesUsed = arg->keyPrefixLength;
        ByteBuffer_AppendCString(&entry->key, keySuffix);

        // Move dbVersion back to newVersion, since successful PUTs do this
        // in order to sync with the actual entry on disk
        if (entry->newVersion.array.data == NULL) {
            entry->newVersion = entry->dbVersion;
            entry->dbVersion = BYTE_BUFFER_NONE;
        }

        // Prepare the next chunk of data to store
        ByteBuffer_AppendArray(
            &entry->value,
            ByteBuffer_Consume(
                &arg->data,
                MIN(ByteBuffer_BytesRemaining(arg->data), KINETIC_OBJ_SIZE)
            )
        );

        // Store the data slice
        LOGF("Storing a data slice (%u bytes)", entry->value.bytesUsed);
        KineticStatus status = KineticClient_Put(arg->sessionHandle, entry);
        TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
        LOGF("KineticClient put to disk success, ip:%s", arg->ip);

        objIndex++;
    }

    return (void*)0;
}
void store_data(write_args* args)
{
    KineticEntry* entry = &(args->entry);
    int32_t objIndex = 0;

    while (ByteBuffer_BytesRemaining(args->data) > 0) {

        // Configure entry meta-data
        ByteBuffer_Reset(&entry->key);
        ByteBuffer_AppendCString(&entry->key, args->keyPrefix);
        char keySuffix[8];
        snprintf(keySuffix, sizeof(keySuffix), "%02d", objIndex);
        ByteBuffer_AppendCString(&entry->key, keySuffix);

        // Prepare entry with the next object to store
        ByteBuffer_Reset(&entry->value);
        ByteBuffer_AppendArray(
            &entry->value,
            ByteBuffer_Consume(
                &args->data,
                MIN(ByteBuffer_BytesRemaining(args->data), KINETIC_OBJ_SIZE))
        );

        // Store the object
        KineticStatus status = KineticClient_Put(args->session, entry, NULL);
        if (status != KINETIC_STATUS_SUCCESS) {
            fprintf(stderr, "Kinetic PUT of object %d to host %s failed w/ status: %s\n",
                objIndex, args->ip, Kinetic_GetStatusDescription(status));
            exit(-1);
        }

        objIndex++;
    }

    printf("File stored on Kinetic Device across %d entries\n", objIndex);
}
void test_Flush_should_flush_pending_PUTs_and_DELETEs(void)
{
    // Arguments shared between entries
    uint8_t TagData[1024];
    ByteBuffer tagBuffer = ByteBuffer_CreateAndAppendCString(TagData, sizeof(TagData), "tag_val");
    uint8_t key1[10];
    ByteBuffer keyBuffer1 = ByteBuffer_CreateAndAppendCString(key1, sizeof(key1), "key1");
    uint8_t value1[10];
    ByteBuffer valueBuffer1 = ByteBuffer_CreateAndAppendCString(value1, sizeof(value1), "value1");
    uint8_t key2[10];
    ByteBuffer keyBuffer2 = ByteBuffer_CreateAndAppendCString(key2, sizeof(key2), "key2");
    uint8_t value2[10];
    ByteBuffer valueBuffer2 = ByteBuffer_CreateAndAppendCString(value2, sizeof(value2), "value2");

    // Do a blocking PUT ("key1" => "value1") so we can delete it later
    KineticEntry Entry = (KineticEntry) {
        .key = keyBuffer1,
        .tag = tagBuffer,
        .algorithm = KINETIC_ALGORITHM_SHA1,
        .value = valueBuffer1,
        .synchronization = KINETIC_SYNCHRONIZATION_WRITEBACK,
        .force = true,
    };
    KineticStatus status = KineticClient_Put(Fixture.session, &Entry, NULL);
    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);

    Entry = (KineticEntry) {
        .key = keyBuffer2,
        .tag = tagBuffer,
        .algorithm = KINETIC_ALGORITHM_SHA1,
        .value = valueBuffer2,
        .synchronization = KINETIC_SYNCHRONIZATION_WRITEBACK,
        .force = true,
    };

    status = KineticClient_Put(Fixture.session, &Entry, NULL);
    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);

    // Do an async DELETE so we can flush to complete it
    KineticEntry deleteEntry = {
        .key = keyBuffer1,
        .synchronization = KINETIC_SYNCHRONIZATION_WRITETHROUGH,
    };
    KineticCompletionClosure no_op_closure = {
        .callback = &no_op_callback,
    };
    status = KineticClient_Delete(Fixture.session, &deleteEntry, &no_op_closure);

    /* Now do a blocking flush and confirm that (key1,value1) has been
     * DELETEd and (key2,value2) have been PUT. */
    status = KineticClient_Flush(Fixture.session, NULL);
    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);

    // GET key1 --> expect NOT FOUND
    KineticEntry getEntry1 = {
        .key = keyBuffer1,
        .tag = tagBuffer,
        .value = valueBuffer1,
    };
    status = KineticClient_Get(Fixture.session, &getEntry1, NULL);
    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_NOT_FOUND, status);

    // GET key2 --> present
    KineticEntry getEntry2 = {
        .key = keyBuffer2,
        .tag = tagBuffer,
        .value = valueBuffer2,
    };
    status = KineticClient_Get(Fixture.session, &getEntry2, NULL);
    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
}
void setUp(void)
{
    SystemTestSetup(0, true);

    KeyBuffer = ByteBuffer_CreateAndAppendCString(KeyData, sizeof(KeyData), strKey);
    ExpectedKeyBuffer = ByteBuffer_CreateAndAppendCString(ExpectedKeyData, sizeof(ExpectedKeyData), strKey);
    TagBuffer = ByteBuffer_CreateAndAppendCString(TagData, sizeof(TagData), "SomeTagValue");
    ExpectedTagBuffer = ByteBuffer_CreateAndAppendCString(ExpectedTagData, sizeof(ExpectedTagData), "SomeTagValue");
    TestValue = ByteArray_CreateWithCString("lorem ipsum... blah blah blah... etc.");
    ValueBuffer = ByteBuffer_CreateAndAppendArray(ValueData, sizeof(ValueData), TestValue);

    // Setup to write some test data
    KineticEntry putEntry = {
        .key = KeyBuffer,
        .tag = TagBuffer,
        .algorithm = KINETIC_ALGORITHM_SHA1,
        .value = ValueBuffer,
        .force = true,
        .synchronization = KINETIC_SYNCHRONIZATION_FLUSH,
    };

    KineticStatus status = KineticClient_Put(Fixture.session, &putEntry, NULL);
    if (status != KINETIC_STATUS_SUCCESS) {
        failing = true;
        TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
    }

    // Validate the object exists initially
    KineticEntry getEntry = {
        .key = KeyBuffer,
        .tag = TagBuffer,
        .algorithm = KINETIC_ALGORITHM_SHA1,
        .value = ValueBuffer,
        .force = true,
        .synchronization = KINETIC_SYNCHRONIZATION_WRITETHROUGH,
    };
    status = KineticClient_Get(Fixture.session, &getEntry, NULL);
    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
    TEST_ASSERT_EQUAL_ByteArray(putEntry.key.array, getEntry.key.array);
    TEST_ASSERT_EQUAL_ByteArray(putEntry.tag.array, getEntry.tag.array);
    TEST_ASSERT_EQUAL(putEntry.algorithm, getEntry.algorithm);
    TEST_ASSERT_EQUAL_ByteBuffer(putEntry.value, getEntry.value);

    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);

    // Set the erase PIN to something non-empty
    strcpy(NewPinData, SESSION_PIN);
    OldPin = ByteArray_Create(OldPinData, 0);
    NewPin = ByteArray_Create(NewPinData, strlen(NewPinData));
    status = KineticAdminClient_SetErasePin(Fixture.adminSession,
        OldPin, NewPin);
    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
}

void tearDown(void)
{
    KineticStatus status = KINETIC_STATUS_INVALID;
    
    if (failing) { return; }

    // Validate the object no longer exists
    KineticEntry regetEntryMetadata = {
        .key = KeyBuffer,
        .tag = TagBuffer,
        .metadataOnly = true,
    };
    status = KineticClient_Get(Fixture.session, &regetEntryMetadata, NULL);
    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_NOT_FOUND, status);
    TEST_ASSERT_ByteArray_EMPTY(regetEntryMetadata.value.array);

    SystemTestShutDown();
}

void test_SecureErase_should_erase_device_contents(void)
{
    KineticStatus status = KineticAdminClient_SecureErase(Fixture.adminSession, NewPin);
    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
}

void test_InstantErase_should_erase_device_contents(void)
{
    KineticStatus status = KineticAdminClient_InstantErase(Fixture.adminSession, NewPin);
    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
}
void setUp(void)
{
    NewPinSet = false;
    Locked = false;

    SystemTestSetup(1, true);

    KeyBuffer = ByteBuffer_CreateAndAppendCString(KeyData, sizeof(KeyData), strKey);
    ExpectedKeyBuffer = ByteBuffer_CreateAndAppendCString(ExpectedKeyData, sizeof(ExpectedKeyData), strKey);
    TagBuffer = ByteBuffer_CreateAndAppendCString(TagData, sizeof(TagData), "SomeTagValue");
    ExpectedTagBuffer = ByteBuffer_CreateAndAppendCString(ExpectedTagData, sizeof(ExpectedTagData), "SomeTagValue");
    TestValue = ByteArray_CreateWithCString("lorem ipsum... blah blah blah... etc.");
    ValueBuffer = ByteBuffer_CreateAndAppendArray(ValueData, sizeof(ValueData), TestValue);

    // Setup to write some test data
    KineticEntry putEntry = {
        .key = KeyBuffer,
        .tag = TagBuffer,
        .algorithm = KINETIC_ALGORITHM_SHA1,
        .value = ValueBuffer,
        .force = true,
        .synchronization = KINETIC_SYNCHRONIZATION_FLUSH,
    };

    KineticStatus status = KineticClient_Put(Fixture.session, &putEntry, NULL);

    // Validate the object exists initially
    KineticEntry getEntry = {
        .key = KeyBuffer,
        .tag = TagBuffer,
        .algorithm = KINETIC_ALGORITHM_SHA1,
        .value = ValueBuffer,
        .force = true,
        .synchronization = KINETIC_SYNCHRONIZATION_WRITETHROUGH,
    };
    status = KineticClient_Get(Fixture.session, &getEntry, NULL);
    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
    TEST_ASSERT_EQUAL_ByteArray(putEntry.key.array, getEntry.key.array);
    TEST_ASSERT_EQUAL_ByteArray(putEntry.tag.array, getEntry.tag.array);
    TEST_ASSERT_EQUAL(putEntry.algorithm, getEntry.algorithm);
    TEST_ASSERT_EQUAL_ByteBuffer(putEntry.value, getEntry.value);

    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);

    // Set the erase PIN to something non-empty
    strcpy(NewPinData, SESSION_PIN);
    OldPin = ByteArray_Create(OldPinData, 0);
    NewPin = ByteArray_Create(NewPinData, strlen(NewPinData));
    status = KineticAdminClient_SetLockPin(Fixture.adminSession,
        OldPin, NewPin);
    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
    NewPinSet = true;
}

void tearDown(void)
{
    KineticStatus status;

    // Unlock if for some reason we are still locked in order to
    // prevent the device from staying in a locked/unusable state
    if (Locked) {
        status = KineticAdminClient_UnlockDevice(Fixture.adminSession, NewPin);
        TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
        Locked = false;
    }

    // Set the lock PIN back to empty
    if (NewPinSet) {
        status = KineticAdminClient_SetLockPin(Fixture.adminSession, NewPin, OldPin);
        TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
        NewPinSet = false;
    }

    SystemTestShutDown();
}

void test_KineticAdmin_should_lock_and_unlock_a_device(void)
{
    KineticStatus status;

    status = KineticAdminClient_LockDevice(Fixture.adminSession, NewPin);
    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
    Locked = true;

    /* Currently, the device appears to just hang up on us rather than
     * returning DEVICE_LOCKED (unlike the simulator). Some sort of
     * command here to confirm that the device lock succeeded would
     * be a better test. We need to check if the drive has another
     * interface that exposes this. */
    if (SystemTestIsUnderSimulator()) {
        // Validate the object cannot being accessed while locked
        KineticEntry getEntry = {
            .key = KeyBuffer,
            .tag = TagBuffer,
            .value = ValueBuffer,
            .force = true,
        };
        status = KineticClient_Get(Fixture.session, &getEntry, NULL);
        TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_DEVICE_LOCKED, status);
    }
    
    status = KineticAdminClient_UnlockDevice(Fixture.adminSession, NewPin);
    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
    Locked = false;
}
    status = KineticAdminClient_CreateSession(&peerAdminConfig, client, &peerAdminSession);
    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);

    // Create some test entries to have content to copy
    TagBuffer     = ByteBuffer_Create(dummyTagData, sizeof(dummyTagData), sizeof(dummyTagData));
    Key1Buffer    = ByteBuffer_Create(key1Data, sizeof(key1Data), sizeof(key1Data));
    Value1Buffer  = ByteBuffer_Create(value1Data, sizeof(value1Data), sizeof(value1Data));
    KineticEntry putEntry1 = {
        .key = Key1Buffer,
        .tag = TagBuffer,
        .algorithm = KINETIC_ALGORITHM_SHA1,
        .value = Value1Buffer,
        .force = true,
        .synchronization = KINETIC_SYNCHRONIZATION_WRITETHROUGH,
    };
    status = KineticClient_Put(session, &putEntry1, NULL);
    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
    Key2Buffer    = ByteBuffer_Create(key2Data, sizeof(key2Data), sizeof(key2Data));
    Value2Buffer  = ByteBuffer_Create(value2Data, sizeof(value2Data), sizeof(value2Data));
    KineticEntry putEntry2 = {
        .key = Key2Buffer,
        .tag = TagBuffer,
        .algorithm = KINETIC_ALGORITHM_SHA1,
        .value = Value2Buffer,
        .force = true,
        .synchronization = KINETIC_SYNCHRONIZATION_WRITETHROUGH,
    };
    status = KineticClient_Put(session, &putEntry2, NULL);
    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);

    ReadValueBuffer = ByteBuffer_Create(readValueData, sizeof(readValueData), 0);
static void do_put_and_getkeyrange(KineticSession * const session) {
    for (int i = 0; i < 5; i++) {
        char key[] = "keyX";
        key[3] = '0' + i;
        ByteBuffer put_key_buf = ByteBuffer_MallocAndAppend(key, strlen(key));
        
        uint8_t value[] = "valueX";
        value[5] = '0' + i;
        ByteBuffer put_value_buf = ByteBuffer_MallocAndAppend(value, sizeof(value));
        
        /* Populate tag with SHA1 of value */
        ByteBuffer put_tag_buf = ByteBuffer_Malloc(20);
        uint8_t sha1[20];
        SHA1(put_value_buf.array.data, put_value_buf.bytesUsed, &sha1[0]);
        ByteBuffer_Append(&put_tag_buf, sha1, sizeof(sha1));
        
        KineticEntry put_entry = {
            .key = put_key_buf,
            .value = put_value_buf,
            .tag = put_tag_buf,
            .algorithm = KINETIC_ALGORITHM_SHA1,
            /* Set sync to WRITETHROUGH, which will wait to complete
             * until the drive has persistend the write. (WRITEBACK
             * returns as soon as the drive has buffered the write.) */
            .synchronization = KINETIC_SYNCHRONIZATION_WRITETHROUGH,
        };

        /* Put "keyX" => "valueX", where 'X' is 0..4.
         * This will block, because the callback field (arg 3) is NULL. */
        KineticStatus status = KineticClient_Put(session, &put_entry, NULL);
        printf("Put status: %s\n", Kinetic_GetStatusDescription(status));

        ByteBuffer_Free(put_key_buf);
        ByteBuffer_Free(put_value_buf);
        ByteBuffer_Free(put_tag_buf);
    }

    const size_t max_key_count = 5;
    const size_t max_key_length = 64;
    uint8_t first_key[max_key_length];
    uint8_t last_key[max_key_length];

    KineticKeyRange range = {
        .startKey = ByteBuffer_CreateAndAppendCString(first_key, sizeof(first_key), "key"),
        .endKey = ByteBuffer_CreateAndAppendCString(last_key, sizeof(last_key), "key\xFF"),
        .startKeyInclusive = true,
        .endKeyInclusive = true,
        .maxReturned = max_key_count,
    };
    
    uint8_t key_mem[max_key_count][max_key_length];
    memset(key_mem, 0, sizeof(key_mem));

    ByteBuffer key_buffers[max_key_count];
    for (size_t i = 0; i < max_key_count; i++) {
        key_buffers[i] = ByteBuffer_Create(&key_buffers[i], max_key_length, 0);
    }
    ByteBufferArray keys = {
        .buffers = key_buffers,
        .count = max_key_count,
    };

    /* Request the key range as specified in &range, populating the keys in &keys. */
    KineticStatus status = KineticClient_GetKeyRange(session, &range, &keys, NULL);
    printf("GetKeyRange status: %s\n", Kinetic_GetStatusDescription(status));
    
    if (status == KINETIC_STATUS_SUCCESS) {
        for (size_t i = 0; i < max_key_count; i++) {
            printf("%zd: %s\n", i, key_buffers[i].array.data);
        }
    }

    /* No cleanup necessary */
}

int main(int argc, char** argv)
{
    (void)argc;
    (void)argv;

    // Initialize kinetic-c and configure sessions
    KineticSession* session;
    KineticClientConfig clientConfig = {
        .logFile = "stdout",
        .logLevel = 1,
    };
    KineticClient * client = KineticClient_Init(&clientConfig);
    if (client == NULL) { return 1; }
    const char HmacKeyString[] = "asdfasdf";
    KineticSessionConfig sessionConfig = {
        .host = "localhost",
        .port = KINETIC_PORT,
        .clusterVersion = 0,
        .identity = 1,
        .hmacKey = ByteArray_CreateWithCString(HmacKeyString),
    };
    KineticStatus status = KineticClient_CreateSession(&sessionConfig, client, &session);
    if (status != KINETIC_STATUS_SUCCESS) {
        fprintf(stderr, "Failed connecting to the Kinetic device w/status: %s\n",
            Kinetic_GetStatusDescription(status));
        exit(1);
    }

    do_put_and_getkeyrange(session);
    
    // Shutdown client connection and cleanup
    KineticClient_DestroySession(session);
    KineticClient_Shutdown(client);
    return 0;
}
Exemple #11
0
KineticStatus ExecuteOperation(
    struct UtilConfig * cfg)
{
    KineticStatus status = KINETIC_STATUS_INVALID;
    KineticLogInfo * logInfo;
    ByteArray tmpArray;

    switch (cfg->opID) {

        case OPT_NOOP:
            status = KineticClient_NoOp(cfg->session);
            if (status == KINETIC_STATUS_SUCCESS) {
                printf("NoOp operation completed successfully."
                    " Kinetic Device is alive and well!\n"); }
            break;

        case OPT_PUT:
            status = KineticClient_Put(cfg->session, &cfg->entry, NULL);
            if (status == KINETIC_STATUS_SUCCESS) {
                printf("Put operation completed successfully."
                       " Your data has been stored!\n");
                PrintEntry(&cfg->entry);
            }
            break;

        case OPT_GET:
            status = KineticClient_Get(cfg->session, &cfg->entry, NULL);
            if (status == KINETIC_STATUS_SUCCESS) {
                printf("Get executed successfully.\n");
                PrintEntry(&cfg->entry);
            }
            break;

        case OPT_GETNEXT:
            status = KineticClient_GetNext(cfg->session, &cfg->entry, NULL);
            if (status == KINETIC_STATUS_SUCCESS) {
                printf("GetNext executed successfully.\n");
                PrintEntry(&cfg->entry);
            }
            break;

        case OPT_GETPREVIOUS:
            status = KineticClient_GetPrevious(cfg->session, &cfg->entry, NULL);
            if (status == KINETIC_STATUS_SUCCESS) {
                printf("GetPrevious executed successfully.\n");
                PrintEntry(&cfg->entry);
            }
            break;

        case OPT_DELETE:
            status = KineticClient_Delete(cfg->session, &cfg->entry, NULL);
            if (status == KINETIC_STATUS_SUCCESS) {
                printf("Delete executed successfully. The entry has been destroyed!\n");
                PrintEntry(&cfg->entry);
            }
            break;

        case OPT_GETLOG:
            status = KineticAdminClient_GetLog(cfg->adminSession, cfg->logType, &logInfo, NULL);
            if (status == KINETIC_STATUS_SUCCESS) {
                printf("GetLog executed successfully.\n"
                       "The device log info has been retrieved!\n");
                PrintLogInfo(cfg->logType, logInfo);
            }
            break;

        case OPT_GETDEVICESPECIFICLOG:
            if (strlen(cfg->deviceLogName) == 0) {
                fprintf(stderr, "Device-specific log type requires '--devicelogname' to be set!\n");
                exit(1);
            }
            tmpArray.data = (uint8_t*)cfg->deviceLogName;
            tmpArray.len = strlen(cfg->deviceLogName);
            status = KineticAdminClient_GetDeviceSpecificLog(cfg->adminSession, tmpArray, &logInfo, NULL);
            if (status == KINETIC_STATUS_SUCCESS) {
                printf("GetDeviceSpecificLog executed successfully."
                       "The device-specific device log info has been retrieved!\n");
                printf("Device-Specific Log Info:\n");
                char* dev = calloc(1, logInfo->device->name.len + 1);
                memcpy(dev, logInfo->device->name.data, logInfo->device->name.len);
                printf("  %s\n", dev);
                free(dev);
            }
            else if (status == KINETIC_STATUS_NOT_FOUND) {
                fprintf(stderr, "The specified device-specific log '%s' was not found on the device!\n", cfg->deviceLogName);
                status = KINETIC_STATUS_SUCCESS;
            }
            break;

        case OPT_SETERASEPIN:
            status = KineticAdminClient_SetErasePin(cfg->adminSession,
                ByteArray_Create(cfg->pin, strlen(cfg->pin)),
                ByteArray_Create(cfg->newPin, strlen(cfg->newPin)));
            if (status == KINETIC_STATUS_SUCCESS) {
                printf("SetErasePin executed successfully.\n"
                       "The kinetic device erase pin has been changed!\n"); }
            break;

        case OPT_INSTANTERASE:
            status = KineticAdminClient_InstantErase(cfg->adminSession,
                ByteArray_Create(cfg->pin, strlen(cfg->pin)));
            if (status == KINETIC_STATUS_SUCCESS) {
                printf("InstantErase executed successfully.\n"
                       "The kinetic device has been erased!\n"); }
            break;

        case OPT_SECUREERASE:
            status = KineticAdminClient_SecureErase(cfg->adminSession,
                ByteArray_Create(cfg->pin, strlen(cfg->pin)));
            if (status == KINETIC_STATUS_SUCCESS) {
                printf("SecureErase executed successfully.\n"
                       "The kinetic device has been erased!\n"); }
            break;

        case OPT_SETLOCKPIN:
            status = KineticAdminClient_SetLockPin(cfg->adminSession,
                ByteArray_Create(cfg->pin, strlen(cfg->pin)),
                ByteArray_Create(cfg->newPin, strlen(cfg->newPin)));
            if (status == KINETIC_STATUS_SUCCESS) {
                printf("SetLockPin executed successfully.\n"
                       "The kinetic device lock/unlock pin has been changed!\n"); }
            break;

        case OPT_LOCKDEVICE:
            status = KineticAdminClient_LockDevice(cfg->adminSession,
                ByteArray_Create(cfg->pin, strlen(cfg->pin)));
            if (status == KINETIC_STATUS_SUCCESS) {
                printf("LockDevice executed successfully.\n"
                       "The kinetic device is now locked!\n"); }
            break;

        case OPT_UNLOCKDEVICE:
            status = KineticAdminClient_UnlockDevice(cfg->adminSession,
                ByteArray_Create(cfg->pin, strlen(cfg->pin)));
            if (status == KINETIC_STATUS_SUCCESS) {
                printf("UnlockDevice executed successfully.\n"
                       "The kinetic device is now unlocked!\n"); }
            break;

        case OPT_SETCLUSTERVERSION:
            status = KineticAdminClient_SetClusterVersion(cfg->adminSession, cfg->newClusterVersion);
            if (status == KINETIC_STATUS_SUCCESS) {
                printf("SetClusterVersion executed successfully.\n"
                       "The kinetic device's cluster version has been updated!\n"); }
            break;

        case OPT_SETACL:
            status = KineticAdminClient_SetACL(cfg->adminSession, cfg->file);
            if (status == KINETIC_STATUS_SUCCESS) {
                printf("SetACL executed successfully.\n"
                       "The kinetic device ACLs have been replaced!\n"); }
            break;

        case OPT_UPDATEFIRMWARE:
            status = KineticAdminClient_UpdateFirmware(cfg->session, cfg->file);
            if (status == KINETIC_STATUS_SUCCESS) {
                printf("SecureErase executed successfully.\n"
                       "The kinetic device has been restored to empty status!\n"); }
            break;

        default:
            fprintf(stderr, "Specified operation '%d' is invalid!\n",
                (int)cfg->opID);
            exit(-1);
    };

    // Print out status code description if operation was not successful
    if (status != KINETIC_STATUS_SUCCESS) {
        fprintf(stderr, "\nERROR: Operation '%s' failed with status '%s'\n",
            GetOptString(cfg->opID), Kinetic_GetStatusDescription(status));
    }

    return status;
}
static bool add_keys(int count)
{
    static const ssize_t sz = 10;
    char key_buf[sz];
    char tag_buf[sz];
    char value_buf[sz];

    for (int i = 0; i < count; i++) {

        KineticEntry entry = {
            .key = ByteBuffer_CreateAndAppendFormattedCString(key_buf, sz, "key_%d", i),
            .tag = ByteBuffer_CreateAndAppendFormattedCString(tag_buf, sz, "tag_%d", i),
            .value = ByteBuffer_CreateAndAppendFormattedCString(value_buf, sz, "val_%d", i),
            .algorithm = KINETIC_ALGORITHM_SHA1,
            .force = true,
        };

        KineticStatus status = KineticClient_Put(Fixture.session, &entry, NULL);
        TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
    }
    return true;
}

typedef enum { CMD_NEXT, CMD_PREVIOUS } GET_CMD;

static void compare_against_offset_key(GET_CMD cmd, bool metadataOnly)
{
    static const ssize_t sz = 10;
    char key_buf[sz];
    char tag_buf[sz];
    char value_buf[sz];
    char key_exp_buf[sz];
    char value_exp_buf[sz];

    const int count = 3;

    TEST_ASSERT_TRUE(add_keys(count));    /* add keys [key_X -> test_X] */

    int low = 0;
    int high = count;
    int8_t offset = 0;

    switch (cmd) {
    case CMD_NEXT:
        low = 1;
        offset = -1;
        break;
    case CMD_PREVIOUS:
        high = count - 1;
        offset = +1;
        break;
    default:
        TEST_ASSERT(false);
    }

    for (int i = low; i < high; i++) {
        ByteBuffer keyBuffer = ByteBuffer_CreateAndAppendFormattedCString(key_buf, sz, "key_%d", i + offset);
        ByteBuffer tagBuffer = ByteBuffer_CreateAndAppendFormattedCString(tag_buf, sz, "tag_%d", i + offset);
        ByteBuffer valueBuffer = ByteBuffer_Create(value_buf, sz, 0);

        printf("KEY '%s'\n", key_buf);

        KineticEntry entry = {
            .key = keyBuffer,
            .tag = tagBuffer,
            .value = valueBuffer,
            .algorithm = KINETIC_ALGORITHM_SHA1,
            .metadataOnly = metadataOnly,
        };
        KineticStatus status = KINETIC_STATUS_INVALID;

        switch (cmd) {
        case CMD_NEXT:
            status = KineticClient_GetNext(Fixture.session, &entry, NULL);
            break;
        case CMD_PREVIOUS:
            status = KineticClient_GetPrevious(Fixture.session, &entry, NULL);
            break;
        default:
            TEST_ASSERT(false);
        }
        
        TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);

        ByteBuffer expectedKeyBuffer = ByteBuffer_CreateAndAppendFormattedCString(key_exp_buf, sz, "key_%d", i);
        ByteBuffer expectedValueBuffer = ByteBuffer_CreateAndAppendFormattedCString(value_exp_buf, sz, "val_%d", i);

        TEST_ASSERT_EQUAL_ByteBuffer(expectedKeyBuffer, entry.key);
        if (!metadataOnly) {
            TEST_ASSERT_EQUAL_ByteBuffer(expectedValueBuffer, entry.value);
        }
    }
}

void test_GetNext_should_retrieve_object_and_metadata_from_device(void)
{
    compare_against_offset_key(CMD_NEXT, false);
}
int main(int argc, char** argv)
{
    (void)argc;
    (void)argv;

    // Initialize kinetic-c and configure sessions
    KineticSession* session;
    KineticClientConfig clientConfig = {
        .logFile = "stdout",
        .logLevel = 1,
    };
    KineticClient * client = KineticClient_Init(&clientConfig);
    if (client == NULL) { return 1; }
    const char HmacKeyString[] = "asdfasdf";
    KineticSessionConfig sessionConfig = {
        .host = "localhost",
        .port = KINETIC_PORT,
        .clusterVersion = 0,
        .identity = 1,
        .hmacKey = ByteArray_CreateWithCString(HmacKeyString),
    };
    KineticStatus status = KineticClient_CreateSession(&sessionConfig, client, &session);
    if (status != KINETIC_STATUS_SUCCESS) {
        fprintf(stderr, "Failed connecting to the Kinetic device w/status: %s\n",
            Kinetic_GetStatusDescription(status));
        exit(1);
    }

    // some dummy data to PUT
    uint8_t value_data[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
    ByteBuffer value = ByteBuffer_MallocAndAppend(value_data, sizeof(value_data));

    // a dummy key
    uint8_t key_data[] = {0x00, 0x01, 0x02, 0x03, 0x04};
    ByteBuffer key = ByteBuffer_MallocAndAppend(key_data, sizeof(key_data));

    // Populate tag with SHA1
    ByteBuffer tag = ByteBuffer_Malloc(20);
    uint8_t sha1[20];
    SHA1(value.array.data, value.bytesUsed, &sha1[0]);
    ByteBuffer_Append(&tag, sha1, sizeof(sha1));

    KineticEntry entry = {
        .key = key,
        .tag = tag,
        .algorithm = KINETIC_ALGORITHM_SHA1,
        .value = value,
        .synchronization = KINETIC_SYNCHRONIZATION_WRITETHROUGH,
    };

    // Do a blocking put to make sure there is something there to read back
    KineticStatus put_status = KineticClient_Put(session, &entry, NULL);

    if (put_status != KINETIC_STATUS_SUCCESS) {
        fprintf(stderr, "Put failed w/status: %s\n", Kinetic_GetStatusDescription(put_status));
        return 1;
    }

    // Create structure to populate with GET status in callback
    //   a semaphore is used to notify the main thread that it's
    //   safe to proceed.
    GetStatus get_status = {
        .sem = KineticSemaphore_Create(),
        .status = KINETIC_STATUS_INVALID,
    };

    ByteBuffer getTag = ByteBuffer_Malloc(tag.bytesUsed);
    ByteBuffer getValue = ByteBuffer_Malloc(value.bytesUsed);

    // Because I'm passing a pointer to this entry to KineticClient_Put(), this entry must not
    //   go out of scope until the GET completes
    KineticEntry get_entry = {
        .key = key,
        .tag = getTag,
        .algorithm = KINETIC_ALGORITHM_SHA1,
        .value = getValue,
        .force = true,
    };

    status = KineticClient_Get(
        session,
        &get_entry,
        &(KineticCompletionClosure) {
            .callback = get_finished,
            .clientData = &get_status,
        }
    );
    if (status != KINETIC_STATUS_SUCCESS) {
        fprintf(stderr, "Get failed w/status: %s\n", Kinetic_GetStatusDescription(status));
        return 1;
    }

    // Wait for GET to finish
    KineticSemaphore_WaitForSignalAndDestroy(get_status.sem);

    if (get_status.status != KINETIC_STATUS_SUCCESS) {
        fprintf(stderr, "GET failed w/status: %s\n", Kinetic_GetStatusDescription(get_status.status));
        return 1;
    }

    if ((value.bytesUsed == getValue.bytesUsed) &&
        (memcmp(value.array.data, getValue.array.data, getValue.bytesUsed) != 0)) {
        fprintf(stderr, "GET completed but returned unexpected value");
        return 1;
    }
    printf("GET completed successfully!\n");

    // Free malloc'd buffers
    ByteBuffer_Free(value);
    ByteBuffer_Free(key);
    ByteBuffer_Free(tag);

    ByteBuffer_Free(getValue);
    ByteBuffer_Free(getTag);
    

    // Shutdown client connection and cleanup
    KineticClient_DestroySession(session);
    KineticClient_Shutdown(client);

    return 0;
}

static void get_finished(KineticCompletionData* kinetic_data, void* clientData)
{
    GetStatus * get_status = clientData;

    // Save GET result status
    get_status->status = kinetic_data->status;
    // Signal that we're done
    KineticSemaphore_Signal(get_status->sem);
}
static void do_put_and_get(KineticSession *session) {
    const char key[] = "key";
    ByteBuffer put_key_buf = ByteBuffer_MallocAndAppend(key, strlen(key));

    const uint8_t value[] = "value\x01\x02\x03\x04";
    ByteBuffer put_value_buf = ByteBuffer_MallocAndAppend(value, sizeof(value));
    
    /* Populate tag with SHA1 of value */
    ByteBuffer put_tag_buf = ByteBuffer_Malloc(20);
    uint8_t sha1[20];
    SHA1(put_value_buf.array.data, put_value_buf.bytesUsed, &sha1[0]);
    ByteBuffer_Append(&put_tag_buf, sha1, sizeof(sha1));

    KineticEntry put_entry = {
        .key = put_key_buf,
        .value = put_value_buf,
        .tag = put_tag_buf,
        .algorithm = KINETIC_ALGORITHM_SHA1,
        /* Set sync to WRITETHROUGH, which will wait to complete
         * until the drive has persistend the write. (WRITEBACK
         * returns as soon as the drive has buffered the write.) */
        .synchronization = KINETIC_SYNCHRONIZATION_WRITETHROUGH,
    };

    /* Put "key" => "value\x01\x02\x03\x04".
     * This will block, because the callback field (arg 3) is NULL. */
    KineticStatus status = KineticClient_Put(session, &put_entry, NULL);
    printf("Put status: %s\n", Kinetic_GetStatusDescription(status));

    /* Allocate a tag large enough for the SHA1. */
    ByteBuffer get_tag_buf = ByteBuffer_Malloc(put_tag_buf.bytesUsed);
    /* Allocate a buffer large enough for the value. */
    ByteBuffer get_value_buf = ByteBuffer_Malloc(put_value_buf.bytesUsed);

    KineticEntry get_entry = {
        .key = put_key_buf,
        .value = get_value_buf,
        .tag = get_tag_buf,
        .algorithm = KINETIC_ALGORITHM_SHA1,
    };

    status = KineticClient_Get(session, &get_entry, NULL);
    printf("Get status: %s\n", Kinetic_GetStatusDescription(status));
    printf("Get value: %zd bytes\n    ", get_entry.value.bytesUsed);
    for (size_t i = 0; i < get_entry.value.bytesUsed; i++) {
        printf("%02x ", get_entry.value.array.data[i]);
    }
    printf("    ");
    for (size_t i = 0; i < get_entry.value.bytesUsed; i++) {
        char c = get_entry.value.array.data[i];
        printf("%c", isprint(c) ? c : '.');
    }
    printf("\n");

    /* Cleanup */
    ByteBuffer_Free(put_key_buf);
    ByteBuffer_Free(put_value_buf);
    ByteBuffer_Free(put_tag_buf);
    ByteBuffer_Free(get_entry.value);
    ByteBuffer_Free(get_tag_buf);
}
static void do_put_and_getprevious_getnext(KineticSession *session) {
    for (int i = 0; i < 3; i++) {
        char key[] = "keyX";
        key[3] = '0' + i;
        ByteBuffer put_key_buf = ByteBuffer_MallocAndAppend(key, strlen(key));

        uint8_t value[] = "valueX";
        value[5] = '0' + i;
        ByteBuffer put_value_buf = ByteBuffer_MallocAndAppend(value, sizeof(value));

        /* Populate tag with SHA1 of value */
        ByteBuffer put_tag_buf = ByteBuffer_Malloc(20);
        uint8_t sha1[20];
        SHA1(put_value_buf.array.data, put_value_buf.bytesUsed, &sha1[0]);
        ByteBuffer_Append(&put_tag_buf, sha1, sizeof(sha1));

        KineticEntry put_entry = {
            .key = put_key_buf,
            .value = put_value_buf,
            .tag = put_tag_buf,
            .algorithm = KINETIC_ALGORITHM_SHA1,
            /* Set sync to WRITETHROUGH, which will wait to complete
             * until the drive has persistend the write. (WRITEBACK
             * returns as soon as the drive has buffered the write.) */
            .synchronization = KINETIC_SYNCHRONIZATION_WRITETHROUGH,
        };

        /* Put "keyX" => "valueX", where 'X' is 0..4.
         * This will block, because the callback field (arg 3) is NULL. */
        KineticStatus status = KineticClient_Put(session, &put_entry, NULL);
        printf("Put status: %s\n", Kinetic_GetStatusDescription(status));

        ByteBuffer_Free(put_key_buf);
        ByteBuffer_Free(put_value_buf);
        ByteBuffer_Free(put_tag_buf);
    }

    printf("\n\n\n");

    for (int i = 1; i < 3; i++) {
        KineticStatus status = KINETIC_STATUS_INVALID;
        static const ssize_t sz = 100;
        char key_buf[sz];
        char tag_buf[sz];
        char value_buf[sz];
        ByteBuffer keyBuffer = ByteBuffer_CreateAndAppendFormattedCString(key_buf, sz, "key%d", i);
        ByteBuffer tagBuffer = ByteBuffer_CreateAndAppendFormattedCString(tag_buf, sz, "tag%d", i);
        ByteBuffer valueBuffer = ByteBuffer_Create(value_buf, sz, 0);

        KineticEntry entry = {
            .key = keyBuffer,
            .tag = tagBuffer,
            .value = valueBuffer,
            .algorithm = KINETIC_ALGORITHM_SHA1,
        };

        if (i > 0) {
            status = KineticClient_GetPrevious(session, &entry, NULL);
            printf("GetPrevious status: %s\n", Kinetic_GetStatusDescription(status));
            if (status == KINETIC_STATUS_SUCCESS) {
                printf("Previous key before 'key%d': '%s', value '%s'\n",
                       i, key_buf, value_buf);
            }
        }
    }

    for (int i = 0; i < 2; i++) {
        KineticStatus status = KINETIC_STATUS_INVALID;
        static const ssize_t sz = 100;
        char key_buf[sz];
        char tag_buf[sz];
        char value_buf[sz];
        ByteBuffer keyBuffer = ByteBuffer_CreateAndAppendFormattedCString(key_buf, sz, "key%d", i);
        ByteBuffer tagBuffer = ByteBuffer_CreateAndAppendFormattedCString(tag_buf, sz, "tag%d", i);
        ByteBuffer valueBuffer = ByteBuffer_Create(value_buf, sz, 0);

        KineticEntry entry = {
            .key = keyBuffer,
            .tag = tagBuffer,
            .value = valueBuffer,
            .algorithm = KINETIC_ALGORITHM_SHA1,
        };

        if (i < 2) {
            status = KineticClient_GetNext(session, &entry, NULL);
            printf("GetNext status: %s\n", Kinetic_GetStatusDescription(status));
            if (status == KINETIC_STATUS_SUCCESS) {
                printf("Next key after 'key%d': '%s', value '%s'\n",
                       i, key_buf, value_buf);
            }
        }
    }

    /* No cleanup necessary */
}
    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_VERSION_MISMATCH, status);
}


void test_KineticClient_Put_should_allow_NULL_pointer_to_value_data_if_length_is_zero(void)
{
    ByteArray value = {.data = NULL, .len = 0};
    KineticEntry entry = {.value = ByteBuffer_CreateWithArray(value)};
    KineticOperation operation;
    operation.session = &Session;
    
    KineticAllocator_NewOperation_ExpectAndReturn(&Session, &operation);
    KineticBuilder_BuildPut_ExpectAndReturn(&operation, &entry, KINETIC_STATUS_SUCCESS);
    KineticController_ExecuteOperation_ExpectAndReturn(&operation, NULL, KINETIC_STATUS_VERSION_MISMATCH);

    KineticStatus status = KineticClient_Put(&Session, &entry, NULL);

    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_VERSION_MISMATCH, status);
}

void test_KineticClient_Put_should_return_BUFFER_OVERRUN_if_object_value_too_long(void)
{
    ByteArray value = ByteArray_CreateWithCString("Four score, and seven years ago");
    KineticEntry entry = {.value = ByteBuffer_CreateWithArray(value)};
    KineticOperation operation;
    operation.session = &Session;
    
    KineticAllocator_NewOperation_ExpectAndReturn(&Session, &operation);

    KineticBuilder_BuildPut_ExpectAndReturn(&operation, &entry, KINETIC_STATUS_BUFFER_OVERRUN);
    KineticAllocator_FreeOperation_Expect(&operation);
void test_put_get_delete_one_entry_by_one_entry(void)
{
    uint8_t version_data[DEFAULT_BUFFER_SIZE], tag_data[DEFAULT_BUFFER_SIZE], value_data[DEFAULT_BUFFER_SIZE];
    ByteBuffer version_buffer, tag_buffer, value_buffer;
    version_buffer = ByteBuffer_CreateAndAppendCString(version_data, sizeof(version_data), "v1.0");
    ExpectedVersionBuffer = ByteBuffer_CreateAndAppendCString(version_data, sizeof(version_data), "v1.0");
    tag_buffer = ByteBuffer_CreateAndAppendCString(tag_data, sizeof(tag_data), "SomeTagValue");
    ExpectedTagBuffer = ByteBuffer_CreateAndAppendCString(tag_data, sizeof(tag_data), "SomeTagValue");
    value_buffer = ByteBuffer_Create(value_data, DEFAULT_BUFFER_SIZE, 0);

    unsigned int i;
    for (i=0; i<KV_PAIRS_PER_GROUP; i++)
    {
    	// put object
        KineticEntry putEntry = {
   	       .key = generate_entry_key_by_index(i),
   	       .tag = tag_buffer,
   	       .newVersion = version_buffer,
           .algorithm = KINETIC_ALGORITHM_SHA1,
           .value = generate_entry_value_by_index(i),
           .force = true,
           .synchronization = KINETIC_SYNCHRONIZATION_WRITETHROUGH,
        };

        KineticStatus status = KineticClient_Put(Fixture.session, &putEntry, NULL);
        TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);

        ByteBuffer_Reset(&value_buffer);

        // get object
		value_buffer = ByteBuffer_Create(value_data, DEFAULT_BUFFER_SIZE, 0);
		KineticEntry getEntry = {
	        .key = generate_entry_key_by_index(i),
			.dbVersion = version_buffer,
	        .tag = tag_buffer,
			.value = value_buffer,
	    };

	    status = KineticClient_Get(Fixture.session, &getEntry, NULL);

	    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
	    TEST_ASSERT_EQUAL_ByteBuffer(ExpectedVersionBuffer, getEntry.dbVersion);
	    TEST_ASSERT_ByteBuffer_NULL(getEntry.newVersion);
	    TEST_ASSERT_EQUAL_ByteBuffer(generate_entry_key_by_index(i), getEntry.key);
	    TEST_ASSERT_EQUAL_ByteBuffer(ExpectedTagBuffer, getEntry.tag);
	    TEST_ASSERT_EQUAL(KINETIC_ALGORITHM_SHA1, getEntry.algorithm);
	    TEST_ASSERT_EQUAL_ByteBuffer(generate_entry_value_by_index(i), getEntry.value);

	    TEST_ASSERT_EQUAL_ByteBuffer(ExpectedVersionBuffer, version_buffer);
   	    // delete object
	    KineticEntry deleteEntry = {
	        .key = generate_entry_key_by_index(i),
			.dbVersion = version_buffer,
	    };
	    TEST_ASSERT_EQUAL_ByteBuffer(ExpectedVersionBuffer, version_buffer);
	    status = KineticClient_Delete(Fixture.session, &deleteEntry, NULL);
	    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
	    TEST_ASSERT_EQUAL(0, deleteEntry.value.bytesUsed);

   	    // get object again
     	status = KineticClient_Get(Fixture.session, &getEntry, NULL);
     	TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_NOT_FOUND, status);
    }
}

void test_put_get_delete_one_group_by_one_group(void)
{
    uint8_t version_data[DEFAULT_BUFFER_SIZE], tag_data[DEFAULT_BUFFER_SIZE], value_data[DEFAULT_BUFFER_SIZE];
    ByteBuffer version_buffer, tag_buffer, value_buffer;
    version_buffer = ByteBuffer_CreateAndAppendCString(version_data, sizeof(version_data), "v1.0");
    ExpectedVersionBuffer = ByteBuffer_CreateAndAppendCString(version_data, sizeof(version_data), "v1.0");
    tag_buffer = ByteBuffer_CreateAndAppendCString(tag_data, sizeof(tag_data), "SomeTagValue");
    ExpectedTagBuffer = ByteBuffer_CreateAndAppendCString(tag_data, sizeof(tag_data), "SomeTagValue");
    value_buffer = ByteBuffer_Create(value_data, DEFAULT_BUFFER_SIZE, 0);

    unsigned int i, j;
    for (i =0; i<TOTAL_GROUPS; i++)
    {
    	KineticStatus status;

    	// put a group of entries
        for (j=0; j<KV_PAIRS_PER_GROUP; j++)
        {
            KineticEntry putEntry = {
       	       .key = generate_entry_key_by_index(i*KV_PAIRS_PER_GROUP + j),
       	       .tag = tag_buffer,
       	       .newVersion = version_buffer,
               .algorithm = KINETIC_ALGORITHM_SHA1,
               .value = generate_entry_value_by_index(i*KV_PAIRS_PER_GROUP + j),
               .force = true,
               .synchronization = KINETIC_SYNCHRONIZATION_WRITETHROUGH,
            };

            status = KineticClient_Put(Fixture.session, &putEntry, NULL);
            TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
        }

        // get key_range
        KineticKeyRange range = {
            .startKey = generate_entry_key_by_index(i*KV_PAIRS_PER_GROUP),
            .endKey = generate_entry_key_by_index(i*KV_PAIRS_PER_GROUP + KV_PAIRS_PER_GROUP -1),
            .startKeyInclusive = true,
            .endKeyInclusive = true,
            .maxReturned = KV_PAIRS_PER_GROUP,
        };

        ByteBuffer keyBuff[KV_PAIRS_PER_GROUP];
        uint8_t keysData[KV_PAIRS_PER_GROUP][DEFAULT_BUFFER_SIZE];
        for (j = 0; j < KV_PAIRS_PER_GROUP; j++) {
            memset(&keysData[j], 0, DEFAULT_BUFFER_SIZE);
            keyBuff[j] = ByteBuffer_Create(&keysData[j], DEFAULT_BUFFER_SIZE, 0);
        }
        ByteBufferArray keys = {.buffers = keyBuff, .count = KV_PAIRS_PER_GROUP, .used = 0};

        status = KineticClient_GetKeyRange(Fixture.session, &range, &keys, NULL);
        TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
        TEST_ASSERT_EQUAL(KV_PAIRS_PER_GROUP, keys.used);
        for (j = 0; j < KV_PAIRS_PER_GROUP; j++)
        {
        	TEST_ASSERT_EQUAL_ByteBuffer(generate_entry_key_by_index(i*KV_PAIRS_PER_GROUP + j), keys.buffers[j]);
        }

        // delete a group of entries
        for (j=0; j<KV_PAIRS_PER_GROUP; j++)
        {
        	ByteBuffer_Reset(&value_buffer);

   	        // get object
   			value_buffer = ByteBuffer_Create(value_data, DEFAULT_BUFFER_SIZE, 0);
   			KineticEntry getEntry = {
   		        .key = generate_entry_key_by_index(i*KV_PAIRS_PER_GROUP + j),
   				.dbVersion = version_buffer,
   		        .tag = tag_buffer,
   				.value = value_buffer,
   		    };

   		    status = KineticClient_Get(Fixture.session, &getEntry, NULL);

   		    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
   		    TEST_ASSERT_EQUAL_ByteBuffer(ExpectedVersionBuffer, getEntry.dbVersion);
   		    TEST_ASSERT_ByteBuffer_NULL(getEntry.newVersion);
   		    TEST_ASSERT_EQUAL_ByteBuffer(generate_entry_key_by_index(i*KV_PAIRS_PER_GROUP + j), getEntry.key);
   		    TEST_ASSERT_EQUAL_ByteBuffer(ExpectedTagBuffer, getEntry.tag);
   		    TEST_ASSERT_EQUAL(KINETIC_ALGORITHM_SHA1, getEntry.algorithm);
   		    TEST_ASSERT_EQUAL_ByteBuffer(generate_entry_value_by_index(i*KV_PAIRS_PER_GROUP + j), getEntry.value);
   		    TEST_ASSERT_EQUAL_ByteBuffer(ExpectedVersionBuffer, version_buffer);
        }

        // delete a group of entries
        for (j=0; j<KV_PAIRS_PER_GROUP; j++)
        {
        	   	    // delete object
  		    KineticEntry deleteEntry = {
   		        .key = generate_entry_key_by_index(i*KV_PAIRS_PER_GROUP + j),
   				.dbVersion = version_buffer,
   		    };
   		    TEST_ASSERT_EQUAL_ByteBuffer(ExpectedVersionBuffer, version_buffer);
   		    status = KineticClient_Delete(Fixture.session, &deleteEntry, NULL);
   		    TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_SUCCESS, status);
   		    TEST_ASSERT_EQUAL(0, deleteEntry.value.bytesUsed);

   	   	    // get object again
        	ByteBuffer_Reset(&value_buffer);

   	        // get object
   			value_buffer = ByteBuffer_Create(value_data, DEFAULT_BUFFER_SIZE, 0);
   			KineticEntry getEntry = {
   		        .key = generate_entry_key_by_index(i*KV_PAIRS_PER_GROUP + j),
   				.dbVersion = version_buffer,
   		        .tag = tag_buffer,
   				.value = value_buffer,
   		    };
   	     	status = KineticClient_Get(Fixture.session, &getEntry, NULL);
   	     	TEST_ASSERT_EQUAL_KineticStatus(KINETIC_STATUS_NOT_FOUND, status);
        }
    }
}
Exemple #18
0
int main(int argc, char** argv)
{
    (void)argc;
    (void)argv;

    // Initialize kinetic-c and configure sessions
    KineticSession* session;
    KineticClientConfig clientConfig = {
        .logFile = "stdout",
        .logLevel = 1,
    };
    KineticClient * client = KineticClient_Init(&clientConfig);
    if (client == NULL) { return 1; }
    const char HmacKeyString[] = "asdfasdf";
    KineticSessionConfig sessionConfig = {
        .host = "localhost",
        .port = KINETIC_PORT,
        .clusterVersion = 0,
        .identity = 1,
        .hmacKey = ByteArray_CreateWithCString(HmacKeyString),
    };
    KineticStatus status = KineticClient_CreateSession(&sessionConfig, client, &session);
    if (status != KINETIC_STATUS_SUCCESS) {
        fprintf(stderr, "Failed connecting to the Kinetic device w/status: %s\n",
            Kinetic_GetStatusDescription(status));
        exit(1);
    }

    // Create some entries so that we can query the keys
    printf("Storing some entries on the device...\n");
    const size_t numKeys = 3;
    if (!create_entries(session, numKeys)) {
        return 2;
    }

    // Query a range of keys
    const size_t keyLen = 64;
    uint8_t startKeyData[keyLen], endKeyData[keyLen];
    KineticKeyRange range = {
        .startKey = ByteBuffer_CreateAndAppendCString(startKeyData, sizeof(startKeyData), "key_prefix_00"),
        .endKey = ByteBuffer_CreateAndAppendCString(endKeyData, sizeof(endKeyData), "key_prefix_01"),
        .startKeyInclusive = true,
        .endKeyInclusive = true,
        .maxReturned = 3,
    };
    uint8_t keysData[numKeys][keyLen];
    ByteBuffer keyBuff[] = {
        ByteBuffer_Create(&keysData[0], keyLen, 0),
        ByteBuffer_Create(&keysData[1], keyLen, 0),
        ByteBuffer_Create(&keysData[2], keyLen, 0),
    };
    ByteBufferArray keys = {.buffers = &keyBuff[0], .count = numKeys};

    status = KineticClient_GetKeyRange(session, &range, &keys, NULL);
    if (status != KINETIC_STATUS_SUCCESS) {
        fprintf(stderr, "FAILURE: Failed retrieving key range from device!\n");
        return 3;
    }

    if (keys.used != 2) {
        fprintf(stderr, "FAILURE: Unexpected number of keys in returned range!\n");
        return 4;
    };
    if (keyBuff[0].bytesUsed != strlen("key_prefix_00")) {
        fprintf(stderr, "FAILURE: Key 0 length check failed!\n");
        return 4;
    }
    if (keyBuff[1].bytesUsed != strlen("key_prefix_01")) {
        fprintf(stderr, "FAILURE: Key 1 length check failed!\n");
        return 4;
    }
    if (keyBuff[2].bytesUsed != 0) {
        fprintf(stderr, "FAILURE: Key 2 was not empty as expected!\n");
        return 4;
    }

    // Shutdown client connection and cleanup
    KineticClient_DestroySession(session);
    KineticClient_Shutdown(client);
    printf("Key range retrieved successfully!\n");

    return 0;
}

static bool create_entries(KineticSession * const session, const int count)
{
    static const ssize_t sz = 20;
    char key_buf[sz];
    char value_buf[sz];

    for (int i = 0; i < count; i++) {

        ByteBuffer KeyBuffer = ByteBuffer_CreateAndAppendFormattedCString(key_buf, sz, "key_prefix_%02d", i);
        ByteBuffer ValueBuffer = ByteBuffer_CreateAndAppendFormattedCString(value_buf, sz, "val_%02d", i);
        
        /* Populate tag with SHA1 of value */
        ByteBuffer put_tag_buf = ByteBuffer_Malloc(20);
        uint8_t sha1[20];
        SHA1(ValueBuffer.array.data, ValueBuffer.bytesUsed, &sha1[0]);
        ByteBuffer_Append(&put_tag_buf, sha1, sizeof(sha1));
        
        KineticEntry entry = {
            .key = KeyBuffer,
            .value = ValueBuffer,
            .tag = put_tag_buf,
            .algorithm = KINETIC_ALGORITHM_SHA1,
            .force = true,
            .synchronization = KINETIC_SYNCHRONIZATION_WRITETHROUGH,
        };

        KineticStatus status = KineticClient_Put(session, &entry, NULL);
        if (KINETIC_STATUS_SUCCESS != status) { return false; }
    }

    return true;
}