443 lines
14 KiB
C
443 lines
14 KiB
C
|
|
#include "amduat/asl/asl_derivation_index_fs.h"
|
||
|
|
#include "amduat/asl/asl_store_index_fs.h"
|
||
|
|
#include "amduat/asl/collection.h"
|
||
|
|
#include "amduat/asl/log_cursor.h"
|
||
|
|
#include "amduat/asl/projection_checkpoint.h"
|
||
|
|
#include "amduat/asl/record.h"
|
||
|
|
#include "amduat/asl/store.h"
|
||
|
|
#include "amduat/enc/asl1_core.h"
|
||
|
|
#include "amduat/enc/tgk1_edge.h"
|
||
|
|
#include "amduat/hash/asl1.h"
|
||
|
|
#include "amduat/pel/derivation_sid.h"
|
||
|
|
#include "amduat/tgk/store.h"
|
||
|
|
#include "amduat/tgk/tgk_store_asl_index_fs.h"
|
||
|
|
|
||
|
|
#include <dirent.h>
|
||
|
|
#include <errno.h>
|
||
|
|
#include <stdbool.h>
|
||
|
|
#include <stdint.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <sys/stat.h>
|
||
|
|
#include <sys/types.h>
|
||
|
|
#include <unistd.h>
|
||
|
|
|
||
|
|
static char *make_temp_root(void) {
|
||
|
|
char *templ;
|
||
|
|
const char template_prefix[] = "/tmp/amduat_test_scriptlab_adapter_XXXXXX";
|
||
|
|
|
||
|
|
templ = (char *)malloc(sizeof(template_prefix));
|
||
|
|
if (templ == NULL) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
memcpy(templ, template_prefix, sizeof(template_prefix));
|
||
|
|
if (mkdtemp(templ) == NULL) {
|
||
|
|
free(templ);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
return templ;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool remove_tree(const char *path) {
|
||
|
|
struct stat st;
|
||
|
|
DIR *dir;
|
||
|
|
struct dirent *entry;
|
||
|
|
|
||
|
|
if (path == NULL) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (lstat(path, &st) != 0) {
|
||
|
|
return errno == ENOENT;
|
||
|
|
}
|
||
|
|
if (!S_ISDIR(st.st_mode)) {
|
||
|
|
return unlink(path) == 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
dir = opendir(path);
|
||
|
|
if (dir == NULL) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
while ((entry = readdir(dir)) != NULL) {
|
||
|
|
char child[2048];
|
||
|
|
if (strcmp(entry->d_name, ".") == 0 ||
|
||
|
|
strcmp(entry->d_name, "..") == 0) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
snprintf(child, sizeof(child), "%s/%s", path, entry->d_name);
|
||
|
|
if (!remove_tree(child)) {
|
||
|
|
closedir(dir);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
closedir(dir);
|
||
|
|
return rmdir(path) == 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool ref_list_contains(amduat_reference_t needle,
|
||
|
|
const amduat_reference_t *list,
|
||
|
|
size_t len) {
|
||
|
|
for (size_t i = 0u; i < len; ++i) {
|
||
|
|
if (amduat_reference_eq(needle, list[i])) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int test_scriptlab_adapter_flow(void) {
|
||
|
|
amduat_asl_store_config_t config;
|
||
|
|
amduat_asl_store_index_fs_t asl_fs;
|
||
|
|
amduat_asl_store_t asl_store;
|
||
|
|
amduat_asl_collection_store_t collection_store;
|
||
|
|
amduat_asl_derivation_index_fs_t deriv_index;
|
||
|
|
amduat_reference_t node_a;
|
||
|
|
amduat_reference_t node_b;
|
||
|
|
amduat_reference_t edge_payload_ref;
|
||
|
|
amduat_reference_t edge_ref;
|
||
|
|
amduat_reference_t snapshot_ref;
|
||
|
|
amduat_reference_t program_ref;
|
||
|
|
amduat_reference_t checkpoint_ref;
|
||
|
|
amduat_octets_t edge_bytes = amduat_octets(NULL, 0u);
|
||
|
|
amduat_octets_t checkpoint_bytes = amduat_octets(NULL, 0u);
|
||
|
|
amduat_octets_t sid = amduat_octets(NULL, 0u);
|
||
|
|
amduat_asl_log_record_t *log_records = NULL;
|
||
|
|
size_t log_count = 0u;
|
||
|
|
amduat_asl_log_position_t next_logseq = 0u;
|
||
|
|
bool log_end = true;
|
||
|
|
amduat_asl_index_state_t index_state;
|
||
|
|
uint64_t collection_offset = 0u;
|
||
|
|
bool swapped = false;
|
||
|
|
char *root;
|
||
|
|
int exit_code = 1;
|
||
|
|
|
||
|
|
root = make_temp_root();
|
||
|
|
if (root == NULL) {
|
||
|
|
fprintf(stderr, "temp root failed\n");
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(&config, 0, sizeof(config));
|
||
|
|
config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
|
||
|
|
config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
|
||
|
|
|
||
|
|
if (!amduat_asl_store_index_fs_init(&asl_fs, config, root)) {
|
||
|
|
fprintf(stderr, "index fs init failed\n");
|
||
|
|
goto cleanup_root;
|
||
|
|
}
|
||
|
|
amduat_asl_store_init(&asl_store, config, amduat_asl_store_index_fs_ops(), &asl_fs);
|
||
|
|
|
||
|
|
if (!amduat_asl_collection_store_init(&collection_store, root, &asl_store)) {
|
||
|
|
fprintf(stderr, "collection store init failed\n");
|
||
|
|
goto cleanup_root;
|
||
|
|
}
|
||
|
|
if (!amduat_asl_derivation_index_fs_init(&deriv_index, root)) {
|
||
|
|
fprintf(stderr, "derivation index init failed\n");
|
||
|
|
goto cleanup_root;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (amduat_asl_record_store_put(
|
||
|
|
&asl_store,
|
||
|
|
amduat_octets("scriptlab/node", strlen("scriptlab/node")),
|
||
|
|
amduat_octets("A", 1u),
|
||
|
|
&node_a) != AMDUAT_ASL_STORE_OK) {
|
||
|
|
fprintf(stderr, "node A store failed\n");
|
||
|
|
goto cleanup_root;
|
||
|
|
}
|
||
|
|
if (amduat_asl_record_store_put(
|
||
|
|
&asl_store,
|
||
|
|
amduat_octets("scriptlab/node", strlen("scriptlab/node")),
|
||
|
|
amduat_octets("B", 1u),
|
||
|
|
&node_b) != AMDUAT_ASL_STORE_OK) {
|
||
|
|
fprintf(stderr, "node B store failed\n");
|
||
|
|
goto cleanup_nodes;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (amduat_asl_record_store_put(
|
||
|
|
&asl_store,
|
||
|
|
amduat_octets("tgk/edge_payload", strlen("tgk/edge_payload")),
|
||
|
|
amduat_octets("relates", strlen("relates")),
|
||
|
|
&edge_payload_ref) != AMDUAT_ASL_STORE_OK) {
|
||
|
|
fprintf(stderr, "edge payload store failed\n");
|
||
|
|
goto cleanup_nodes;
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
amduat_tgk_edge_body_t edge;
|
||
|
|
amduat_reference_t from_refs[1] = {node_a};
|
||
|
|
amduat_reference_t to_refs[1] = {node_b};
|
||
|
|
|
||
|
|
memset(&edge, 0, sizeof(edge));
|
||
|
|
edge.type = 1u;
|
||
|
|
edge.from = from_refs;
|
||
|
|
edge.from_len = 1u;
|
||
|
|
edge.to = to_refs;
|
||
|
|
edge.to_len = 1u;
|
||
|
|
edge.payload = edge_payload_ref;
|
||
|
|
|
||
|
|
if (!amduat_enc_tgk1_edge_encode_v1(&edge, &edge_bytes)) {
|
||
|
|
fprintf(stderr, "edge encode failed\n");
|
||
|
|
goto cleanup_nodes;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
amduat_artifact_t edge_artifact = amduat_artifact_with_type(
|
||
|
|
edge_bytes, amduat_type_tag(AMDUAT_TYPE_TAG_TGK1_EDGE_V1));
|
||
|
|
if (amduat_asl_store_put(&asl_store, edge_artifact, &edge_ref) != AMDUAT_ASL_STORE_OK) {
|
||
|
|
fprintf(stderr, "edge store failed\n");
|
||
|
|
goto cleanup_edge_bytes;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
amduat_octets_free(&edge_bytes);
|
||
|
|
|
||
|
|
if (amduat_asl_collection_append(&collection_store,
|
||
|
|
"tgk_edges",
|
||
|
|
edge_ref,
|
||
|
|
0u,
|
||
|
|
amduat_octets(NULL, 0u),
|
||
|
|
&collection_offset) != AMDUAT_ASL_COLLECTION_OK) {
|
||
|
|
fprintf(stderr, "collection append failed\n");
|
||
|
|
goto cleanup_edge_ref;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (amduat_asl_collection_snapshot(&collection_store,
|
||
|
|
"tgk_edges",
|
||
|
|
UINT64_MAX,
|
||
|
|
&snapshot_ref,
|
||
|
|
&swapped) != AMDUAT_ASL_COLLECTION_OK) {
|
||
|
|
fprintf(stderr, "collection snapshot failed\n");
|
||
|
|
goto cleanup_edge_ref;
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
amduat_asl_record_t snapshot_record;
|
||
|
|
if (amduat_asl_record_store_get(&asl_store, snapshot_ref, &snapshot_record) !=
|
||
|
|
AMDUAT_ASL_STORE_OK) {
|
||
|
|
fprintf(stderr, "snapshot record fetch failed\n");
|
||
|
|
goto cleanup_snapshot_ref;
|
||
|
|
}
|
||
|
|
{
|
||
|
|
amduat_asl_collection_snapshot_payload_t payload;
|
||
|
|
if (!amduat_asl_collection_snapshot_payload_decode_v1(
|
||
|
|
snapshot_record.payload, &payload)) {
|
||
|
|
fprintf(stderr, "snapshot payload decode failed\n");
|
||
|
|
amduat_asl_record_free(&snapshot_record);
|
||
|
|
goto cleanup_snapshot_ref;
|
||
|
|
}
|
||
|
|
if (!ref_list_contains(edge_ref, payload.refs, payload.refs_len)) {
|
||
|
|
fprintf(stderr, "snapshot missing edge ref\n");
|
||
|
|
amduat_asl_collection_snapshot_payload_free(&payload);
|
||
|
|
amduat_asl_record_free(&snapshot_record);
|
||
|
|
goto cleanup_snapshot_ref;
|
||
|
|
}
|
||
|
|
amduat_asl_collection_snapshot_payload_free(&payload);
|
||
|
|
}
|
||
|
|
amduat_asl_record_free(&snapshot_record);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (amduat_asl_record_store_put(
|
||
|
|
&asl_store,
|
||
|
|
amduat_octets("scriptlab/transform", strlen("scriptlab/transform")),
|
||
|
|
amduat_octets("tgk_snapshot_v1", strlen("tgk_snapshot_v1")),
|
||
|
|
&program_ref) != AMDUAT_ASL_STORE_OK) {
|
||
|
|
fprintf(stderr, "program record store failed\n");
|
||
|
|
goto cleanup_snapshot_ref;
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
amduat_pel_derivation_sid_input_t sid_input;
|
||
|
|
amduat_asl_derivation_record_t record;
|
||
|
|
amduat_octets_t exec_profile =
|
||
|
|
amduat_octets("scriptlab/adapter", strlen("scriptlab/adapter"));
|
||
|
|
amduat_reference_t inputs[1] = {edge_ref};
|
||
|
|
|
||
|
|
memset(&sid_input, 0, sizeof(sid_input));
|
||
|
|
sid_input.program_ref = program_ref;
|
||
|
|
sid_input.input_refs = inputs;
|
||
|
|
sid_input.input_refs_len = 1u;
|
||
|
|
sid_input.has_params_ref = false;
|
||
|
|
sid_input.has_exec_profile = true;
|
||
|
|
sid_input.exec_profile = exec_profile;
|
||
|
|
|
||
|
|
if (!amduat_pel_derivation_sid_compute(&sid_input, &sid)) {
|
||
|
|
fprintf(stderr, "sid compute failed\n");
|
||
|
|
goto cleanup_program_ref;
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(&record, 0, sizeof(record));
|
||
|
|
record.sid = sid;
|
||
|
|
record.program_ref = program_ref;
|
||
|
|
record.output_index = 0u;
|
||
|
|
record.input_refs = inputs;
|
||
|
|
record.input_refs_len = 1u;
|
||
|
|
record.has_params_ref = false;
|
||
|
|
record.has_exec_profile = true;
|
||
|
|
record.exec_profile = exec_profile;
|
||
|
|
|
||
|
|
if (amduat_asl_derivation_index_fs_add(&deriv_index,
|
||
|
|
snapshot_ref,
|
||
|
|
&record) != AMDUAT_ASL_STORE_OK) {
|
||
|
|
fprintf(stderr, "derivation index add failed\n");
|
||
|
|
goto cleanup_sid;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!amduat_asl_index_current_state(&asl_store, &index_state)) {
|
||
|
|
fprintf(stderr, "index state read failed\n");
|
||
|
|
goto cleanup_sid;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!amduat_asl_log_read_since(&asl_store,
|
||
|
|
0u,
|
||
|
|
0u,
|
||
|
|
&log_records,
|
||
|
|
&log_count,
|
||
|
|
&next_logseq,
|
||
|
|
&log_end)) {
|
||
|
|
fprintf(stderr, "log read since failed\n");
|
||
|
|
goto cleanup_sid;
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
amduat_asl_projection_checkpoint_v1_t checkpoint;
|
||
|
|
amduat_asl_projection_checkpoint_v1_t decoded;
|
||
|
|
|
||
|
|
memset(&checkpoint, 0, sizeof(checkpoint));
|
||
|
|
checkpoint.checkpoint_version = AMDUAT_ASL_PROJECTION_CHECKPOINT_VERSION_V1;
|
||
|
|
checkpoint.projection_name =
|
||
|
|
amduat_octets("scriptlab", strlen("scriptlab"));
|
||
|
|
checkpoint.projection_schema_version = 1u;
|
||
|
|
checkpoint.has_snapshot_id = true;
|
||
|
|
checkpoint.snapshot_id = index_state.snapshot_id;
|
||
|
|
checkpoint.log_position = next_logseq;
|
||
|
|
checkpoint.created_at = 0u;
|
||
|
|
checkpoint.builder_id =
|
||
|
|
amduat_octets("scriptlab-adapter", strlen("scriptlab-adapter"));
|
||
|
|
checkpoint.has_artifact_count = true;
|
||
|
|
checkpoint.artifact_count = log_count;
|
||
|
|
|
||
|
|
if (!amduat_asl_projection_checkpoint_encode_v1(&checkpoint,
|
||
|
|
&checkpoint_bytes)) {
|
||
|
|
fprintf(stderr, "checkpoint encode failed\n");
|
||
|
|
goto cleanup_log_records;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (amduat_asl_record_store_put(
|
||
|
|
&asl_store,
|
||
|
|
amduat_octets("projection/checkpoint",
|
||
|
|
strlen("projection/checkpoint")),
|
||
|
|
checkpoint_bytes,
|
||
|
|
&checkpoint_ref) != AMDUAT_ASL_STORE_OK) {
|
||
|
|
fprintf(stderr, "checkpoint store failed\n");
|
||
|
|
goto cleanup_checkpoint_bytes;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!amduat_asl_projection_checkpoint_decode_v1(checkpoint_bytes,
|
||
|
|
&decoded)) {
|
||
|
|
fprintf(stderr, "checkpoint decode failed\n");
|
||
|
|
goto cleanup_checkpoint_ref;
|
||
|
|
}
|
||
|
|
if (decoded.log_position != next_logseq) {
|
||
|
|
fprintf(stderr, "checkpoint log position mismatch\n");
|
||
|
|
amduat_asl_projection_checkpoint_free(&decoded);
|
||
|
|
goto cleanup_checkpoint_ref;
|
||
|
|
}
|
||
|
|
amduat_asl_projection_checkpoint_free(&decoded);
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
amduat_tgk_identity_domain_t domains[1];
|
||
|
|
uint32_t edge_tags[1];
|
||
|
|
amduat_tgk_edge_type_id_t edge_types[1];
|
||
|
|
amduat_asl_encoding_profile_id_t encodings[1];
|
||
|
|
amduat_tgk_store_config_t tgk_config;
|
||
|
|
amduat_tgk_store_asl_index_fs_t tgk_fs;
|
||
|
|
amduat_tgk_store_t tgk_store;
|
||
|
|
amduat_tgk_edge_type_filter_t filter;
|
||
|
|
amduat_tgk_graph_edge_view_list_t edges;
|
||
|
|
bool found = false;
|
||
|
|
|
||
|
|
memset(&tgk_config, 0, sizeof(tgk_config));
|
||
|
|
domains[0].encoding_profile = AMDUAT_ENC_ASL1_CORE_V1;
|
||
|
|
domains[0].hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
|
||
|
|
tgk_config.id_space.domains = domains;
|
||
|
|
tgk_config.id_space.domains_len = 1u;
|
||
|
|
tgk_config.artifact_scope.description = amduat_octets(NULL, 0u);
|
||
|
|
edge_tags[0] = AMDUAT_TYPE_TAG_TGK1_EDGE_V1;
|
||
|
|
edge_types[0] = 1u;
|
||
|
|
encodings[0] = TGK1_EDGE_ENC_V1;
|
||
|
|
tgk_config.tgk_profiles.edge_tags = edge_tags;
|
||
|
|
tgk_config.tgk_profiles.edge_tags_len = 1u;
|
||
|
|
tgk_config.tgk_profiles.edge_types = edge_types;
|
||
|
|
tgk_config.tgk_profiles.edge_types_len = 1u;
|
||
|
|
tgk_config.tgk_profiles.encodings = encodings;
|
||
|
|
tgk_config.tgk_profiles.encodings_len = 1u;
|
||
|
|
|
||
|
|
if (!amduat_tgk_store_asl_index_fs_init(&tgk_fs, tgk_config, &asl_fs)) {
|
||
|
|
fprintf(stderr, "tgk store init failed\n");
|
||
|
|
goto cleanup_checkpoint_ref;
|
||
|
|
}
|
||
|
|
amduat_tgk_store_init(&tgk_store,
|
||
|
|
tgk_config,
|
||
|
|
amduat_tgk_store_asl_index_fs_ops(),
|
||
|
|
&tgk_fs);
|
||
|
|
|
||
|
|
filter.types = edge_types;
|
||
|
|
filter.types_len = 1u;
|
||
|
|
|
||
|
|
if (!amduat_tgk_store_edges_from(&tgk_store, node_a, filter, &edges)) {
|
||
|
|
fprintf(stderr, "tgk edges_from failed\n");
|
||
|
|
goto cleanup_checkpoint_ref;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (size_t i = 0u; i < edges.len; ++i) {
|
||
|
|
amduat_tgk_edge_body_t *body = &edges.edges[i].body;
|
||
|
|
if (ref_list_contains(node_b, body->to, body->to_len)) {
|
||
|
|
found = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
amduat_tgk_graph_edge_view_list_free(&edges);
|
||
|
|
|
||
|
|
if (!found) {
|
||
|
|
fprintf(stderr, "tgk traversal missing expected edge\n");
|
||
|
|
goto cleanup_checkpoint_ref;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
exit_code = 0;
|
||
|
|
|
||
|
|
cleanup_checkpoint_ref:
|
||
|
|
amduat_reference_free(&checkpoint_ref);
|
||
|
|
cleanup_log_records:
|
||
|
|
amduat_enc_asl_log_free(log_records, log_count);
|
||
|
|
cleanup_checkpoint_bytes:
|
||
|
|
amduat_octets_free(&checkpoint_bytes);
|
||
|
|
cleanup_sid:
|
||
|
|
amduat_octets_free(&sid);
|
||
|
|
cleanup_program_ref:
|
||
|
|
amduat_reference_free(&program_ref);
|
||
|
|
cleanup_snapshot_ref:
|
||
|
|
amduat_reference_free(&snapshot_ref);
|
||
|
|
cleanup_edge_ref:
|
||
|
|
amduat_reference_free(&edge_ref);
|
||
|
|
cleanup_edge_bytes:
|
||
|
|
amduat_octets_free(&edge_bytes);
|
||
|
|
cleanup_nodes:
|
||
|
|
amduat_reference_free(&edge_payload_ref);
|
||
|
|
amduat_reference_free(&node_b);
|
||
|
|
amduat_reference_free(&node_a);
|
||
|
|
cleanup_root:
|
||
|
|
if (!remove_tree(root)) {
|
||
|
|
fprintf(stderr, "cleanup failed\n");
|
||
|
|
exit_code = 1;
|
||
|
|
}
|
||
|
|
free(root);
|
||
|
|
return exit_code;
|
||
|
|
}
|
||
|
|
|
||
|
|
int main(void) {
|
||
|
|
return test_scriptlab_adapter_flow();
|
||
|
|
}
|