mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-09-03 08:46:57 +00:00
upcloud provision fixes and relay tweaks
This commit is contained in:
@@ -18,6 +18,7 @@ done
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update && apt-get upgrade -y
|
||||
apt-get install -y git gcc make curl libsqlite3-dev nodejs npm htop systemd-timesyncd
|
||||
sed -i 's/^#NTP=.*/NTP=0.debian.pool.ntp.org 1.debian.pool.ntp.org 2.debian.pool.ntp.org 3.debian.pool.ntp.org/' /etc/systemd/timesyncd.conf
|
||||
timedatectl set-ntp true
|
||||
|
||||
# Swap (for small instances)
|
||||
|
||||
@@ -646,10 +646,20 @@ func createFirewallRules(ctx context.Context, svc *service.Service, serverUUID,
|
||||
Position: 2,
|
||||
Comment: "Allow private network",
|
||||
},
|
||||
{
|
||||
Direction: upcloud.FirewallRuleDirectionIn,
|
||||
Action: upcloud.FirewallRuleActionAccept,
|
||||
Family: upcloud.IPAddressFamilyIPv4,
|
||||
Protocol: upcloud.FirewallRuleProtocolUDP,
|
||||
SourcePortStart: "123",
|
||||
SourcePortEnd: "123",
|
||||
Position: 3,
|
||||
Comment: "Allow NTP replies",
|
||||
},
|
||||
{
|
||||
Direction: upcloud.FirewallRuleDirectionIn,
|
||||
Action: upcloud.FirewallRuleActionDrop,
|
||||
Position: 3,
|
||||
Position: 4,
|
||||
Comment: "Drop all other inbound",
|
||||
},
|
||||
},
|
||||
|
||||
+22
-17
@@ -67,6 +67,7 @@ type RelayStatus struct {
|
||||
Online bool
|
||||
Error string
|
||||
HasRequestCrawl bool
|
||||
RequestCrawlStatus int // HTTP status code from probe (400=open, 401/403=auth required, 5xx=error)
|
||||
HasListReposByCollection bool
|
||||
RepoStatus *RepoStatus
|
||||
HostStatus *HostStatus
|
||||
@@ -90,15 +91,14 @@ func CheckRelayStatus(relayURL, hostname, did string) *RelayStatus {
|
||||
// Probe requestCrawl
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
supported, online := probeRequestCrawl(relayURL)
|
||||
supported, statusCode, online := probeRequestCrawl(relayURL)
|
||||
if online {
|
||||
markOnline()
|
||||
}
|
||||
if supported {
|
||||
mu.Lock()
|
||||
result.HasRequestCrawl = true
|
||||
mu.Unlock()
|
||||
}
|
||||
mu.Lock()
|
||||
result.HasRequestCrawl = supported
|
||||
result.RequestCrawlStatus = statusCode
|
||||
mu.Unlock()
|
||||
}()
|
||||
|
||||
// Check host status
|
||||
@@ -158,26 +158,31 @@ func CheckRelayStatus(relayURL, hostname, did string) *RelayStatus {
|
||||
return result
|
||||
}
|
||||
|
||||
// probeRequestCrawl checks if a relay supports the requestCrawl endpoint using a HEAD request.
|
||||
// A 4xx response (e.g. 405 Method Not Allowed) means the endpoint exists.
|
||||
// A 5xx or connection failure means it's broken or unsupported.
|
||||
func probeRequestCrawl(relayURL string) (supported bool, online bool) {
|
||||
// probeRequestCrawl checks if a relay supports the requestCrawl endpoint by POSTing
|
||||
// an empty hostname. Returns (supported, statusCode, online):
|
||||
// - 400 = endpoint exists and accepts unauthenticated crawls (supported=true)
|
||||
// - 401/403 = endpoint exists but requires auth (supported=false)
|
||||
// - 5xx = endpoint is broken (supported=false)
|
||||
// - connection error = relay offline (online=false)
|
||||
func probeRequestCrawl(relayURL string) (supported bool, statusCode int, online bool) {
|
||||
client := &http.Client{Timeout: 5 * time.Second}
|
||||
req, err := http.NewRequest("HEAD", relayURL+SyncRequestCrawl, nil)
|
||||
body := bytes.NewReader([]byte(`{"hostname":""}`))
|
||||
req, err := http.NewRequest("POST", relayURL+SyncRequestCrawl, body)
|
||||
if err != nil {
|
||||
return false, false
|
||||
return false, 0, false
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, false
|
||||
return false, 0, false
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Any HTTP response means the relay is online.
|
||||
// 4xx (typically 405 Method Not Allowed) = endpoint exists.
|
||||
// 5xx = endpoint is broken.
|
||||
return resp.StatusCode >= 400 && resp.StatusCode < 500, true
|
||||
// 400 = endpoint exists, accepts unauthenticated requests (empty hostname rejected as expected)
|
||||
// 401/403 = endpoint exists but requires authentication
|
||||
// 5xx = endpoint is broken
|
||||
return resp.StatusCode == http.StatusBadRequest, resp.StatusCode, true
|
||||
}
|
||||
|
||||
// probeListReposByCollection checks if a relay supports the listReposByCollection endpoint.
|
||||
|
||||
@@ -217,6 +217,8 @@ func TestCheckHostStatus(t *testing.T) {
|
||||
func TestCheckRelayStatus_AllEndpointsSucceed(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case SyncRequestCrawl:
|
||||
w.WriteHeader(http.StatusBadRequest) // empty hostname = 400
|
||||
case SyncGetHostStatus:
|
||||
json.NewEncoder(w).Encode(HostStatus{Hostname: "hold.example.com", Active: true})
|
||||
case SyncGetRepoStatus:
|
||||
@@ -237,6 +239,12 @@ func TestCheckRelayStatus_AllEndpointsSucceed(t *testing.T) {
|
||||
if status.Error != "" {
|
||||
t.Errorf("expected no error, got %q", status.Error)
|
||||
}
|
||||
if !status.HasRequestCrawl {
|
||||
t.Error("expected HasRequestCrawl = true")
|
||||
}
|
||||
if status.RequestCrawlStatus != http.StatusBadRequest {
|
||||
t.Errorf("RequestCrawlStatus = %d, want %d", status.RequestCrawlStatus, http.StatusBadRequest)
|
||||
}
|
||||
if !status.HasListReposByCollection {
|
||||
t.Error("expected HasListReposByCollection = true")
|
||||
}
|
||||
@@ -257,6 +265,8 @@ func TestCheckRelayStatus_AllEndpointsSucceed(t *testing.T) {
|
||||
func TestCheckRelayStatus_OnlineButUnknownHost(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case SyncRequestCrawl:
|
||||
w.WriteHeader(http.StatusBadRequest) // empty hostname = 400
|
||||
case SyncGetHostStatus:
|
||||
w.WriteHeader(http.StatusBadRequest) // relay doesn't know this host
|
||||
case SyncGetRepoStatus:
|
||||
@@ -303,6 +313,8 @@ func TestCheckRelayStatus_Offline(t *testing.T) {
|
||||
func TestCheckRelayStatus_NoListReposByCollection(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case SyncRequestCrawl:
|
||||
w.WriteHeader(http.StatusBadRequest) // empty hostname = 400
|
||||
case SyncGetHostStatus:
|
||||
json.NewEncoder(w).Encode(HostStatus{Hostname: "hold.example.com", Active: true})
|
||||
case SyncGetRepoStatus:
|
||||
@@ -327,3 +339,39 @@ func TestCheckRelayStatus_NoListReposByCollection(t *testing.T) {
|
||||
t.Error("expected RepoStatus to be active")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckRelayStatus_AuthRequired(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case SyncRequestCrawl:
|
||||
w.WriteHeader(http.StatusForbidden) // auth required
|
||||
case SyncGetHostStatus:
|
||||
json.NewEncoder(w).Encode(HostStatus{Hostname: "hold.example.com", Active: true})
|
||||
case SyncGetRepoStatus:
|
||||
json.NewEncoder(w).Encode(RepoStatus{DID: "did:web:hold.example.com", Active: true, Rev: "r1"})
|
||||
case SyncListReposByCollection:
|
||||
json.NewEncoder(w).Encode(map[string]any{"repos": []any{}})
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
status := CheckRelayStatus(srv.URL, "hold.example.com", "did:web:hold.example.com")
|
||||
|
||||
if !status.Online {
|
||||
t.Error("expected Online = true")
|
||||
}
|
||||
if status.HasRequestCrawl {
|
||||
t.Error("expected HasRequestCrawl = false (auth required)")
|
||||
}
|
||||
if status.RequestCrawlStatus != http.StatusForbidden {
|
||||
t.Errorf("RequestCrawlStatus = %d, want %d", status.RequestCrawlStatus, http.StatusForbidden)
|
||||
}
|
||||
if !status.HasListReposByCollection {
|
||||
t.Error("expected HasListReposByCollection = true")
|
||||
}
|
||||
if status.RepoStatus == nil || !status.RepoStatus.Active {
|
||||
t.Error("expected RepoStatus to be active")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ type RelayStatusView struct {
|
||||
Online bool
|
||||
Error string
|
||||
HasRequestCrawl bool
|
||||
RequestCrawlStatus int
|
||||
HasListReposByCollection bool
|
||||
RepoStatus *atproto.RepoStatus
|
||||
HostStatus *atproto.HostStatus
|
||||
@@ -77,6 +78,7 @@ func (ui *AdminUI) handleRelayStatus(w http.ResponseWriter, r *http.Request) {
|
||||
Online: status.Online,
|
||||
Error: status.Error,
|
||||
HasRequestCrawl: status.HasRequestCrawl,
|
||||
RequestCrawlStatus: status.RequestCrawlStatus,
|
||||
HasListReposByCollection: status.HasListReposByCollection,
|
||||
RepoStatus: status.RepoStatus,
|
||||
HostStatus: status.HostStatus,
|
||||
|
||||
@@ -22,7 +22,13 @@
|
||||
<td>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
{{if .Online}}
|
||||
{{if .HasRequestCrawl}}<span class="badge badge-ghost badge-sm">requestCrawl</span>{{end}}
|
||||
{{if .HasRequestCrawl}}
|
||||
<span class="badge badge-ghost badge-sm">requestCrawl</span>
|
||||
{{else if or (eq .RequestCrawlStatus 401) (eq .RequestCrawlStatus 403)}}
|
||||
<span class="badge badge-warning badge-sm">requestCrawl (auth required)</span>
|
||||
{{else if ge .RequestCrawlStatus 500}}
|
||||
<span class="badge badge-error badge-sm">requestCrawl ({{.RequestCrawlStatus}})</span>
|
||||
{{end}}
|
||||
{{if .HasListReposByCollection}}<span class="badge badge-ghost badge-sm">listReposByCollection</span>{{end}}
|
||||
{{else}}
|
||||
<span class="text-base-content/30 text-sm">-</span>
|
||||
|
||||
Reference in New Issue
Block a user