ProjectsWorker mirrors DO Projects to cloud_projects table in a two-pass sweep: upsert projects, then walk each project's resource membership (list_project_resources) and update cloud_resources.cloud_project_id + tenant_id. DO URN kinds get normalized via normalize_kind/1 (domain → dns_zone, space → spaces_bucket) so attribution matches local naming. DomainsWorker syncs DNS zones (DO Domains). Same upsert chokepoint, same three-strike stale handling. Zones are global to the account; attribution happens via ProjectsWorker if a domain is in a DO project, else stays NULL pending operator classification. Oban.Plugins.Cron added with 15-minute schedules for ProjectsWorker, DropletsWorker, DomainsWorker — workers run automatically once a token is configured. Phase 0/1 cadence; phase 2 moves droplets to cloud_sync_fast (1-min) for real-time status visibility. DigitalOcean.Client gains list_domains / list_volumes / list_floating_ips. Volumes and floating IPs not yet wired to workers; trivial follow-on. Live smoke test: 5 droplets + 7 DNS zones discovered, all attributed to their existing DO projects via membership lookup (skyai-internal becomes the fallback only for genuinely orphan resources). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
54 lines
1.3 KiB
Elixir
54 lines
1.3 KiB
Elixir
defmodule ArcadiaCloud.Sync.DomainsWorker do
|
|
@moduledoc """
|
|
Full sync of DigitalOcean Domains (DNS zones).
|
|
|
|
Domains are global to the DO account (no region, no project membership
|
|
via URN in the same way droplets have). They are typically attributed
|
|
manually by operator action; first sync leaves them with cloud_project_id
|
|
nil + tenant_id nil. ProjectsWorker's attribution pass picks them up if
|
|
they appear in project memberships.
|
|
"""
|
|
|
|
use Oban.Worker, queue: :cloud_sync_full, max_attempts: 3
|
|
|
|
alias ArcadiaCloud.Cloud
|
|
alias ArcadiaCloud.DigitalOcean.Client
|
|
|
|
@kind "dns_zone"
|
|
@provider "digitalocean"
|
|
|
|
@impl Oban.Worker
|
|
def perform(_job) do
|
|
sync_started_at = DateTime.utc_now() |> DateTime.truncate(:second)
|
|
|
|
with {:ok, domains} <- Client.list_domains() do
|
|
Enum.each(domains, fn d ->
|
|
Cloud.upsert_resource(normalize(d, sync_started_at))
|
|
end)
|
|
|
|
Cloud.mark_stale(@kind, sync_started_at)
|
|
:ok
|
|
end
|
|
end
|
|
|
|
defp normalize(d, now) do
|
|
name = d["name"]
|
|
|
|
%{
|
|
provider: @provider,
|
|
provider_id: name,
|
|
kind: @kind,
|
|
name: name,
|
|
region: nil,
|
|
status: "active",
|
|
tags: [],
|
|
attrs: %{
|
|
ttl: d["ttl"],
|
|
zone_file: d["zone_file"]
|
|
},
|
|
first_seen_at: now,
|
|
last_seen_at: now
|
|
}
|
|
end
|
|
end
|