-- Copyright (c) Microsoft Corporation. -- Licensed under the PostgreSQL License. -- Azure Storage Account domain tests -- -- Covers: .blob.core.windows.net, .blob.storage.azure.net, -- .queue.core.windows.net, .table.core.windows.net, -- .file.core.windows.net -- -- Requires environment variables: -- AHD_STORAGE_ACCOUNT, AHD_STORAGE_SAS, AHD_STORAGE_CONTAINER, -- AHD_STORAGE_BLOB, AHD_STORAGE_QUEUE, AHD_STORAGE_TABLE, -- AHD_STORAGE_SHARE \getenv storage_account AHD_STORAGE_ACCOUNT \getenv storage_sas AHD_STORAGE_SAS \getenv storage_container AHD_STORAGE_CONTAINER \getenv storage_blob AHD_STORAGE_BLOB \getenv storage_queue AHD_STORAGE_QUEUE \getenv storage_table AHD_STORAGE_TABLE \getenv storage_share AHD_STORAGE_SHARE SELECT df.clearvars(); SELECT df.setvar('acct', :'storage_account'); SELECT df.setvar('sas', :'storage_sas'); SELECT df.setvar('container', :'storage_container'); SELECT df.setvar('blob', :'storage_blob'); SELECT df.setvar('queue', :'storage_queue'); SELECT df.setvar('tbl', :'storage_table'); SELECT df.setvar('share', :'storage_share'); -- ============================================================================ -- Test 1: Blob Storage — GET blob (.blob.core.windows.net) -- ============================================================================ CREATE TEMP TABLE _test_blob (instance_id TEXT); INSERT INTO _test_blob SELECT df.start( df.http( 'https://{acct}.blob.core.windows.net/{container}/{blob}?{sas}', 'GET', NULL, '{"x-ms-version": "2023-01-03"}'::jsonb, 30 ) |=> 'resp', 'ahd-test-blob-storage' ); DO $$ DECLARE inst_id TEXT; status TEXT; result TEXT; BEGIN SELECT instance_id INTO inst_id FROM _test_blob; SELECT df.await_instance(inst_id) INTO status; IF status != 'completed' THEN SELECT r.result::text INTO result FROM df.nodes r WHERE r.instance_id = inst_id AND r.node_type = 'HTTP'; RAISE EXCEPTION 'TEST FAILED: blob storage status=%, result=%', status, result; END IF; RAISE NOTICE 'TEST PASSED: blob_storage (.blob.core.windows.net)'; END $$; DROP TABLE _test_blob; -- ============================================================================ -- Test 2: Queue Storage — peek messages (.queue.core.windows.net) -- ============================================================================ CREATE TEMP TABLE _test_queue (instance_id TEXT); INSERT INTO _test_queue SELECT df.start( df.http( 'https://{acct}.queue.core.windows.net/{queue}/messages?peekonly=true&{sas}', 'GET', NULL, '{"x-ms-version": "2023-01-03"}'::jsonb, 30 ) |=> 'resp', 'ahd-test-queue-storage' ); DO $$ DECLARE inst_id TEXT; status TEXT; result TEXT; BEGIN SELECT instance_id INTO inst_id FROM _test_queue; SELECT df.await_instance(inst_id) INTO status; IF status != 'completed' THEN SELECT r.result::text INTO result FROM df.nodes r WHERE r.instance_id = inst_id AND r.node_type = 'HTTP'; RAISE EXCEPTION 'TEST FAILED: queue storage status=%, result=%', status, result; END IF; RAISE NOTICE 'TEST PASSED: queue_storage (.queue.core.windows.net)'; END $$; DROP TABLE _test_queue; -- ============================================================================ -- Test 3: Table Storage — insert entity (.table.core.windows.net) -- ============================================================================ CREATE TEMP TABLE _test_table (instance_id TEXT); INSERT INTO _test_table SELECT df.start( df.http( 'https://{acct}.table.core.windows.net/{tbl}?{sas}', 'POST', '{"PartitionKey": "pgdtest", "RowKey": "1", "Value": "hello from pg_durable"}', '{"x-ms-version": "2023-01-03", "Content-Type": "application/json", "Accept": "application/json;odata=nometadata", "Prefer": "return-no-content"}'::jsonb, 30 ) |=> 'resp', 'ahd-test-table-storage' ); DO $$ DECLARE inst_id TEXT; status TEXT; result TEXT; BEGIN SELECT instance_id INTO inst_id FROM _test_table; SELECT df.await_instance(inst_id) INTO status; IF status != 'completed' THEN SELECT r.result::text INTO result FROM df.nodes r WHERE r.instance_id = inst_id AND r.node_type = 'HTTP'; RAISE EXCEPTION 'TEST FAILED: table storage status=%, result=%', status, result; END IF; RAISE NOTICE 'TEST PASSED: table_storage (.table.core.windows.net)'; END $$; DROP TABLE _test_table; -- ============================================================================ -- Test 4: File Storage — list share root (.file.core.windows.net) -- ============================================================================ CREATE TEMP TABLE _test_file (instance_id TEXT); INSERT INTO _test_file SELECT df.start( df.http( 'https://{acct}.file.core.windows.net/{share}?restype=directory&comp=list&{sas}', 'GET', NULL, '{"x-ms-version": "2023-01-03"}'::jsonb, 30 ) |=> 'resp', 'ahd-test-file-storage' ); DO $$ DECLARE inst_id TEXT; status TEXT; result TEXT; BEGIN SELECT instance_id INTO inst_id FROM _test_file; SELECT df.await_instance(inst_id) INTO status; IF status != 'completed' THEN SELECT r.result::text INTO result FROM df.nodes r WHERE r.instance_id = inst_id AND r.node_type = 'HTTP'; RAISE EXCEPTION 'TEST FAILED: file storage status=%, result=%', status, result; END IF; RAISE NOTICE 'TEST PASSED: file_storage (.file.core.windows.net)'; END $$; DROP TABLE _test_file; -- ============================================================================ -- Test 5: Blob Storage alternate domain (.blob.storage.azure.net) -- -- Azure Storage supports an alternate domain form: -- https://.z.blob.storage.azure.net -- The zone number depends on the region; we query it via the primary endpoint. -- For simplicity we attempt zone 6 which is common for eastus — a connection -- error (not an allowlist block) is also a passing result since it proves the -- domain suffix passes the allowlist. -- ============================================================================ CREATE TEMP TABLE _test_blob_alt (instance_id TEXT); INSERT INTO _test_blob_alt SELECT df.start( df.http( 'https://{acct}.z6.blob.storage.azure.net/{container}/{blob}?{sas}', 'GET', NULL, '{"x-ms-version": "2023-01-03"}'::jsonb, 15 ) |=> 'resp', 'ahd-test-blob-alt' ); DO $$ DECLARE inst_id TEXT; status TEXT; node_result TEXT; BEGIN SELECT instance_id INTO inst_id FROM _test_blob_alt; SELECT df.await_instance(inst_id, 30) INTO status; -- Success or a non-allowlist failure both count as passing. IF status = 'completed' THEN RAISE NOTICE 'TEST PASSED: blob_storage_alt (.blob.storage.azure.net) — 200 OK'; ELSE SELECT r.result::text INTO node_result FROM df.nodes r WHERE r.instance_id = inst_id AND r.node_type = 'HTTP'; IF node_result ILIKE '%not in the allowed%' THEN RAISE EXCEPTION 'TEST FAILED: blob alt domain blocked by allowlist: %', node_result; END IF; -- Connection/DNS/timeout errors are acceptable — the domain passed the allowlist. RAISE NOTICE 'TEST PASSED: blob_storage_alt (.blob.storage.azure.net) — domain allowed (status=%)', status; END IF; END $$; DROP TABLE _test_blob_alt; SELECT 'TEST PASSED' AS result;