Add Minio adapter.
This commit is contained in:
parent
1f4b8ff5bc
commit
7e3e302988
|
|
@ -157,6 +157,11 @@ set(AMDUAT_ASL_STORE_INDEX_FS_SRCS
|
||||||
src/adapters/asl_store_index_fs/asl_store_index_fs_layout.c
|
src/adapters/asl_store_index_fs/asl_store_index_fs_layout.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(AMDUAT_ASL_STORE_MINIO_SRCS
|
||||||
|
src/adapters/asl_store_minio/asl_store_minio.c
|
||||||
|
src/adapters/asl_store_minio/s3_sigv4.c
|
||||||
|
)
|
||||||
|
|
||||||
set(AMDUAT_ASL_DERIVATION_INDEX_FS_SRCS
|
set(AMDUAT_ASL_DERIVATION_INDEX_FS_SRCS
|
||||||
src/adapters/asl_derivation_index_fs/asl_derivation_index_fs.c
|
src/adapters/asl_derivation_index_fs/asl_derivation_index_fs.c
|
||||||
)
|
)
|
||||||
|
|
@ -213,6 +218,10 @@ amduat_add_lib(asl_store_index_fs SRCS ${AMDUAT_ASL_STORE_INDEX_FS_SRCS})
|
||||||
amduat_link(asl_store_index_fs amduat_asl amduat_enc amduat_hash_asl1 amduat_util)
|
amduat_link(asl_store_index_fs amduat_asl amduat_enc amduat_hash_asl1 amduat_util)
|
||||||
target_compile_definitions(amduat_asl_store_index_fs_obj PRIVATE _POSIX_C_SOURCE=200809L)
|
target_compile_definitions(amduat_asl_store_index_fs_obj PRIVATE _POSIX_C_SOURCE=200809L)
|
||||||
|
|
||||||
|
amduat_add_lib(asl_store_minio SRCS ${AMDUAT_ASL_STORE_MINIO_SRCS})
|
||||||
|
amduat_link(asl_store_minio amduat_asl amduat_enc amduat_hash_asl1 amduat_util)
|
||||||
|
target_compile_definitions(amduat_asl_store_minio_obj PRIVATE _POSIX_C_SOURCE=200809L)
|
||||||
|
|
||||||
amduat_add_lib(asl_derivation_index_fs SRCS ${AMDUAT_ASL_DERIVATION_INDEX_FS_SRCS})
|
amduat_add_lib(asl_derivation_index_fs SRCS ${AMDUAT_ASL_DERIVATION_INDEX_FS_SRCS})
|
||||||
amduat_link(asl_derivation_index_fs amduat_asl amduat_enc amduat_hash_asl1 amduat_util)
|
amduat_link(asl_derivation_index_fs amduat_asl amduat_enc amduat_hash_asl1 amduat_util)
|
||||||
target_compile_definitions(amduat_asl_derivation_index_fs_obj PRIVATE _POSIX_C_SOURCE=200809L)
|
target_compile_definitions(amduat_asl_derivation_index_fs_obj PRIVATE _POSIX_C_SOURCE=200809L)
|
||||||
|
|
@ -318,6 +327,7 @@ target_include_directories(amduat_conformance_run
|
||||||
)
|
)
|
||||||
target_link_libraries(amduat_conformance_run
|
target_link_libraries(amduat_conformance_run
|
||||||
PRIVATE amduat_asl_store_fs amduat_asl_store_index_fs
|
PRIVATE amduat_asl_store_fs amduat_asl_store_index_fs
|
||||||
|
amduat_asl_store_minio
|
||||||
amduat_tgk_store_mem amduat_tgk_store_fs amduat_tgk_store_asl_index_fs
|
amduat_tgk_store_mem amduat_tgk_store_fs amduat_tgk_store_asl_index_fs
|
||||||
amduat_tgk amduat_asl amduat_enc amduat_hash_asl1 amduat_util
|
amduat_tgk amduat_asl amduat_enc amduat_hash_asl1 amduat_util
|
||||||
amduat_format
|
amduat_format
|
||||||
|
|
@ -598,6 +608,7 @@ target_compile_definitions(amduat_test_asl_store_conformance
|
||||||
)
|
)
|
||||||
target_link_libraries(amduat_test_asl_store_conformance
|
target_link_libraries(amduat_test_asl_store_conformance
|
||||||
PRIVATE amduat_asl_store_fs amduat_asl_store_index_fs
|
PRIVATE amduat_asl_store_fs amduat_asl_store_index_fs
|
||||||
|
amduat_asl_store_minio
|
||||||
amduat_asl amduat_enc amduat_hash_asl1 amduat_util
|
amduat_asl amduat_enc amduat_hash_asl1 amduat_util
|
||||||
)
|
)
|
||||||
add_test(NAME asl_store_conformance
|
add_test(NAME asl_store_conformance
|
||||||
|
|
|
||||||
67
docs/minio-adapter.md
Normal file
67
docs/minio-adapter.md
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
# MinIO ASL Store Adapter
|
||||||
|
|
||||||
|
This adapter provides an S3-compatible `ASL/1-STORE` backend for MinIO. It is
|
||||||
|
opt-in for conformance runs and does not require MinIO or any external services
|
||||||
|
at build time.
|
||||||
|
|
||||||
|
## Required environment
|
||||||
|
|
||||||
|
These are required when running conformance against the MinIO backend:
|
||||||
|
|
||||||
|
- `AMDUAT_MINIO_ENDPOINT` (e.g. `http://localhost:9000`)
|
||||||
|
- `AMDUAT_MINIO_BUCKET` (e.g. `amduat`)
|
||||||
|
- `AMDUAT_MINIO_ACCESS_KEY`
|
||||||
|
- `AMDUAT_MINIO_SECRET_KEY`
|
||||||
|
|
||||||
|
Optional configuration:
|
||||||
|
|
||||||
|
- `AMDUAT_MINIO_REGION` (default: `us-east-1`)
|
||||||
|
- `AMDUAT_MINIO_PREFIX` (default: `asl`)
|
||||||
|
- `AMDUAT_MINIO_FORCE_PATH_STYLE` (default: `1`)
|
||||||
|
- `AMDUAT_MINIO_TLS_VERIFY` (default: `1`)
|
||||||
|
- `AMDUAT_MINIO_TIMEOUT_MS` (default: `30000`)
|
||||||
|
- `AMDUAT_MINIO_RETRY_MAX` (default: `3`)
|
||||||
|
|
||||||
|
## Conformance runs
|
||||||
|
|
||||||
|
MinIO is skipped unless explicitly enabled.
|
||||||
|
|
||||||
|
Example (opt-in conformance run):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
AMDUAT_CONFORMANCE_ENABLE_MINIO=1 \
|
||||||
|
AMDUAT_MINIO_ENDPOINT=http://localhost:9000 \
|
||||||
|
AMDUAT_MINIO_BUCKET=amduat \
|
||||||
|
AMDUAT_MINIO_ACCESS_KEY=minioadmin \
|
||||||
|
AMDUAT_MINIO_SECRET_KEY=minioadmin \
|
||||||
|
./build/amduat_conformance_run --suite asl --backend minio --workdir /tmp/amduat_conformance
|
||||||
|
```
|
||||||
|
|
||||||
|
If required environment variables are missing, the backend is reported as
|
||||||
|
`SKIPPED` with the missing variable names.
|
||||||
|
|
||||||
|
## Key layout
|
||||||
|
|
||||||
|
Objects are stored under a sharded key derived from the canonical reference
|
||||||
|
hex:
|
||||||
|
|
||||||
|
```
|
||||||
|
<prefix>/<r0r1>/<r2r3>/<full_reference_hex>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error mapping
|
||||||
|
|
||||||
|
| HTTP/S3 response | Canonical taxonomy | Store error |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 404 | NotFound | `AMDUAT_ASL_STORE_ERR_NOT_FOUND` |
|
||||||
|
| 401, 403 | PermissionDenied | `AMDUAT_ASL_STORE_ERR_IO` |
|
||||||
|
| 408, 429 | TransientIO | `AMDUAT_ASL_STORE_ERR_IO` |
|
||||||
|
| 5xx | TransientIO | `AMDUAT_ASL_STORE_ERR_IO` |
|
||||||
|
| 4xx (other) | InvalidArtifact | `AMDUAT_ASL_STORE_ERR_INTEGRITY` |
|
||||||
|
| Network/TLS/parse errors | InternalError/TransientIO | `AMDUAT_ASL_STORE_ERR_IO` |
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
- HTTPS is not supported yet (HTTP-only). Use `http://` endpoints for now.
|
||||||
|
- Multipart uploads, streaming artifacts, and very large objects are not
|
||||||
|
implemented in Phase 0.
|
||||||
67
include/amduat/asl/asl_store_minio.h
Normal file
67
include/amduat/asl/asl_store_minio.h
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
#ifndef AMDUAT_ASL_STORE_MINIO_H
|
||||||
|
#define AMDUAT_ASL_STORE_MINIO_H
|
||||||
|
|
||||||
|
#include "amduat/asl/store.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum {
|
||||||
|
AMDUAT_ASL_STORE_MINIO_ENDPOINT_MAX = 1024,
|
||||||
|
AMDUAT_ASL_STORE_MINIO_HOST_MAX = 256,
|
||||||
|
AMDUAT_ASL_STORE_MINIO_BUCKET_MAX = 256,
|
||||||
|
AMDUAT_ASL_STORE_MINIO_ACCESS_KEY_MAX = 256,
|
||||||
|
AMDUAT_ASL_STORE_MINIO_SECRET_KEY_MAX = 256,
|
||||||
|
AMDUAT_ASL_STORE_MINIO_REGION_MAX = 64,
|
||||||
|
AMDUAT_ASL_STORE_MINIO_PREFIX_MAX = 128
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
amduat_asl_store_config_t store_config;
|
||||||
|
const char *endpoint;
|
||||||
|
const char *bucket;
|
||||||
|
const char *access_key;
|
||||||
|
const char *secret_key;
|
||||||
|
const char *region;
|
||||||
|
const char *prefix;
|
||||||
|
bool force_path_style;
|
||||||
|
bool tls_verify;
|
||||||
|
uint32_t timeout_ms;
|
||||||
|
uint32_t retry_max;
|
||||||
|
} amduat_asl_store_minio_config_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
amduat_asl_store_config_t store_config;
|
||||||
|
char endpoint[AMDUAT_ASL_STORE_MINIO_ENDPOINT_MAX];
|
||||||
|
char host[AMDUAT_ASL_STORE_MINIO_HOST_MAX];
|
||||||
|
uint16_t port;
|
||||||
|
bool use_tls;
|
||||||
|
char bucket[AMDUAT_ASL_STORE_MINIO_BUCKET_MAX];
|
||||||
|
char access_key[AMDUAT_ASL_STORE_MINIO_ACCESS_KEY_MAX];
|
||||||
|
char secret_key[AMDUAT_ASL_STORE_MINIO_SECRET_KEY_MAX];
|
||||||
|
char region[AMDUAT_ASL_STORE_MINIO_REGION_MAX];
|
||||||
|
char prefix[AMDUAT_ASL_STORE_MINIO_PREFIX_MAX];
|
||||||
|
bool force_path_style;
|
||||||
|
bool tls_verify;
|
||||||
|
uint32_t timeout_ms;
|
||||||
|
uint32_t retry_max;
|
||||||
|
} amduat_asl_store_minio_t;
|
||||||
|
|
||||||
|
void amduat_asl_store_minio_config_defaults(
|
||||||
|
amduat_asl_store_minio_config_t *config);
|
||||||
|
|
||||||
|
bool amduat_asl_store_minio_init(amduat_asl_store_minio_t *minio,
|
||||||
|
amduat_asl_store_minio_config_t config);
|
||||||
|
|
||||||
|
amduat_asl_store_ops_t amduat_asl_store_minio_ops(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* AMDUAT_ASL_STORE_MINIO_H */
|
||||||
1316
src/adapters/asl_store_minio/asl_store_minio.c
Normal file
1316
src/adapters/asl_store_minio/asl_store_minio.c
Normal file
File diff suppressed because it is too large
Load diff
288
src/adapters/asl_store_minio/s3_sigv4.c
Normal file
288
src/adapters/asl_store_minio/s3_sigv4.c
Normal file
|
|
@ -0,0 +1,288 @@
|
||||||
|
#include "s3_sigv4.h"
|
||||||
|
|
||||||
|
#include "amduat/asl/core.h"
|
||||||
|
#include "amduat/hash/asl1.h"
|
||||||
|
#include "amduat/util/hex.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
enum { AMDUAT_S3_SIGV4_SHA256_LEN = 32u, AMDUAT_S3_SIGV4_BLOCK_LEN = 64u };
|
||||||
|
|
||||||
|
static bool sha256_digest(const uint8_t *data, size_t len, uint8_t out[32]) {
|
||||||
|
amduat_octets_t input;
|
||||||
|
|
||||||
|
input.data = data;
|
||||||
|
input.len = len;
|
||||||
|
return amduat_hash_asl1_digest(AMDUAT_HASH_ASL1_ID_SHA256, input, out, 32u);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool sha256_hex(const uint8_t *data, size_t len, char out_hex[65]) {
|
||||||
|
uint8_t digest[AMDUAT_S3_SIGV4_SHA256_LEN];
|
||||||
|
|
||||||
|
if (!sha256_digest(data, len, digest)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!amduat_hex_encode_lower(digest, sizeof(digest), out_hex, 65u)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out_hex[64] = '\0';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool hmac_sha256(const uint8_t *key,
|
||||||
|
size_t key_len,
|
||||||
|
const uint8_t *msg,
|
||||||
|
size_t msg_len,
|
||||||
|
uint8_t out[32]) {
|
||||||
|
uint8_t key_block[AMDUAT_S3_SIGV4_BLOCK_LEN];
|
||||||
|
uint8_t ipad[AMDUAT_S3_SIGV4_BLOCK_LEN];
|
||||||
|
uint8_t opad[AMDUAT_S3_SIGV4_BLOCK_LEN];
|
||||||
|
uint8_t inner_hash[AMDUAT_S3_SIGV4_SHA256_LEN];
|
||||||
|
size_t i;
|
||||||
|
amduat_hash_asl1_stream_t stream;
|
||||||
|
|
||||||
|
memset(key_block, 0, sizeof(key_block));
|
||||||
|
if (key_len > sizeof(key_block)) {
|
||||||
|
if (!sha256_digest(key, key_len, key_block)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
key_len = AMDUAT_S3_SIGV4_SHA256_LEN;
|
||||||
|
} else if (key_len > 0 && key != NULL) {
|
||||||
|
memcpy(key_block, key, key_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(key_block); ++i) {
|
||||||
|
ipad[i] = (uint8_t)(key_block[i] ^ 0x36u);
|
||||||
|
opad[i] = (uint8_t)(key_block[i] ^ 0x5cu);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduat_hash_asl1_stream_init(AMDUAT_HASH_ASL1_ID_SHA256, &stream)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!amduat_hash_asl1_stream_update(
|
||||||
|
&stream, amduat_octets(ipad, sizeof(ipad))) ||
|
||||||
|
(msg_len > 0 &&
|
||||||
|
!amduat_hash_asl1_stream_update(
|
||||||
|
&stream, amduat_octets(msg, msg_len))) ||
|
||||||
|
!amduat_hash_asl1_stream_final(&stream, inner_hash, sizeof(inner_hash))) {
|
||||||
|
amduat_hash_asl1_stream_destroy(&stream);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
amduat_hash_asl1_stream_destroy(&stream);
|
||||||
|
|
||||||
|
if (!amduat_hash_asl1_stream_init(AMDUAT_HASH_ASL1_ID_SHA256, &stream)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!amduat_hash_asl1_stream_update(
|
||||||
|
&stream, amduat_octets(opad, sizeof(opad))) ||
|
||||||
|
!amduat_hash_asl1_stream_update(
|
||||||
|
&stream, amduat_octets(inner_hash, sizeof(inner_hash))) ||
|
||||||
|
!amduat_hash_asl1_stream_final(&stream, out, AMDUAT_S3_SIGV4_SHA256_LEN)) {
|
||||||
|
amduat_hash_asl1_stream_destroy(&stream);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
amduat_hash_asl1_stream_destroy(&stream);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool amduat_s3_sigv4_build_authorization(const char *method,
|
||||||
|
const char *canonical_uri,
|
||||||
|
const char *canonical_query,
|
||||||
|
const char *host,
|
||||||
|
const char *payload_hash_hex,
|
||||||
|
const char *access_key,
|
||||||
|
const char *secret_key,
|
||||||
|
const char *region,
|
||||||
|
const char *amz_date,
|
||||||
|
char **out_authorization) {
|
||||||
|
const char *signed_headers = "host;x-amz-content-sha256;x-amz-date";
|
||||||
|
char date_stamp[9];
|
||||||
|
char *canonical_request = NULL;
|
||||||
|
char *string_to_sign = NULL;
|
||||||
|
char canonical_request_hash[65];
|
||||||
|
uint8_t k_date[AMDUAT_S3_SIGV4_SHA256_LEN];
|
||||||
|
uint8_t k_region[AMDUAT_S3_SIGV4_SHA256_LEN];
|
||||||
|
uint8_t k_service[AMDUAT_S3_SIGV4_SHA256_LEN];
|
||||||
|
uint8_t k_signing[AMDUAT_S3_SIGV4_SHA256_LEN];
|
||||||
|
uint8_t signature_bytes[AMDUAT_S3_SIGV4_SHA256_LEN];
|
||||||
|
char signature_hex[65];
|
||||||
|
char *auth_header = NULL;
|
||||||
|
size_t auth_len;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (out_authorization != NULL) {
|
||||||
|
*out_authorization = NULL;
|
||||||
|
}
|
||||||
|
if (method == NULL || canonical_uri == NULL || canonical_query == NULL ||
|
||||||
|
host == NULL || payload_hash_hex == NULL || access_key == NULL ||
|
||||||
|
secret_key == NULL || region == NULL || amz_date == NULL ||
|
||||||
|
out_authorization == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (strlen(amz_date) < 8u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(date_stamp, amz_date, 8u);
|
||||||
|
date_stamp[8] = '\0';
|
||||||
|
|
||||||
|
rc = snprintf(NULL,
|
||||||
|
0,
|
||||||
|
"%s\n%s\n%s\n"
|
||||||
|
"host:%s\n"
|
||||||
|
"x-amz-content-sha256:%s\n"
|
||||||
|
"x-amz-date:%s\n\n"
|
||||||
|
"%s\n"
|
||||||
|
"%s",
|
||||||
|
method,
|
||||||
|
canonical_uri,
|
||||||
|
canonical_query,
|
||||||
|
host,
|
||||||
|
payload_hash_hex,
|
||||||
|
amz_date,
|
||||||
|
signed_headers,
|
||||||
|
payload_hash_hex);
|
||||||
|
if (rc <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
canonical_request = (char *)malloc((size_t)rc + 1u);
|
||||||
|
if (canonical_request == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
snprintf(canonical_request,
|
||||||
|
(size_t)rc + 1u,
|
||||||
|
"%s\n%s\n%s\n"
|
||||||
|
"host:%s\n"
|
||||||
|
"x-amz-content-sha256:%s\n"
|
||||||
|
"x-amz-date:%s\n\n"
|
||||||
|
"%s\n"
|
||||||
|
"%s",
|
||||||
|
method,
|
||||||
|
canonical_uri,
|
||||||
|
canonical_query,
|
||||||
|
host,
|
||||||
|
payload_hash_hex,
|
||||||
|
amz_date,
|
||||||
|
signed_headers,
|
||||||
|
payload_hash_hex);
|
||||||
|
|
||||||
|
if (!sha256_hex((const uint8_t *)canonical_request,
|
||||||
|
strlen(canonical_request),
|
||||||
|
canonical_request_hash)) {
|
||||||
|
free(canonical_request);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = snprintf(NULL,
|
||||||
|
0,
|
||||||
|
"AWS4-HMAC-SHA256\n%s\n%s/%s/s3/aws4_request\n%s",
|
||||||
|
amz_date,
|
||||||
|
date_stamp,
|
||||||
|
region,
|
||||||
|
canonical_request_hash);
|
||||||
|
if (rc <= 0) {
|
||||||
|
free(canonical_request);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
string_to_sign = (char *)malloc((size_t)rc + 1u);
|
||||||
|
if (string_to_sign == NULL) {
|
||||||
|
free(canonical_request);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
snprintf(string_to_sign,
|
||||||
|
(size_t)rc + 1u,
|
||||||
|
"AWS4-HMAC-SHA256\n%s\n%s/%s/s3/aws4_request\n%s",
|
||||||
|
amz_date,
|
||||||
|
date_stamp,
|
||||||
|
region,
|
||||||
|
canonical_request_hash);
|
||||||
|
|
||||||
|
{
|
||||||
|
size_t secret_len = strlen(secret_key);
|
||||||
|
size_t key_len = 4u + secret_len;
|
||||||
|
char *key_prefix = (char *)malloc(key_len + 1u);
|
||||||
|
if (key_prefix == NULL) {
|
||||||
|
free(canonical_request);
|
||||||
|
free(string_to_sign);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(key_prefix, "AWS4", 4u);
|
||||||
|
memcpy(key_prefix + 4u, secret_key, secret_len);
|
||||||
|
key_prefix[key_len] = '\0';
|
||||||
|
|
||||||
|
if (!hmac_sha256((const uint8_t *)key_prefix,
|
||||||
|
key_len,
|
||||||
|
(const uint8_t *)date_stamp,
|
||||||
|
strlen(date_stamp),
|
||||||
|
k_date) ||
|
||||||
|
!hmac_sha256(k_date,
|
||||||
|
sizeof(k_date),
|
||||||
|
(const uint8_t *)region,
|
||||||
|
strlen(region),
|
||||||
|
k_region) ||
|
||||||
|
!hmac_sha256(k_region,
|
||||||
|
sizeof(k_region),
|
||||||
|
(const uint8_t *)"s3",
|
||||||
|
2u,
|
||||||
|
k_service) ||
|
||||||
|
!hmac_sha256(k_service,
|
||||||
|
sizeof(k_service),
|
||||||
|
(const uint8_t *)"aws4_request",
|
||||||
|
12u,
|
||||||
|
k_signing) ||
|
||||||
|
!hmac_sha256(k_signing,
|
||||||
|
sizeof(k_signing),
|
||||||
|
(const uint8_t *)string_to_sign,
|
||||||
|
strlen(string_to_sign),
|
||||||
|
signature_bytes)) {
|
||||||
|
free(key_prefix);
|
||||||
|
free(canonical_request);
|
||||||
|
free(string_to_sign);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
free(key_prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduat_hex_encode_lower(signature_bytes,
|
||||||
|
sizeof(signature_bytes),
|
||||||
|
signature_hex,
|
||||||
|
sizeof(signature_hex))) {
|
||||||
|
free(canonical_request);
|
||||||
|
free(string_to_sign);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auth_len = (size_t)snprintf(NULL,
|
||||||
|
0,
|
||||||
|
"AWS4-HMAC-SHA256 Credential=%s/%s/%s/s3/aws4_request, "
|
||||||
|
"SignedHeaders=%s, Signature=%s",
|
||||||
|
access_key,
|
||||||
|
date_stamp,
|
||||||
|
region,
|
||||||
|
signed_headers,
|
||||||
|
signature_hex);
|
||||||
|
auth_header = (char *)malloc(auth_len + 1u);
|
||||||
|
if (auth_header == NULL) {
|
||||||
|
free(canonical_request);
|
||||||
|
free(string_to_sign);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
snprintf(auth_header,
|
||||||
|
auth_len + 1u,
|
||||||
|
"AWS4-HMAC-SHA256 Credential=%s/%s/%s/s3/aws4_request, "
|
||||||
|
"SignedHeaders=%s, Signature=%s",
|
||||||
|
access_key,
|
||||||
|
date_stamp,
|
||||||
|
region,
|
||||||
|
signed_headers,
|
||||||
|
signature_hex);
|
||||||
|
|
||||||
|
free(canonical_request);
|
||||||
|
free(string_to_sign);
|
||||||
|
*out_authorization = auth_header;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
25
src/adapters/asl_store_minio/s3_sigv4.h
Normal file
25
src/adapters/asl_store_minio/s3_sigv4.h
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef AMDUAT_ADAPTERS_ASL_STORE_MINIO_S3_SIGV4_H
|
||||||
|
#define AMDUAT_ADAPTERS_ASL_STORE_MINIO_S3_SIGV4_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool amduat_s3_sigv4_build_authorization(const char *method,
|
||||||
|
const char *canonical_uri,
|
||||||
|
const char *canonical_query,
|
||||||
|
const char *host,
|
||||||
|
const char *payload_hash_hex,
|
||||||
|
const char *access_key,
|
||||||
|
const char *secret_key,
|
||||||
|
const char *region,
|
||||||
|
const char *amz_date,
|
||||||
|
char **out_authorization);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* AMDUAT_ADAPTERS_ASL_STORE_MINIO_S3_SIGV4_H */
|
||||||
|
|
@ -70,9 +70,13 @@ static int print_results(const char *suite,
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < results_len; ++i) {
|
for (i = 0; i < results_len; ++i) {
|
||||||
const char *status = results[i].status == 0 ? "PASS" : "FAIL";
|
const char *status =
|
||||||
|
results[i].status == AMDUAT_CONFORMANCE_STATUS_PASS
|
||||||
|
? "PASS"
|
||||||
|
: (results[i].status == AMDUAT_CONFORMANCE_STATUS_SKIP ? "SKIPPED"
|
||||||
|
: "FAIL");
|
||||||
printf("%s backend=%s %s\n", suite, results[i].backend, status);
|
printf("%s backend=%s %s\n", suite, results[i].backend, status);
|
||||||
if (results[i].status != 0) {
|
if (results[i].status == AMDUAT_CONFORMANCE_STATUS_FAIL) {
|
||||||
rc = 1;
|
rc = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,15 @@
|
||||||
|
|
||||||
#include "amduat/asl/asl_store_fs.h"
|
#include "amduat/asl/asl_store_fs.h"
|
||||||
#include "amduat/asl/asl_store_index_fs.h"
|
#include "amduat/asl/asl_store_index_fs.h"
|
||||||
|
#include "amduat/asl/asl_store_minio.h"
|
||||||
#include "amduat/asl/core.h"
|
#include "amduat/asl/core.h"
|
||||||
|
#include "amduat/asl/ref_text.h"
|
||||||
#include "amduat/asl/store.h"
|
#include "amduat/asl/store.h"
|
||||||
#include "amduat/enc/asl1_core.h"
|
#include "amduat/enc/asl1_core.h"
|
||||||
#include "amduat/enc/asl1_core_codec.h"
|
#include "amduat/enc/asl1_core_codec.h"
|
||||||
|
#include "amduat/enc/asl_log.h"
|
||||||
#include "amduat/hash/asl1.h"
|
#include "amduat/hash/asl1.h"
|
||||||
|
#include "amduat/asl/index_replay.h"
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
|
@ -41,6 +45,18 @@ typedef struct {
|
||||||
amduat_asl_store_index_fs_t fs;
|
amduat_asl_store_index_fs_t fs;
|
||||||
} amduat_asl_index_fs_ctx_t;
|
} amduat_asl_index_fs_ctx_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
amduat_asl_store_minio_t minio;
|
||||||
|
} amduat_asl_minio_ctx_t;
|
||||||
|
|
||||||
|
static bool conformance_env_truthy(const char *value) {
|
||||||
|
if (value == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return strcmp(value, "1") == 0 || strcmp(value, "true") == 0 ||
|
||||||
|
strcmp(value, "yes") == 0;
|
||||||
|
}
|
||||||
|
|
||||||
static bool conformance_join_path(const char *base,
|
static bool conformance_join_path(const char *base,
|
||||||
const char *segment,
|
const char *segment,
|
||||||
char **out_path) {
|
char **out_path) {
|
||||||
|
|
@ -82,6 +98,82 @@ static bool conformance_join_path(const char *base,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool conformance_append_list(char **list, const char *entry) {
|
||||||
|
size_t list_len;
|
||||||
|
size_t entry_len;
|
||||||
|
size_t next_len;
|
||||||
|
char *next;
|
||||||
|
|
||||||
|
if (list == NULL || entry == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*list == NULL) {
|
||||||
|
*list = strdup(entry);
|
||||||
|
return *list != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_len = strlen(*list);
|
||||||
|
entry_len = strlen(entry);
|
||||||
|
next_len = list_len + 2u + entry_len + 1u;
|
||||||
|
|
||||||
|
next = (char *)realloc(*list, next_len);
|
||||||
|
if (next == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
next[list_len] = ',';
|
||||||
|
next[list_len + 1u] = ' ';
|
||||||
|
memcpy(next + list_len + 2u, entry, entry_len);
|
||||||
|
next[next_len - 1u] = '\0';
|
||||||
|
*list = next;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool conformance_minio_missing_envs(char **out_missing) {
|
||||||
|
if (out_missing != NULL) {
|
||||||
|
*out_missing = NULL;
|
||||||
|
}
|
||||||
|
if (out_missing == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const char *value = getenv("AMDUAT_MINIO_ENDPOINT");
|
||||||
|
if (value == NULL || value[0] == '\0') {
|
||||||
|
if (!conformance_append_list(out_missing, "AMDUAT_MINIO_ENDPOINT")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const char *value = getenv("AMDUAT_MINIO_BUCKET");
|
||||||
|
if (value == NULL || value[0] == '\0') {
|
||||||
|
if (!conformance_append_list(out_missing, "AMDUAT_MINIO_BUCKET")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const char *value = getenv("AMDUAT_MINIO_ACCESS_KEY");
|
||||||
|
if (value == NULL || value[0] == '\0') {
|
||||||
|
if (!conformance_append_list(out_missing, "AMDUAT_MINIO_ACCESS_KEY")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const char *value = getenv("AMDUAT_MINIO_SECRET_KEY");
|
||||||
|
if (value == NULL || value[0] == '\0') {
|
||||||
|
if (!conformance_append_list(out_missing, "AMDUAT_MINIO_SECRET_KEY")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool conformance_remove_tree(const char *path) {
|
static bool conformance_remove_tree(const char *path) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
DIR *dir;
|
DIR *dir;
|
||||||
|
|
@ -536,6 +628,18 @@ static bool conformance_build_artifact(amduat_octets_t payload,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void conformance_free_ref_hexes(char **refs, size_t refs_len) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (refs == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (i = 0; i < refs_len; ++i) {
|
||||||
|
free(refs[i]);
|
||||||
|
}
|
||||||
|
free(refs);
|
||||||
|
}
|
||||||
|
|
||||||
static bool conformance_asl_is_not_found(amduat_asl_store_error_t err) {
|
static bool conformance_asl_is_not_found(amduat_asl_store_error_t err) {
|
||||||
return err == AMDUAT_ASL_STORE_ERR_NOT_FOUND;
|
return err == AMDUAT_ASL_STORE_ERR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
@ -543,7 +647,8 @@ static bool conformance_asl_is_not_found(amduat_asl_store_error_t err) {
|
||||||
static int conformance_test_put_get(amduat_asl_store_t *store,
|
static int conformance_test_put_get(amduat_asl_store_t *store,
|
||||||
const amduat_conformance_fixture_t *fixtures,
|
const amduat_conformance_fixture_t *fixtures,
|
||||||
size_t fixtures_len,
|
size_t fixtures_len,
|
||||||
const char *fixture_root) {
|
const char *fixture_root,
|
||||||
|
char **out_ref_hexes) {
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
for (i = 0; i < fixtures_len; ++i) {
|
for (i = 0; i < fixtures_len; ++i) {
|
||||||
|
|
@ -594,6 +699,18 @@ static int conformance_test_put_get(amduat_asl_store_t *store,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (out_ref_hexes != NULL) {
|
||||||
|
char *hex = NULL;
|
||||||
|
if (!amduat_asl_ref_encode_hex(ref, &hex)) {
|
||||||
|
fprintf(stderr, "ref encode failed for fixture %s\n", fixtures[i].id);
|
||||||
|
amduat_reference_free(&ref);
|
||||||
|
amduat_artifact_free(&artifact);
|
||||||
|
amduat_artifact_free(&fetched);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
out_ref_hexes[i] = hex;
|
||||||
|
}
|
||||||
|
|
||||||
if (!amduat_artifact_eq(artifact, fetched)) {
|
if (!amduat_artifact_eq(artifact, fetched)) {
|
||||||
fprintf(stderr, "payload mismatch for fixture %s\n", fixtures[i].id);
|
fprintf(stderr, "payload mismatch for fixture %s\n", fixtures[i].id);
|
||||||
amduat_reference_free(&ref);
|
amduat_reference_free(&ref);
|
||||||
|
|
@ -697,6 +814,88 @@ static int conformance_test_get_not_found(amduat_asl_store_t *store) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool conformance_replay_state_equal(
|
||||||
|
const amduat_asl_replay_state_t *lhs,
|
||||||
|
const amduat_asl_replay_state_t *rhs) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (lhs->segments_len != rhs->segments_len ||
|
||||||
|
lhs->tombstones_len != rhs->tombstones_len ||
|
||||||
|
lhs->state.log_position != rhs->state.log_position) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < lhs->segments_len; ++i) {
|
||||||
|
const amduat_asl_segment_seal_t *left = &lhs->segments[i];
|
||||||
|
const amduat_asl_segment_seal_t *right = &rhs->segments[i];
|
||||||
|
if (left->segment_id != right->segment_id ||
|
||||||
|
memcmp(left->segment_hash, right->segment_hash,
|
||||||
|
sizeof(left->segment_hash)) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < lhs->tombstones_len; ++i) {
|
||||||
|
const amduat_asl_tombstone_entry_t *left = &lhs->tombstones[i];
|
||||||
|
const amduat_asl_tombstone_entry_t *right = &rhs->tombstones[i];
|
||||||
|
if (left->tombstone_logseq != right->tombstone_logseq ||
|
||||||
|
!amduat_reference_eq(left->ref, right->ref)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int conformance_test_replay_determinism(amduat_asl_store_t *store) {
|
||||||
|
amduat_asl_log_record_t *records = NULL;
|
||||||
|
size_t record_count = 0u;
|
||||||
|
amduat_asl_replay_state_t first;
|
||||||
|
amduat_asl_replay_state_t second;
|
||||||
|
amduat_asl_store_error_t err;
|
||||||
|
amduat_asl_log_position_t log_position = 0u;
|
||||||
|
int rc = 1;
|
||||||
|
|
||||||
|
err = amduat_asl_log_scan(store, &records, &record_count);
|
||||||
|
if (err != AMDUAT_ASL_STORE_OK) {
|
||||||
|
fprintf(stderr, "log scan failed: %d\n", err);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (record_count == 0u) {
|
||||||
|
fprintf(stderr, "log scan returned no records\n");
|
||||||
|
amduat_enc_asl_log_free(records, record_count);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_position = records[record_count - 1u].logseq;
|
||||||
|
|
||||||
|
if (!amduat_asl_replay_init(&first) ||
|
||||||
|
!amduat_asl_replay_init(&second)) {
|
||||||
|
fprintf(stderr, "replay init failed\n");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduat_asl_replay_apply_log(records, record_count, log_position, &first) ||
|
||||||
|
!amduat_asl_replay_apply_log(records, record_count, log_position, &second)) {
|
||||||
|
fprintf(stderr, "replay apply failed\n");
|
||||||
|
goto cleanup_states;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!conformance_replay_state_equal(&first, &second)) {
|
||||||
|
fprintf(stderr, "replay determinism mismatch\n");
|
||||||
|
goto cleanup_states;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = 0;
|
||||||
|
|
||||||
|
cleanup_states:
|
||||||
|
amduat_asl_replay_free(&first);
|
||||||
|
amduat_asl_replay_free(&second);
|
||||||
|
cleanup:
|
||||||
|
amduat_enc_asl_log_free(records, record_count);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
static bool conformance_init_asl_store_fs(const char *root,
|
static bool conformance_init_asl_store_fs(const char *root,
|
||||||
amduat_asl_store_t *out_store,
|
amduat_asl_store_t *out_store,
|
||||||
void **out_ctx) {
|
void **out_ctx) {
|
||||||
|
|
@ -757,6 +956,71 @@ static bool conformance_init_asl_store_index_fs(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool conformance_init_asl_store_minio(
|
||||||
|
const char *root,
|
||||||
|
amduat_asl_store_t *out_store,
|
||||||
|
void **out_ctx) {
|
||||||
|
amduat_asl_minio_ctx_t *ctx;
|
||||||
|
amduat_asl_store_minio_config_t config;
|
||||||
|
const char *timeout_env;
|
||||||
|
const char *retry_env;
|
||||||
|
const char *force_env;
|
||||||
|
const char *tls_env;
|
||||||
|
|
||||||
|
(void)root;
|
||||||
|
|
||||||
|
if (out_store == NULL || out_ctx == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = (amduat_asl_minio_ctx_t *)calloc(1, sizeof(*ctx));
|
||||||
|
if (ctx == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
amduat_asl_store_minio_config_defaults(&config);
|
||||||
|
config.endpoint = getenv("AMDUAT_MINIO_ENDPOINT");
|
||||||
|
config.bucket = getenv("AMDUAT_MINIO_BUCKET");
|
||||||
|
config.access_key = getenv("AMDUAT_MINIO_ACCESS_KEY");
|
||||||
|
config.secret_key = getenv("AMDUAT_MINIO_SECRET_KEY");
|
||||||
|
config.region = getenv("AMDUAT_MINIO_REGION") != NULL
|
||||||
|
? getenv("AMDUAT_MINIO_REGION")
|
||||||
|
: config.region;
|
||||||
|
config.prefix = getenv("AMDUAT_MINIO_PREFIX") != NULL
|
||||||
|
? getenv("AMDUAT_MINIO_PREFIX")
|
||||||
|
: config.prefix;
|
||||||
|
|
||||||
|
force_env = getenv("AMDUAT_MINIO_FORCE_PATH_STYLE");
|
||||||
|
if (force_env != NULL) {
|
||||||
|
config.force_path_style = conformance_env_truthy(force_env);
|
||||||
|
}
|
||||||
|
|
||||||
|
tls_env = getenv("AMDUAT_MINIO_TLS_VERIFY");
|
||||||
|
if (tls_env != NULL) {
|
||||||
|
config.tls_verify = conformance_env_truthy(tls_env);
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout_env = getenv("AMDUAT_MINIO_TIMEOUT_MS");
|
||||||
|
if (timeout_env != NULL) {
|
||||||
|
config.timeout_ms = (uint32_t)strtoul(timeout_env, NULL, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
retry_env = getenv("AMDUAT_MINIO_RETRY_MAX");
|
||||||
|
if (retry_env != NULL) {
|
||||||
|
config.retry_max = (uint32_t)strtoul(retry_env, NULL, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduat_asl_store_minio_init(&ctx->minio, config)) {
|
||||||
|
free(ctx);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
amduat_asl_store_init(out_store, config.store_config,
|
||||||
|
amduat_asl_store_minio_ops(), &ctx->minio);
|
||||||
|
*out_ctx = ctx;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void conformance_free_ctx(void *ctx) {
|
static void conformance_free_ctx(void *ctx) {
|
||||||
free(ctx);
|
free(ctx);
|
||||||
}
|
}
|
||||||
|
|
@ -766,7 +1030,8 @@ static int conformance_run_backend(
|
||||||
const char *workdir,
|
const char *workdir,
|
||||||
const amduat_conformance_fixture_t *fixtures,
|
const amduat_conformance_fixture_t *fixtures,
|
||||||
size_t fixtures_len,
|
size_t fixtures_len,
|
||||||
const char *fixture_root) {
|
const char *fixture_root,
|
||||||
|
char **out_ref_hexes) {
|
||||||
amduat_asl_store_t store;
|
amduat_asl_store_t store;
|
||||||
void *ctx = NULL;
|
void *ctx = NULL;
|
||||||
int rc = 1;
|
int rc = 1;
|
||||||
|
|
@ -778,7 +1043,8 @@ static int conformance_run_backend(
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conformance_test_put_get(&store, fixtures, fixtures_len, fixture_root) != 0 ||
|
if (conformance_test_put_get(&store, fixtures, fixtures_len, fixture_root,
|
||||||
|
out_ref_hexes) != 0 ||
|
||||||
conformance_test_put_idempotent(&store, &fixtures[0], fixture_root) != 0 ||
|
conformance_test_put_idempotent(&store, &fixtures[0], fixture_root) != 0 ||
|
||||||
conformance_test_get_not_found(&store) != 0) {
|
conformance_test_get_not_found(&store) != 0) {
|
||||||
rc = 1;
|
rc = 1;
|
||||||
|
|
@ -786,6 +1052,12 @@ static int conformance_run_backend(
|
||||||
rc = 0;
|
rc = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rc == 0 && strcmp(backend->name, "asl_store_index_fs") == 0) {
|
||||||
|
if (conformance_test_replay_determinism(&store) != 0) {
|
||||||
|
rc = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (backend->cleanup != NULL) {
|
if (backend->cleanup != NULL) {
|
||||||
backend->cleanup(ctx);
|
backend->cleanup(ctx);
|
||||||
}
|
}
|
||||||
|
|
@ -804,6 +1076,7 @@ int amduat_asl_store_conformance_run(
|
||||||
{"asl_store_fs", false, conformance_init_asl_store_fs, conformance_free_ctx},
|
{"asl_store_fs", false, conformance_init_asl_store_fs, conformance_free_ctx},
|
||||||
{"asl_store_index_fs", false, conformance_init_asl_store_index_fs,
|
{"asl_store_index_fs", false, conformance_init_asl_store_index_fs,
|
||||||
conformance_free_ctx},
|
conformance_free_ctx},
|
||||||
|
{"minio", false, conformance_init_asl_store_minio, conformance_free_ctx},
|
||||||
};
|
};
|
||||||
char *fixture_dir = NULL;
|
char *fixture_dir = NULL;
|
||||||
char *manifest_path = NULL;
|
char *manifest_path = NULL;
|
||||||
|
|
@ -813,6 +1086,8 @@ int amduat_asl_store_conformance_run(
|
||||||
size_t i;
|
size_t i;
|
||||||
size_t results_len = 0u;
|
size_t results_len = 0u;
|
||||||
amduat_conformance_backend_result_t *results = NULL;
|
amduat_conformance_backend_result_t *results = NULL;
|
||||||
|
char **baseline_refs = NULL;
|
||||||
|
size_t baseline_len = 0u;
|
||||||
|
|
||||||
if (out_results == NULL || out_len == NULL) {
|
if (out_results == NULL || out_len == NULL) {
|
||||||
return 1;
|
return 1;
|
||||||
|
|
@ -855,10 +1130,97 @@ int amduat_asl_store_conformance_run(
|
||||||
if (backend->optional && !include_optional) {
|
if (backend->optional && !include_optional) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (strcmp(backend->name, "minio") == 0) {
|
||||||
|
const char *enable_env = getenv("AMDUAT_CONFORMANCE_ENABLE_MINIO");
|
||||||
|
char *missing = NULL;
|
||||||
|
const char *endpoint_env = getenv("AMDUAT_MINIO_ENDPOINT");
|
||||||
|
|
||||||
|
if (!conformance_env_truthy(enable_env)) {
|
||||||
|
results = (amduat_conformance_backend_result_t *)realloc(
|
||||||
|
results, (results_len + 1u) * sizeof(*results));
|
||||||
|
if (results == NULL) {
|
||||||
|
conformance_fixture_list_free(fixtures, fixtures_len);
|
||||||
|
free(fixture_dir);
|
||||||
|
free(manifest_path);
|
||||||
|
free(artifact_dir);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
results[results_len].backend = backend->name;
|
||||||
|
results[results_len].status = AMDUAT_CONFORMANCE_STATUS_SKIP;
|
||||||
|
results_len++;
|
||||||
|
if (verbose) {
|
||||||
|
fprintf(stdout,
|
||||||
|
"asl minio SKIPPED: set AMDUAT_CONFORMANCE_ENABLE_MINIO=1\n");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!conformance_minio_missing_envs(&missing)) {
|
||||||
|
conformance_fixture_list_free(fixtures, fixtures_len);
|
||||||
|
free(fixture_dir);
|
||||||
|
free(manifest_path);
|
||||||
|
free(artifact_dir);
|
||||||
|
free(results);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (missing != NULL) {
|
||||||
|
results = (amduat_conformance_backend_result_t *)realloc(
|
||||||
|
results, (results_len + 1u) * sizeof(*results));
|
||||||
|
if (results == NULL) {
|
||||||
|
free(missing);
|
||||||
|
conformance_fixture_list_free(fixtures, fixtures_len);
|
||||||
|
free(fixture_dir);
|
||||||
|
free(manifest_path);
|
||||||
|
free(artifact_dir);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
results[results_len].backend = backend->name;
|
||||||
|
results[results_len].status = AMDUAT_CONFORMANCE_STATUS_SKIP;
|
||||||
|
results_len++;
|
||||||
|
if (verbose) {
|
||||||
|
fprintf(stdout, "asl minio SKIPPED: missing %s\n", missing);
|
||||||
|
}
|
||||||
|
free(missing);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endpoint_env != NULL && strncmp(endpoint_env, "https://", 8u) == 0) {
|
||||||
|
results = (amduat_conformance_backend_result_t *)realloc(
|
||||||
|
results, (results_len + 1u) * sizeof(*results));
|
||||||
|
if (results == NULL) {
|
||||||
|
conformance_fixture_list_free(fixtures, fixtures_len);
|
||||||
|
free(fixture_dir);
|
||||||
|
free(manifest_path);
|
||||||
|
free(artifact_dir);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
results[results_len].backend = backend->name;
|
||||||
|
results[results_len].status = AMDUAT_CONFORMANCE_STATUS_SKIP;
|
||||||
|
results_len++;
|
||||||
|
if (verbose) {
|
||||||
|
fprintf(stdout,
|
||||||
|
"asl minio SKIPPED: https endpoints not supported\n");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char **ref_hexes =
|
||||||
|
(char **)calloc(fixtures_len, sizeof(char *));
|
||||||
|
if (ref_hexes == NULL) {
|
||||||
|
fprintf(stderr, "out of memory\n");
|
||||||
|
conformance_fixture_list_free(fixtures, fixtures_len);
|
||||||
|
free(fixture_dir);
|
||||||
|
free(manifest_path);
|
||||||
|
free(artifact_dir);
|
||||||
|
free(results);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!conformance_prepare_workdir(workdir, backend->name, "asl",
|
if (!conformance_prepare_workdir(workdir, backend->name, "asl",
|
||||||
&backend_workdir)) {
|
&backend_workdir)) {
|
||||||
fprintf(stderr, "workdir prep failed for %s\n", backend->name);
|
fprintf(stderr, "workdir prep failed for %s\n", backend->name);
|
||||||
|
conformance_free_ref_hexes(ref_hexes, fixtures_len);
|
||||||
conformance_fixture_list_free(fixtures, fixtures_len);
|
conformance_fixture_list_free(fixtures, fixtures_len);
|
||||||
free(fixture_dir);
|
free(fixture_dir);
|
||||||
free(manifest_path);
|
free(manifest_path);
|
||||||
|
|
@ -881,11 +1243,44 @@ int amduat_asl_store_conformance_run(
|
||||||
results[results_len].backend = backend->name;
|
results[results_len].backend = backend->name;
|
||||||
results[results_len].status =
|
results[results_len].status =
|
||||||
conformance_run_backend(backend, backend_workdir, fixtures,
|
conformance_run_backend(backend, backend_workdir, fixtures,
|
||||||
fixtures_len, artifact_dir);
|
fixtures_len, artifact_dir, ref_hexes);
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
fprintf(stdout, "asl %s %s\n", backend->name,
|
const char *status =
|
||||||
results[results_len].status == 0 ? "PASS" : "FAIL");
|
results[results_len].status == AMDUAT_CONFORMANCE_STATUS_PASS
|
||||||
|
? "PASS"
|
||||||
|
: (results[results_len].status == AMDUAT_CONFORMANCE_STATUS_SKIP
|
||||||
|
? "SKIPPED"
|
||||||
|
: "FAIL");
|
||||||
|
fprintf(stdout, "asl %s %s\n", backend->name, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (results[results_len].status ==
|
||||||
|
AMDUAT_CONFORMANCE_STATUS_PASS) {
|
||||||
|
if (baseline_refs == NULL) {
|
||||||
|
baseline_refs = ref_hexes;
|
||||||
|
baseline_len = fixtures_len;
|
||||||
|
ref_hexes = NULL;
|
||||||
|
} else {
|
||||||
|
size_t j;
|
||||||
|
if (baseline_len != fixtures_len) {
|
||||||
|
fprintf(stderr, "cross-backend ref length mismatch\n");
|
||||||
|
results[results_len].status = 1;
|
||||||
|
} else {
|
||||||
|
for (j = 0; j < fixtures_len; ++j) {
|
||||||
|
if (baseline_refs[j] == NULL || ref_hexes[j] == NULL ||
|
||||||
|
strcmp(baseline_refs[j], ref_hexes[j]) != 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"cross-backend ref mismatch for fixture %s\n",
|
||||||
|
fixtures[j].id);
|
||||||
|
results[results_len].status = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conformance_free_ref_hexes(ref_hexes, fixtures_len);
|
||||||
results_len++;
|
results_len++;
|
||||||
free(backend_workdir);
|
free(backend_workdir);
|
||||||
}
|
}
|
||||||
|
|
@ -894,12 +1289,13 @@ int amduat_asl_store_conformance_run(
|
||||||
free(fixture_dir);
|
free(fixture_dir);
|
||||||
free(manifest_path);
|
free(manifest_path);
|
||||||
free(artifact_dir);
|
free(artifact_dir);
|
||||||
|
conformance_free_ref_hexes(baseline_refs, baseline_len);
|
||||||
|
|
||||||
*out_results = results;
|
*out_results = results;
|
||||||
*out_len = results_len;
|
*out_len = results_len;
|
||||||
|
|
||||||
for (i = 0; i < results_len; ++i) {
|
for (i = 0; i < results_len; ++i) {
|
||||||
if (results[i].status != 0) {
|
if (results[i].status == AMDUAT_CONFORMANCE_STATUS_FAIL) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ int main(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < results_len; ++i) {
|
for (i = 0; i < results_len; ++i) {
|
||||||
if (results[i].status != 0) {
|
if (results[i].status == AMDUAT_CONFORMANCE_STATUS_FAIL) {
|
||||||
rc = 1;
|
rc = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,12 @@ typedef struct {
|
||||||
int status;
|
int status;
|
||||||
} amduat_conformance_backend_result_t;
|
} amduat_conformance_backend_result_t;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
AMDUAT_CONFORMANCE_STATUS_PASS = 0,
|
||||||
|
AMDUAT_CONFORMANCE_STATUS_FAIL = 1,
|
||||||
|
AMDUAT_CONFORMANCE_STATUS_SKIP = 2
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ int main(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < results_len; ++i) {
|
for (i = 0; i < results_len; ++i) {
|
||||||
if (results[i].status != 0) {
|
if (results[i].status == AMDUAT_CONFORMANCE_STATUS_FAIL) {
|
||||||
rc = 1;
|
rc = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
26
tests/fixtures/conformance/tgk_edges/manifest.json
vendored
Normal file
26
tests/fixtures/conformance/tgk_edges/manifest.json
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"schema": "amduat.conformance.tgk_edges.v0",
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"id": "e1",
|
||||||
|
"type": 16,
|
||||||
|
"from": ["00010a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a"],
|
||||||
|
"to": ["00010b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"],
|
||||||
|
"payload": "00011a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e2",
|
||||||
|
"type": 16,
|
||||||
|
"from": ["00010a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a"],
|
||||||
|
"to": ["00010c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"],
|
||||||
|
"payload": "00011b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "e3",
|
||||||
|
"type": 32,
|
||||||
|
"from": ["00010b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"],
|
||||||
|
"to": ["00010a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a"],
|
||||||
|
"payload": "00011c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue