From d23875cccb21a4baab01ea18d3ee0e716e875e9b Mon Sep 17 00:00:00 2001 From: abtreece Date: Fri, 24 Apr 2026 20:52:42 -0500 Subject: [PATCH 1/4] feat: add GCS buckets for APT/YUM archive repositories --- terraform/repo_buckets.tf | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/terraform/repo_buckets.tf b/terraform/repo_buckets.tf index 26fc536..e077791 100644 --- a/terraform/repo_buckets.tf +++ b/terraform/repo_buckets.tf @@ -38,3 +38,32 @@ resource "google_storage_bucket_iam_binding" "server-edition-yum-repo-writable-b role = "roles/storage.objectAdmin" members = ["principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github-ci-deploy.name}/attribute.repository/fullstaq-ruby/server-edition"] } + +resource "google_storage_bucket" "server-edition-apt-repo-archive" { + depends_on = [google_project_service.storage-api] + name = "${var.gcloud_bucket_prefix}-server-edition-apt-repo-archive" + force_destroy = true + uniform_bucket_level_access = true + location = var.gcloud_storage_location +} + +resource "google_storage_bucket_iam_binding" "server-edition-apt-repo-archive-public-viewable" { + bucket = google_storage_bucket.server-edition-apt-repo-archive.self_link + role = "roles/storage.objectViewer" + members = ["allUsers"] +} + + +resource "google_storage_bucket" "server-edition-yum-repo-archive" { + depends_on = [google_project_service.storage-api] + name = "${var.gcloud_bucket_prefix}-server-edition-yum-repo-archive" + force_destroy = true + uniform_bucket_level_access = true + location = var.gcloud_storage_location +} + +resource "google_storage_bucket_iam_binding" "server-edition-yum-repo-archive-public-viewable" { + bucket = google_storage_bucket.server-edition-yum-repo-archive.self_link + role = "roles/storage.objectViewer" + members = ["allUsers"] +} From cb9de39c66599b4a5d086b2dcb94d21bb10b73f4 Mon Sep 17 00:00:00 2001 From: abtreece Date: Fri, 24 Apr 2026 20:52:50 -0500 Subject: [PATCH 2/4] feat: add DNS zones for APT/YUM archive subdomains --- terraform/dns.tf | 72 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/terraform/dns.tf b/terraform/dns.tf index eb72fc0..18c973f 100644 --- a/terraform/dns.tf +++ b/terraform/dns.tf @@ -149,3 +149,75 @@ resource "azurerm_dns_aaaa_record" "yum" { records = [var.backend_server_ipv6] ttl = 86400 } + + +resource "azurerm_dns_zone" "apt-archive" { + name = "apt-archive.${var.dns_name}" + resource_group_name = "fullstaq-ruby-infra-maintainers" +} + +resource "azurerm_role_assignment" "caddy-update-dns-apt-archive" { + scope = azurerm_dns_zone.apt-archive.id + role_definition_name = "DNS Zone Contributor" + principal_id = azuread_service_principal.caddy.object_id +} + +resource "azurerm_dns_ns_record" "apt-archive" { + name = "apt-archive" + zone_name = azurerm_dns_zone.website.name + resource_group_name = azurerm_dns_zone.website.resource_group_name + ttl = 86400 + records = azurerm_dns_zone.apt-archive.name_servers +} + +resource "azurerm_dns_a_record" "apt-archive" { + name = "@" + zone_name = azurerm_dns_zone.apt-archive.name + resource_group_name = azurerm_dns_zone.apt-archive.resource_group_name + records = [var.backend_server_ipv4] + ttl = 86400 +} + +resource "azurerm_dns_aaaa_record" "apt-archive" { + name = "@" + zone_name = azurerm_dns_zone.apt-archive.name + resource_group_name = azurerm_dns_zone.apt-archive.resource_group_name + records = [var.backend_server_ipv6] + ttl = 86400 +} + + +resource "azurerm_dns_zone" "yum-archive" { + name = "yum-archive.${var.dns_name}" + resource_group_name = "fullstaq-ruby-infra-maintainers" +} + +resource "azurerm_role_assignment" "caddy-update-dns-yum-archive" { + scope = azurerm_dns_zone.yum-archive.id + role_definition_name = "DNS Zone Contributor" + principal_id = azuread_service_principal.caddy.object_id +} + +resource "azurerm_dns_ns_record" "yum-archive" { + name = "yum-archive" + zone_name = azurerm_dns_zone.website.name + resource_group_name = azurerm_dns_zone.website.resource_group_name + ttl = 86400 + records = azurerm_dns_zone.yum-archive.name_servers +} + +resource "azurerm_dns_a_record" "yum-archive" { + name = "@" + zone_name = azurerm_dns_zone.yum-archive.name + resource_group_name = azurerm_dns_zone.yum-archive.resource_group_name + records = [var.backend_server_ipv4] + ttl = 86400 +} + +resource "azurerm_dns_aaaa_record" "yum-archive" { + name = "@" + zone_name = azurerm_dns_zone.yum-archive.name + resource_group_name = azurerm_dns_zone.yum-archive.resource_group_name + records = [var.backend_server_ipv6] + ttl = 86400 +} From 7e7c90ee3977f0c7c77809fc1ec92bc6cca91b58 Mon Sep 17 00:00:00 2001 From: abtreece Date: Fri, 24 Apr 2026 20:52:59 -0500 Subject: [PATCH 3/4] feat: add Caddy config and version query for archive repos --- ansible/files/Caddyfile | 32 +++++++++++++++++++++ ansible/files/query-latest-repo-versions.rb | 15 ++++++++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/ansible/files/Caddyfile b/ansible/files/Caddyfile index 1e35bbe..30fa547 100644 --- a/ansible/files/Caddyfile +++ b/ansible/files/Caddyfile @@ -48,3 +48,35 @@ yum.{$DOMAIN_NAME} { redir https://storage.googleapis.com/{$GCLOUD_BUCKET_PREFIX}-server-edition-yum-repo/versions/{$YUM_LATEST_VERSION}/public{uri} } } + +apt-archive.{$DOMAIN_NAME} { + tls { + dns azure { + tenant_id {$AZURE_TENANT_ID} + subscription_id {$AZURE_SUBSCRIPTION_ID} + resource_group_name fullstaq-ruby-infra-maintainers + client_id {$AZURE_DNS_UPDATER_CLIENT_ID} + client_secret {$AZURE_DNS_UPDATER_CLIENT_SECRET} + } + } + encode gzip + handle { + redir https://storage.googleapis.com/{$GCLOUD_BUCKET_PREFIX}-server-edition-apt-repo-archive/versions/{$APT_ARCHIVE_LATEST_VERSION}/public{uri} + } +} + +yum-archive.{$DOMAIN_NAME} { + tls { + dns azure { + tenant_id {$AZURE_TENANT_ID} + subscription_id {$AZURE_SUBSCRIPTION_ID} + resource_group_name fullstaq-ruby-infra-maintainers + client_id {$AZURE_DNS_UPDATER_CLIENT_ID} + client_secret {$AZURE_DNS_UPDATER_CLIENT_SECRET} + } + } + encode gzip + handle { + redir https://storage.googleapis.com/{$GCLOUD_BUCKET_PREFIX}-server-edition-yum-repo-archive/versions/{$YUM_ARCHIVE_LATEST_VERSION}/public{uri} + } +} diff --git a/ansible/files/query-latest-repo-versions.rb b/ansible/files/query-latest-repo-versions.rb index 3a87c42..f705140 100755 --- a/ansible/files/query-latest-repo-versions.rb +++ b/ansible/files/query-latest-repo-versions.rb @@ -6,12 +6,15 @@ def main open_io(ARGV[0]) do |io| query_repo_version('apt', io) query_repo_version('yum', io) + query_repo_version('apt-archive', io, suffix: '-archive') + query_repo_version('yum-archive', io, suffix: '-archive') io.puts "REPO_QUERY_TIME=#{Time.now.to_f}" end end -def query_repo_version(type, output) - uri = URI("https://storage.googleapis.com/#{require_env(:GCLOUD_BUCKET_PREFIX)}-server-edition-#{type}-repo/versions/latest_version.txt") +def query_repo_version(type, output, suffix: '') + bucket_type = type.gsub('-archive', '') + uri = URI("https://storage.googleapis.com/#{require_env(:GCLOUD_BUCKET_PREFIX)}-server-edition-#{bucket_type}-repo#{suffix}/versions/latest_version.txt") STDERR.puts "Querying #{uri}..." req = Net::HTTP::Get.new(uri) @@ -22,11 +25,17 @@ def query_repo_version(type, output) end if resp.code.to_i / 100 != 2 + if suffix != '' + STDERR.puts "Warning: #{type} repo not found (#{resp.code}), skipping" + output.puts "#{type.upcase.gsub('-', '_')}_LATEST_VERSION=0" + return + end abort("Failed to query #{uri}: #{resp.code} #{resp.body}") end + env_key = type.upcase.gsub('-', '_') STDERR.puts "#{type} latest version: #{resp.body}" - output.puts "#{type.upcase}_LATEST_VERSION=#{resp.body}" + output.puts "#{env_key}_LATEST_VERSION=#{resp.body}" end def open_io(path) From 4f829ce4621102371ac00315fede6d2cdf295a1d Mon Sep 17 00:00:00 2001 From: abtreece Date: Mon, 27 Apr 2026 21:50:07 -0500 Subject: [PATCH 4/4] fix: archive version lookup must fail hard on non-404 errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous code degraded any non-2xx response (auth errors, 5xx, redirects) to LATEST_VERSION=0 whenever the suffix indicated an archive bucket. That silently turns a transient outage or misconfiguration into Caddy redirecting clients to /versions/0/... 404s — broken behavior masquerading as valid config. Only treat HTTP 404 as the legitimate "archive not yet populated" case; surface every other failure. --- ansible/files/query-latest-repo-versions.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ansible/files/query-latest-repo-versions.rb b/ansible/files/query-latest-repo-versions.rb index f705140..49d3e1d 100755 --- a/ansible/files/query-latest-repo-versions.rb +++ b/ansible/files/query-latest-repo-versions.rb @@ -25,8 +25,12 @@ def query_repo_version(type, output, suffix: '') end if resp.code.to_i / 100 != 2 - if suffix != '' - STDERR.puts "Warning: #{type} repo not found (#{resp.code}), skipping" + # Archive bucket may legitimately not exist before the first migration runs. + # Only treat 404 as the "not yet populated" case; any other non-2xx + # (auth, 5xx, redirects) is a real failure and must surface — silently + # falling back to version 0 would point clients at /versions/0/... 404s. + if suffix != '' && resp.code.to_i == 404 + STDERR.puts "Warning: #{type} repo not found (404), skipping" output.puts "#{type.upcase.gsub('-', '_')}_LATEST_VERSION=0" return end