Skip to content

Commit ee6e716

Browse files
tests: out_stackdriver: cover external_account credential parsing
Add runtime tests for the new external_account (Workload Identity Federation) credential handling in out_stackdriver: - external_account_with_impersonation - external_account_no_impersonation - external_account_url_source - external_account_rejects_executable_source - external_account_rejects_aws_source - external_account_rejects_certificate_source - external_account_rejects_missing_audience - external_account_rejects_missing_subject_token_type - external_account_rejects_workforce_mismatch Each test boots a fluent-bit instance with a fixture credentials JSON under tests/runtime/data/stackdriver/, attaches a formatter test-mode callback that flips a flag on first format, ingests one record, and asserts whether the formatter ran. Accepted shapes must reach the formatter (init succeeded, output enabled). Rejected shapes must not: flb_start must fail and the formatter callback must never fire. URL-sourced credential_source is verified at init time only — token acquisition is lazy (first flush) and the runtime test does not stand up a local HTTP server. End-to-end coverage of the URL path (real HTTP GET, STS exchange, log delivery) is exercised by an out-of-tree smoke harness and is not part of ctest. Signed-off-by: Cagatay Gurturk <963018+cagataygurturk@users.noreply.github.com>
1 parent da7f6af commit ee6e716

10 files changed

Lines changed: 295 additions & 0 deletions
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"type": "external_account",
3+
"audience": "//iam.googleapis.com/projects/123456789/locations/global/workloadIdentityPools/test-pool/providers/test-provider",
4+
"subject_token_type": "urn:ietf:params:aws:token-type:aws4_request",
5+
"token_url": "https://sts.googleapis.com/v1/token",
6+
"credential_source": {
7+
"environment_id": "aws1",
8+
"region_url": "http://169.254.169.254/latest/meta-data/placement/availability-zone",
9+
"regional_cred_verification_url": "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15"
10+
}
11+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"type": "external_account",
3+
"audience": "//iam.googleapis.com/projects/123456789/locations/global/workloadIdentityPools/test-pool/providers/test-provider",
4+
"subject_token_type": "urn:ietf:params:oauth:token-type:mtls",
5+
"token_url": "https://sts.googleapis.com/v1/token",
6+
"credential_source": {
7+
"certificate": {
8+
"use_default_certificate_config": true,
9+
"trust_chain_path": "/etc/ssl/certs/trust-chain.pem"
10+
}
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"type": "external_account",
3+
"audience": "//iam.googleapis.com/projects/123456789/locations/global/workloadIdentityPools/test-pool/providers/test-provider",
4+
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
5+
"token_url": "https://sts.googleapis.com/v1/token",
6+
"credential_source": {
7+
"executable": {
8+
"command": "/path/to/helper --json",
9+
"timeout_millis": 5000
10+
}
11+
}
12+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"type": "external_account",
3+
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
4+
"token_url": "https://sts.googleapis.com/v1/token",
5+
"credential_source": {
6+
"file": "/var/run/service-account/token",
7+
"format": {
8+
"type": "text"
9+
}
10+
}
11+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"type": "external_account",
3+
"audience": "//iam.googleapis.com/projects/123456789/locations/global/workloadIdentityPools/test-pool/providers/test-provider",
4+
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
5+
"token_url": "https://sts.googleapis.com/v1/token",
6+
"credential_source": {
7+
"file": "/var/run/service-account/token",
8+
"format": {
9+
"type": "text"
10+
}
11+
}
12+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"type": "external_account",
3+
"audience": "//iam.googleapis.com/projects/123456789/locations/global/workloadIdentityPools/test-pool/providers/test-provider",
4+
"token_url": "https://sts.googleapis.com/v1/token",
5+
"credential_source": {
6+
"file": "/var/run/service-account/token",
7+
"format": {
8+
"type": "text"
9+
}
10+
}
11+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"type": "external_account",
3+
"audience": "//iam.googleapis.com/projects/123456789/locations/global/workloadIdentityPools/test-pool/providers/test-provider",
4+
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
5+
"token_url": "https://sts.googleapis.com/v1/token",
6+
"credential_source": {
7+
"url": "http://localhost:5000/token",
8+
"format": {
9+
"type": "json",
10+
"subject_token_field_name": "id_token"
11+
}
12+
}
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"type": "external_account",
3+
"audience": "//iam.googleapis.com/projects/123456789/locations/global/workloadIdentityPools/test-pool/providers/test-provider",
4+
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
5+
"token_url": "https://sts.googleapis.com/v1/token",
6+
"workforce_pool_user_project": "billing-project",
7+
"credential_source": {
8+
"file": "/var/run/service-account/token",
9+
"format": {
10+
"type": "text"
11+
}
12+
}
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"type": "external_account",
3+
"audience": "//iam.googleapis.com/projects/123456789/locations/global/workloadIdentityPools/test-pool/providers/test-provider",
4+
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
5+
"token_url": "https://sts.googleapis.com/v1/token",
6+
"credential_source": {
7+
"file": "/var/run/service-account/token",
8+
"format": {
9+
"type": "text"
10+
}
11+
},
12+
"service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/logwriter@fluent-bit.iam.gserviceaccount.com:generateAccessToken"
13+
}

