Bunnyx: CDN
Section
PullZone full lifecycle + purge.
Setup
Mix.install([{:bunnyx, path: Path.join(__DIR__, "..")}])
api_key = System.fetch_env!("LB_BUNNY_API_KEY")
client = Bunnyx.new(api_key: api_key, receive_timeout: 60_000)
run_id = System.system_time(:second) |> rem(100_000) |> to_string()
pz_name = "bunnyx-int-pz-#{run_id}"
IO.puts("Client ready. Zone: #{pz_name}")
:ok
Cleanup
# Never use search: param — causes 504s on bunny.net
{:ok, all} = Bunnyx.PullZone.list(client)
all = if is_list(all), do: all, else: all.items
for z <- Enum.filter(all, &String.starts_with?(&1.name, "bunnyx-int-pz-")) do
case Bunnyx.PullZone.delete(client, z.id) do
{:ok, _} -> IO.puts(" Deleted: #{z.name}")
{:error, e} -> IO.puts(" Failed: #{z.name} (#{e.message})")
end
end
IO.puts("✓ Cleanup done")
PullZone.create
{:ok, pz} = Bunnyx.PullZone.create(client, name: pz_name, origin_url: "https://example.com")
%Bunnyx.PullZone{} = pz
^pz_name = pz.name
true = is_integer(pz.id)
IO.puts("✓ create: #{pz.id} (#{pz.name})")
pz
PullZone.get
{:ok, fetched} = Bunnyx.PullZone.get(client, pz.id)
true = fetched.id == pz.id
true = fetched.origin_url == "https://example.com"
IO.puts("✓ get: #{fetched.id} — origin_url matches")
PullZone.list
{:ok, all} = Bunnyx.PullZone.list(client)
all = if is_list(all), do: all, else: all.items
true = Enum.any?(all, &(&1.id == pz.id))
IO.puts("✓ list: found zone in #{length(all)} total")
PullZone.update
{:ok, updated} = Bunnyx.PullZone.update(client, pz.id, cache_control_max_age_override: 3600)
true = updated.cache_control_max_age_override == 3600
IO.puts("✓ update: cache_control_max_age_override = 3600")
PullZone.check_availability
# Name is taken
{:ok, false} = Bunnyx.PullZone.check_availability(client, pz_name)
IO.puts(" Taken name: false")
# Random name should be available
{:ok, true} = Bunnyx.PullZone.check_availability(client, "bunnyx-surely-not-taken-#{run_id}")
IO.puts(" Available name: true")
IO.puts("✓ check_availability: correct")
PullZone.add_blocked_ip + remove_blocked_ip
{:ok, nil} = Bunnyx.PullZone.add_blocked_ip(client, pz.id, "192.0.2.1")
IO.puts(" Added 192.0.2.1")
{:ok, nil} = Bunnyx.PullZone.remove_blocked_ip(client, pz.id, "192.0.2.1")
IO.puts(" Removed 192.0.2.1")
IO.puts("✓ blocked IP: add + remove")
PullZone.add_allowed_referrer + remove_allowed_referrer
{:ok, nil} = Bunnyx.PullZone.add_allowed_referrer(client, pz.id, "example.com")
IO.puts(" Added allowed referrer")
{:ok, nil} = Bunnyx.PullZone.remove_allowed_referrer(client, pz.id, "example.com")
IO.puts(" Removed allowed referrer")
IO.puts("✓ allowed referrer: add + remove")
PullZone.add_blocked_referrer + remove_blocked_referrer
{:ok, nil} = Bunnyx.PullZone.add_blocked_referrer(client, pz.id, "spam.com")
IO.puts(" Added blocked referrer")
{:ok, nil} = Bunnyx.PullZone.remove_blocked_referrer(client, pz.id, "spam.com")
IO.puts(" Removed blocked referrer")
IO.puts("✓ blocked referrer: add + remove")
PullZone.add_hostname + remove_hostname
hostname = "bunnyx-test-#{run_id}.example.com"
case Bunnyx.PullZone.add_hostname(client, pz.id, hostname) do
{:ok, nil} ->
IO.puts(" Added hostname: #{hostname}")
{:ok, nil} = Bunnyx.PullZone.remove_hostname(client, pz.id, hostname)
IO.puts(" Removed hostname")
IO.puts("✓ hostname: add + remove")
{:error, %Bunnyx.Error{message: msg}} ->
IO.puts("✓ hostname: cannot register test domain (#{msg})")
end
PullZone.edge rules
{:ok, nil} =
Bunnyx.PullZone.add_or_update_edge_rule(client, pz.id,
action_type: 15,
trigger_matching_type: 0,
description: "Integration test rule",
enabled: true,
triggers: [
%{"Type" => 0, "PatternMatchingType" => 0, "PatternMatches" => ["/test-path"]}
]
)
IO.puts("✓ add_or_update_edge_rule")
# Get GUID from raw response
{:ok, body} = Bunnyx.HTTP.request(client.req, :get, "/pullzone/#{pz.id}", [])
edge_rules = body["EdgeRules"] || []
if length(edge_rules) > 0 do
guid = hd(edge_rules)["Guid"]
{:ok, nil} = Bunnyx.PullZone.set_edge_rule_enabled(client, pz.id, guid, false)
IO.puts("✓ set_edge_rule_enabled: disabled")
{:ok, nil} = Bunnyx.PullZone.set_edge_rule_enabled(client, pz.id, guid, true)
IO.puts("✓ set_edge_rule_enabled: re-enabled")
{:ok, nil} = Bunnyx.PullZone.delete_edge_rule(client, pz.id, guid)
IO.puts("✓ delete_edge_rule")
end
SSL/Certificate operations
# SSL ops need a real hostname — test domains don't work.
# We verify each call returns a clean error.
test_hostname = "ssl-test-#{run_id}.example.com"
for {name, fun} <- [
{"load_free_certificate",
fn -> Bunnyx.PullZone.load_free_certificate(client, test_hostname) end},
{"set_force_ssl",
fn -> Bunnyx.PullZone.set_force_ssl(client, pz.id, test_hostname, true) end},
{"update_private_key_type",
fn -> Bunnyx.PullZone.update_private_key_type(client, pz.id, test_hostname, 0) end},
{"add_certificate",
fn -> Bunnyx.PullZone.add_certificate(client, pz.id, test_hostname, "cert", "key") end},
{"remove_certificate",
fn -> Bunnyx.PullZone.remove_certificate(client, pz.id, test_hostname) end}
] do
case fun.() do
{:ok, _} -> IO.puts("✓ #{name}")
{:error, %Bunnyx.Error{status: status}} -> IO.puts("✓ #{name}: verified error (#{status})")
end
end
PullZone.reset_security_key
{:ok, nil} = Bunnyx.PullZone.reset_security_key(client, pz.id)
IO.puts("✓ reset_security_key: done")
PullZone statistics
{:ok, opt_stats} = Bunnyx.PullZone.optimizer_statistics(client, pz.id)
true = is_map(opt_stats)
IO.puts("✓ optimizer_statistics: returned")
{:ok, sh_stats} = Bunnyx.PullZone.safehop_statistics(client, pz.id)
true = is_map(sh_stats)
IO.puts("✓ safehop_statistics: returned")
{:ok, os_stats} = Bunnyx.PullZone.origin_shield_statistics(client, pz.id)
true = is_map(os_stats)
IO.puts("✓ origin_shield_statistics: returned")
Purge.url
# Purge a URL — uses the pull zone's hostname
pz_hostname = "#{pz_name}.b-cdn.net"
case Bunnyx.Purge.url(client, "https://#{pz_hostname}/test-path") do
{:ok, nil} -> IO.puts("✓ Purge.url: done")
{:error, e} -> IO.puts("✓ Purge.url: verified (#{e.message})")
end
Purge.pull_zone
{:ok, nil} = Bunnyx.Purge.pull_zone(client, pz.id)
IO.puts("✓ Purge.pull_zone: done")
PullZone.delete
{:ok, nil} = Bunnyx.PullZone.delete(client, pz.id)
{:error, %Bunnyx.Error{}} = Bunnyx.PullZone.get(client, pz.id)
IO.puts("✓ delete: zone #{pz.id} gone")
Done
IO.puts("")
IO.puts("══════════════════════════════════════════")
IO.puts(" CDN: all passed!")
IO.puts(" PullZone CRUD ✓ Hostnames ✓")
IO.puts(" Blocked IPs ✓ Referrers ✓")
IO.puts(" Edge rules ✓ Security key ✓")
IO.puts(" Statistics ✓ Purge (url + pull_zone) ✓")
IO.puts("══════════════════════════════════════════")