116 lines
4.2 KiB
Go
116 lines
4.2 KiB
Go
package report
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Config-pull error types for setup-wizard UI display.
|
|
//
|
|
// These (and PullConfig) were previously part of infra_pull.go alongside the
|
|
// disk-tier DR-recovery (PullRecovery / infra-backup) client. Disk recovery moved
|
|
// to the host agent in slice 8C; only the fresh-install config download survives
|
|
// here, so it lives in this slimmed file.
|
|
var (
|
|
ErrHubUnreachable = errors.New("hub unreachable")
|
|
ErrAuthFailed = errors.New("authentication failed")
|
|
ErrNotFound = errors.New("customer not found")
|
|
ErrHubError = errors.New("hub error")
|
|
)
|
|
|
|
// PullConfig fetches a generated controller.yaml from the Hub config endpoint.
|
|
// Auth: X-Retrieval-Password header. Used by the setup wizard's fresh-install flow.
|
|
func PullConfig(hubURL, customerID, retrievalPassword string) (string, error) {
|
|
url := strings.TrimRight(hubURL, "/") + "/api/v1/config/" + customerID
|
|
|
|
client := &http.Client{Timeout: 30 * time.Second}
|
|
|
|
req, err := http.NewRequest(http.MethodGet, url, nil)
|
|
if err != nil {
|
|
return "", fmt.Errorf("%w: %v", ErrHubError, err)
|
|
}
|
|
req.Header.Set("X-Retrieval-Password", retrievalPassword)
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return "", fmt.Errorf("%w: %v", ErrHubUnreachable, err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
switch resp.StatusCode {
|
|
case http.StatusOK:
|
|
// success
|
|
case http.StatusUnauthorized:
|
|
return "", ErrAuthFailed
|
|
case http.StatusNotFound:
|
|
return "", ErrNotFound
|
|
default:
|
|
return "", fmt.Errorf("%w: HTTP %d", ErrHubError, resp.StatusCode)
|
|
}
|
|
|
|
body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20)) // 1MB limit
|
|
if err != nil {
|
|
return "", fmt.Errorf("%w: reading response: %v", ErrHubError, err)
|
|
}
|
|
|
|
return string(body), nil
|
|
}
|
|
|
|
// ErrBoxConfigUnsupported: the hub has no GET /box-config (a hub before v0.161.0) — the caller falls back to the
|
|
// passphrase pull.
|
|
var ErrBoxConfigUnsupported = errors.New("hub has no box-config route")
|
|
|
|
// PullBoxConfig fetches the generated controller.yaml with the box's OWN hub key (GET /api/v1/box-config/{id}, hub ≥
|
|
// v0.161.0; R-1159). A live box refreshes its config this way, so the hub can refuse the household's retrieval
|
|
// passphrase once the box is live (hub `api/passphrase_scope.go`).
|
|
func PullBoxConfig(hubURL, customerID, apiKey string) (string, error) {
|
|
url := strings.TrimRight(hubURL, "/") + "/api/v1/box-config/" + customerID
|
|
client := &http.Client{Timeout: 30 * time.Second}
|
|
req, err := http.NewRequest(http.MethodGet, url, nil)
|
|
if err != nil {
|
|
return "", fmt.Errorf("%w: %v", ErrHubError, err)
|
|
}
|
|
req.Header.Set("Authorization", "Bearer "+apiKey)
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return "", fmt.Errorf("%w: %v", ErrHubUnreachable, err)
|
|
}
|
|
defer resp.Body.Close()
|
|
switch resp.StatusCode {
|
|
case http.StatusOK:
|
|
case http.StatusUnauthorized:
|
|
return "", ErrAuthFailed
|
|
case http.StatusNotFound, http.StatusMethodNotAllowed:
|
|
return "", ErrBoxConfigUnsupported
|
|
default:
|
|
return "", fmt.Errorf("%w: HTTP %d", ErrHubError, resp.StatusCode)
|
|
}
|
|
body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
|
|
if err != nil {
|
|
return "", fmt.Errorf("%w: reading response: %v", ErrHubError, err)
|
|
}
|
|
return string(body), nil
|
|
}
|
|
|
|
// PullConfigPreferKey is the LIVE box's config refresh (report.ConfigRefresher): with the box's own key first; the
|
|
// retrieval passphrase only when the hub has no box-config route (a hub before v0.161.0) or the box holds no key yet.
|
|
// Any other answer to the key pull is returned as it is — never retried with the passphrase (R-1159: the passphrase
|
|
// is for a box that has no key of its own). Pinned by TestR1159_RefreshUsesTheBoxKey.
|
|
func PullConfigPreferKey(hubURL, customerID, apiKey, retrievalPassword string, logf func(string, ...any)) (string, error) {
|
|
if apiKey != "" {
|
|
y, err := PullBoxConfig(hubURL, customerID, apiKey)
|
|
if !errors.Is(err, ErrBoxConfigUnsupported) {
|
|
if err == nil {
|
|
logf("[INFO] [config] refreshed controller.yaml with the box's own hub key (box-config)")
|
|
}
|
|
return y, err
|
|
}
|
|
logf("[INFO] [config] the hub has no box-config route (hub < 0.161.0) — refreshing with the retrieval passphrase")
|
|
}
|
|
return PullConfig(hubURL, customerID, retrievalPassword)
|
|
}
|