tests/runtime/out_stackdriver.c

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,26 @@
2929
FLB_TESTS_DATA_PATH "/data/stackdriver/stackdriver-credentials.json"
3030
#define STACKDRIVER_DATA_PATH "/data/stackdriver"
3131

32+
/* external_account / Workload Identity Federation fixtures */
33+
#define EXT_ACCT_CREDS_FILE \
34+
FLB_TESTS_DATA_PATH "/data/stackdriver/stackdriver-credentials-external-account.json"
35+
#define EXT_ACCT_CREDS_NO_IMP \
36+
FLB_TESTS_DATA_PATH "/data/stackdriver/stackdriver-credentials-external-account-no-impersonation.json"
37+
#define EXT_ACCT_CREDS_URL \
38+
FLB_TESTS_DATA_PATH "/data/stackdriver/stackdriver-credentials-external-account-url.json"
39+
#define EXT_ACCT_CREDS_EXEC \
40+
FLB_TESTS_DATA_PATH "/data/stackdriver/stackdriver-credentials-external-account-exec.json"
41+
#define EXT_ACCT_CREDS_NO_AUDIENCE \
42+
FLB_TESTS_DATA_PATH "/data/stackdriver/stackdriver-credentials-external-account-no-audience.json"
43+
#define EXT_ACCT_CREDS_NO_SUBJECT_TYPE \
44+
FLB_TESTS_DATA_PATH "/data/stackdriver/stackdriver-credentials-external-account-no-subject-token-type.json"
45+
#define EXT_ACCT_CREDS_WORKFORCE_MISMATCH \
46+
FLB_TESTS_DATA_PATH "/data/stackdriver/stackdriver-credentials-external-account-workforce-mismatch.json"
47+
#define EXT_ACCT_CREDS_AWS \
48+
FLB_TESTS_DATA_PATH "/data/stackdriver/stackdriver-credentials-external-account-aws.json"
49+
#define EXT_ACCT_CREDS_CERT \
50+
FLB_TESTS_DATA_PATH "/data/stackdriver/stackdriver-credentials-external-account-cert.json"
51+
3252
/* JSON payload example */
3353
#include "data/stackdriver/json.h"
3454
#include "data/stackdriver/stackdriver_test_operation.h"
@@ -6537,6 +6557,162 @@ void flb_test_non_scalar_payload_with_residual_fields()
65376557
flb_destroy(ctx);
65386558
}
65396559

6560+
/*
6561+
* external_account / Workload Identity Federation parser tests.
6562+
*
6563+
* These run the plugin with no network access, so they cannot exercise
6564+
* the STS/IAM round-trip. Instead they verify that the credentials file
6565+
* parser handles each variant correctly: accepted variants leave the
6566+
* plugin reachable through the format callback; rejected variants fail
6567+
* init so the format callback never runs.
6568+
*/
6569+
static int ext_acct_format_called;
6570+
6571+
static void cb_ext_acct_mark(void *ctx, int ffd,
6572+
int res_ret, void *res_data, size_t res_size,
6573+
void *data)
6574+
{
6575+
ext_acct_format_called = FLB_TRUE;
6576+
flb_sds_destroy(res_data);
6577+
}
6578+
6579+
static int run_external_account_init(const char *creds_path)
6580+
{
6581+
int ret;
6582+
int in_ffd;
6583+
int out_ffd;
6584+
flb_ctx_t *ctx;
6585+
static const char dummy_record[] = "[1448403340, {\"k\":\"v\"}]";
6586+
6587+
ext_acct_format_called = FLB_FALSE;
6588+
6589+
ctx = flb_create();
6590+
flb_service_set(ctx, "flush", "1", "grace", "1", NULL);
6591+
6592+
in_ffd = flb_input(ctx, (char *) "lib", NULL);
6593+
flb_input_set(ctx, in_ffd, "tag", "test", NULL);
6594+
6595+
out_ffd = flb_output(ctx, (char *) "stackdriver", NULL);
6596+
flb_output_set(ctx, out_ffd,
6597+
"match", "test",
6598+
"google_service_credentials", creds_path,
6599+
"resource", "global",
6600+
"export_to_project_id", "fluent-bit-test-project",
6601+
NULL);
6602+
6603+
flb_output_set_test(ctx, out_ffd, "formatter",
6604+
cb_ext_acct_mark, NULL, NULL);
6605+
6606+
ret = flb_start(ctx);
6607+
if (ret == 0) {
6608+
flb_lib_push(ctx, in_ffd, (char *) dummy_record,
6609+
sizeof(dummy_record) - 1);
6610+
sleep(1);
6611+
flb_stop(ctx);
6612+
}
6613+
6614+
flb_destroy(ctx);
6615+
return ret;
6616+
}
6617+
6618+
void flb_test_external_account_with_impersonation()
6619+
{
6620+
int ret;
6621+
6622+
ret = run_external_account_init(EXT_ACCT_CREDS_FILE);
6623+
TEST_CHECK(ret == 0);
6624+
TEST_CHECK(ext_acct_format_called == FLB_TRUE);
6625+
}
6626+
6627+
void flb_test_external_account_no_impersonation()
6628+
{
6629+
int ret;
6630+
6631+
ret = run_external_account_init(EXT_ACCT_CREDS_NO_IMP);
6632+
TEST_CHECK(ret == 0);
6633+
TEST_CHECK(ext_acct_format_called == FLB_TRUE);
6634+
}
6635+
6636+
void flb_test_external_account_url_source()
6637+
{
6638+
int ret;
6639+
6640+
/*
6641+
* URL-sourced credential_source must initialize successfully. Token
6642+
* acquisition is lazy (first flush), and this test does not stand up a
6643+
* local HTTP server — it only verifies that init succeeds and the
6644+
* formatter callback fires for the first record. End-to-end coverage
6645+
* (real HTTP GET, STS exchange, log delivery) is exercised by the
6646+
* out-of-tree smoke harness in tmp/run_url_test.sh.
6647+
*/
6648+
ret = run_external_account_init(EXT_ACCT_CREDS_URL);
6649+
TEST_CHECK(ret == 0);
6650+
TEST_CHECK(ext_acct_format_called == FLB_TRUE);
6651+
}
6652+
6653+
void flb_test_external_account_rejects_executable_source()
6654+
{
6655+
int ret;
6656+
6657+
/*
6658+
* Executable credential_source is unsupported and must be rejected at
6659+
* init time, not silently accepted with the output disabled.
6660+
*/
6661+
ret = run_external_account_init(EXT_ACCT_CREDS_EXEC);
6662+
TEST_CHECK(ret != 0);
6663+
TEST_CHECK(ext_acct_format_called == FLB_FALSE);
6664+
}
6665+
6666+
void flb_test_external_account_rejects_aws_source()
6667+
{
6668+
int ret;
6669+
6670+
ret = run_external_account_init(EXT_ACCT_CREDS_AWS);
6671+
TEST_CHECK(ret != 0);
6672+
TEST_CHECK(ext_acct_format_called == FLB_FALSE);
6673+
}
6674+
6675+
void flb_test_external_account_rejects_certificate_source()
6676+
{
6677+
int ret;
6678+
6679+
ret = run_external_account_init(EXT_ACCT_CREDS_CERT);
6680+
TEST_CHECK(ret != 0);
6681+
TEST_CHECK(ext_acct_format_called == FLB_FALSE);
6682+
}
6683+
6684+
void flb_test_external_account_rejects_missing_audience()
6685+
{
6686+
int ret;
6687+
6688+
ret = run_external_account_init(EXT_ACCT_CREDS_NO_AUDIENCE);
6689+
TEST_CHECK(ret != 0);
6690+
TEST_CHECK(ext_acct_format_called == FLB_FALSE);
6691+
}
6692+
6693+
void flb_test_external_account_rejects_missing_subject_token_type()
6694+
{
6695+
int ret;
6696+
6697+
ret = run_external_account_init(EXT_ACCT_CREDS_NO_SUBJECT_TYPE);
6698+
TEST_CHECK(ret != 0);
6699+
TEST_CHECK(ext_acct_format_called == FLB_FALSE);
6700+
}
6701+
6702+
void flb_test_external_account_rejects_workforce_mismatch()
6703+
{
6704+
int ret;
6705+
6706+
/*
6707+
* workforce_pool_user_project is only valid with workforce-pool
6708+
* audiences. Setting it on a workload-pool audience must be a hard
6709+
* config error, not a silent no-op.
6710+
*/
6711+
ret = run_external_account_init(EXT_ACCT_CREDS_WORKFORCE_MISMATCH);
6712+
TEST_CHECK(ret != 0);
6713+
TEST_CHECK(ext_acct_format_called == FLB_FALSE);
6714+
}
6715+
65406716
/* Test list */
65416717
TEST_LIST = {
65426718
{"severity_multi_entries", flb_test_multi_entries_severity },
@@ -6671,5 +6847,16 @@ TEST_LIST = {
66716847
{"string_text_payload_with_mismatched_text_payload_key", flb_test_string_text_payload_with_mismatched_text_payload_key},
66726848
{"string_text_payload_with_residual_fields", flb_test_string_text_payload_with_residual_fields},
66736849
{"non_scalar_payload_with_residual_fields", flb_test_non_scalar_payload_with_residual_fields},
6850+
6851+
/* external_account / Workload Identity Federation */
6852+
{"external_account_with_impersonation", flb_test_external_account_with_impersonation},
6853+
{"external_account_no_impersonation", flb_test_external_account_no_impersonation},
6854+
{"external_account_url_source", flb_test_external_account_url_source},
6855+
{"external_account_rejects_executable_source", flb_test_external_account_rejects_executable_source},
6856+
{"external_account_rejects_aws_source", flb_test_external_account_rejects_aws_source},
6857+
{"external_account_rejects_certificate_source", flb_test_external_account_rejects_certificate_source},
6858+
{"external_account_rejects_missing_audience", flb_test_external_account_rejects_missing_audience},
6859+
{"external_account_rejects_missing_subject_token_type", flb_test_external_account_rejects_missing_subject_token_type},
6860+
{"external_account_rejects_workforce_mismatch", flb_test_external_account_rejects_workforce_mismatch},
66746861
{NULL, NULL}
66756862
};

0 commit comments

Comments
 (0)