feat: remove module/rollout config key
This commit is contained in:
@@ -5,7 +5,6 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
@@ -89,25 +88,12 @@ func applyHubDefaults(h *HubConfig) {
|
||||
if h.CacheTTL.DurationValue() < 0 {
|
||||
h.CacheTTL = Duration(0)
|
||||
}
|
||||
if trimmed := strings.TrimSpace(h.Module); trimmed == "" {
|
||||
typeKey := strings.ToLower(strings.TrimSpace(h.Type))
|
||||
if meta, ok := hubmodule.Resolve(typeKey); ok {
|
||||
h.Module = meta.Key
|
||||
} else {
|
||||
h.Module = hubmodule.DefaultModuleKey()
|
||||
}
|
||||
} else {
|
||||
h.Module = strings.ToLower(trimmed)
|
||||
}
|
||||
if rollout := strings.TrimSpace(h.Rollout); rollout != "" {
|
||||
h.Rollout = strings.ToLower(rollout)
|
||||
}
|
||||
if h.ValidationMode == "" {
|
||||
h.ValidationMode = string(hubmodule.ValidationModeETag)
|
||||
}
|
||||
}
|
||||
|
||||
// NormalizeHubConfig 公开给无需依赖 loader 的调用方(例如测试)以填充模块/rollout 默认值。
|
||||
// NormalizeHubConfig 公开给无需依赖 loader 的调用方(例如测试)以应用 TTL/校验默认值。
|
||||
func NormalizeHubConfig(h HubConfig) HubConfig {
|
||||
applyHubDefaults(&h)
|
||||
return h
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/any-hub/any-hub/internal/hubmodule"
|
||||
"github.com/any-hub/any-hub/internal/hubmodule/legacy"
|
||||
)
|
||||
|
||||
// HubRuntime 将 Hub 配置与模块元数据合并,方便运行时快速取用策略。
|
||||
@@ -12,16 +11,14 @@ type HubRuntime struct {
|
||||
Config HubConfig
|
||||
Module hubmodule.ModuleMetadata
|
||||
CacheStrategy hubmodule.CacheStrategyProfile
|
||||
Rollout legacy.RolloutFlag
|
||||
}
|
||||
|
||||
// BuildHubRuntime 根据 Hub 配置和模块元数据创建运行时描述,应用最终 TTL 覆盖。
|
||||
func BuildHubRuntime(cfg HubConfig, meta hubmodule.ModuleMetadata, ttl time.Duration, flag legacy.RolloutFlag) HubRuntime {
|
||||
func BuildHubRuntime(cfg HubConfig, meta hubmodule.ModuleMetadata, ttl time.Duration) HubRuntime {
|
||||
strategy := hubmodule.ResolveStrategy(meta, cfg.StrategyOverrides(ttl))
|
||||
return HubRuntime{
|
||||
Config: cfg,
|
||||
Module: meta,
|
||||
CacheStrategy: strategy,
|
||||
Rollout: flag,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/any-hub/any-hub/internal/hubmodule"
|
||||
"github.com/any-hub/any-hub/internal/hubmodule/legacy"
|
||||
)
|
||||
|
||||
// Rollout 字段说明(legacy → modular 平滑迁移控制):
|
||||
// - legacy-only:强制使用 legacy 模块(EffectiveModuleKey → legacy);用于未迁移或需要快速回滚时。
|
||||
// - dual:新模块为默认,保留 legacy 以便诊断/灰度;仅当 Module 非空时生效,否则回退 legacy-only。
|
||||
// - modular:仅使用新模块;Module 为空或 legacy 模块时自动回退 legacy-only。
|
||||
// 默认行为:未填写 Rollout 时,空 Module/legacy 模块默认 legacy-only;其它模块默认 modular。
|
||||
// 影响范围:动态选择执行的模块键(EffectiveModuleKey)、路由日志中的 rollout_flag,方便区分迁移阶段。
|
||||
|
||||
// parseRolloutFlag 将配置中的 rollout 字段标准化,并结合模块类型输出最终状态。
|
||||
func parseRolloutFlag(raw string, moduleKey string) (legacy.RolloutFlag, error) {
|
||||
normalized := strings.ToLower(strings.TrimSpace(raw))
|
||||
if normalized == "" {
|
||||
return defaultRolloutFlag(moduleKey), nil
|
||||
}
|
||||
|
||||
switch normalized {
|
||||
case string(legacy.RolloutLegacyOnly):
|
||||
return legacy.RolloutLegacyOnly, nil
|
||||
case string(legacy.RolloutDual):
|
||||
if moduleKey == hubmodule.DefaultModuleKey() {
|
||||
return legacy.RolloutLegacyOnly, nil
|
||||
}
|
||||
return legacy.RolloutDual, nil
|
||||
case string(legacy.RolloutModular):
|
||||
if moduleKey == hubmodule.DefaultModuleKey() {
|
||||
return legacy.RolloutLegacyOnly, nil
|
||||
}
|
||||
return legacy.RolloutModular, nil
|
||||
default:
|
||||
return "", fmt.Errorf("不支持的 rollout 值: %s", raw)
|
||||
}
|
||||
}
|
||||
|
||||
func defaultRolloutFlag(moduleKey string) legacy.RolloutFlag {
|
||||
if strings.TrimSpace(moduleKey) == "" || moduleKey == hubmodule.DefaultModuleKey() {
|
||||
return legacy.RolloutLegacyOnly
|
||||
}
|
||||
return legacy.RolloutModular
|
||||
}
|
||||
|
||||
// EffectiveModuleKey 根据 rollout 状态计算真实运行的模块。
|
||||
func EffectiveModuleKey(moduleKey string, flag legacy.RolloutFlag) string {
|
||||
if flag == legacy.RolloutLegacyOnly {
|
||||
return hubmodule.DefaultModuleKey()
|
||||
}
|
||||
normalized := strings.ToLower(strings.TrimSpace(moduleKey))
|
||||
if normalized == "" {
|
||||
return hubmodule.DefaultModuleKey()
|
||||
}
|
||||
return normalized
|
||||
}
|
||||
|
||||
// RolloutFlagValue 返回当前 Hub 的 rollout flag(假定 Validate 已经通过)。
|
||||
func (h HubConfig) RolloutFlagValue() legacy.RolloutFlag {
|
||||
flag := legacy.RolloutFlag(strings.ToLower(strings.TrimSpace(h.Rollout)))
|
||||
if flag == "" {
|
||||
return defaultRolloutFlag(h.Module)
|
||||
}
|
||||
return flag
|
||||
}
|
||||
@@ -69,8 +69,6 @@ type HubConfig struct {
|
||||
Upstream string `mapstructure:"Upstream"`
|
||||
Proxy string `mapstructure:"Proxy"`
|
||||
Type string `mapstructure:"Type"`
|
||||
Module string `mapstructure:"Module"`
|
||||
Rollout string `mapstructure:"Rollout"`
|
||||
Username string `mapstructure:"Username"`
|
||||
Password string `mapstructure:"Password"`
|
||||
CacheTTL Duration `mapstructure:"CacheTTL"`
|
||||
|
||||
@@ -79,23 +79,9 @@ func (c *Config) Validate() error {
|
||||
}
|
||||
hub.Type = normalizedType
|
||||
|
||||
moduleKey := strings.ToLower(strings.TrimSpace(hub.Module))
|
||||
if moduleKey == "" {
|
||||
if _, ok := hubmodule.Resolve(normalizedType); ok && normalizedType != "" {
|
||||
moduleKey = normalizedType
|
||||
} else {
|
||||
moduleKey = hubmodule.DefaultModuleKey()
|
||||
}
|
||||
if _, ok := hubmodule.Resolve(normalizedType); !ok {
|
||||
return newFieldError(hubField(hub.Name, "Type"), fmt.Sprintf("未注册模块: %s", normalizedType))
|
||||
}
|
||||
if _, ok := hubmodule.Resolve(moduleKey); !ok {
|
||||
return newFieldError(hubField(hub.Name, "Module"), fmt.Sprintf("未注册模块: %s", moduleKey))
|
||||
}
|
||||
hub.Module = moduleKey
|
||||
flag, err := parseRolloutFlag(hub.Rollout, hub.Module)
|
||||
if err != nil {
|
||||
return newFieldError(hubField(hub.Name, "Rollout"), err.Error())
|
||||
}
|
||||
hub.Rollout = string(flag)
|
||||
if hub.ValidationMode != "" {
|
||||
mode := strings.ToLower(strings.TrimSpace(hub.ValidationMode))
|
||||
switch mode {
|
||||
|
||||
@@ -24,7 +24,7 @@ internal/hubmodule/
|
||||
2. 填写模块特有逻辑与缓存策略,并确保包含中文注释解释设计。
|
||||
3. 在模块目录添加 `module_test.go`,使用 `httptest.Server` 与 `t.TempDir()` 复现真实流量。
|
||||
4. 运行 `make modules-test` 验证模块单元测试。
|
||||
5. `[[Hub]].Module` 留空时会优先选择与 `Type` 同名的模块,实际迁移时仍建议显式填写,便于 diagnostics 标记 rollout。
|
||||
5. `[[Hub]].Type` 现已直接映射到同名模块;新增模块时记得将类型加入配置校验与示例配置。
|
||||
|
||||
## 术语
|
||||
- **Module Key**:模块唯一标识(如 `legacy`、`npm-tarball`)。
|
||||
|
||||
@@ -88,10 +88,6 @@ func isAptImmutablePath(p string) bool {
|
||||
|
||||
func isByHashPath(p string) bool {
|
||||
clean := canonicalPath(p)
|
||||
if strings.Contains(clean, "/dists/") {
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.Contains(clean, "/by-hash/")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// RolloutFlag 描述 legacy 模块迁移阶段。
|
||||
type RolloutFlag string
|
||||
|
||||
const (
|
||||
RolloutLegacyOnly RolloutFlag = "legacy-only"
|
||||
RolloutDual RolloutFlag = "dual"
|
||||
RolloutModular RolloutFlag = "modular"
|
||||
)
|
||||
|
||||
// AdapterState 记录特定 Hub 在 legacy 适配器中的运行状态。
|
||||
type AdapterState struct {
|
||||
HubName string
|
||||
ModuleKey string
|
||||
Rollout RolloutFlag
|
||||
}
|
||||
|
||||
var (
|
||||
stateMu sync.RWMutex
|
||||
state = make(map[string]AdapterState)
|
||||
)
|
||||
|
||||
// RecordAdapterState 更新指定 Hub 的 rollout 状态,供诊断端和日志使用。
|
||||
func RecordAdapterState(hubName, moduleKey string, flag RolloutFlag) {
|
||||
if hubName == "" {
|
||||
return
|
||||
}
|
||||
key := strings.ToLower(hubName)
|
||||
stateMu.Lock()
|
||||
state[key] = AdapterState{
|
||||
HubName: hubName,
|
||||
ModuleKey: moduleKey,
|
||||
Rollout: flag,
|
||||
}
|
||||
stateMu.Unlock()
|
||||
}
|
||||
|
||||
// SnapshotAdapterStates 返回所有 Hub 的 rollout 状态,按名称排序。
|
||||
func SnapshotAdapterStates() []AdapterState {
|
||||
stateMu.RLock()
|
||||
defer stateMu.RUnlock()
|
||||
|
||||
if len(state) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(state))
|
||||
for k := range state {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
result := make([]AdapterState, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
result = append(result, state[key])
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -11,15 +11,13 @@ func BaseFields(action, configPath string) logrus.Fields {
|
||||
}
|
||||
|
||||
// RequestFields 提供 hub/domain/命中状态字段,供代理请求日志复用。
|
||||
func RequestFields(hub, domain, hubType, authMode, moduleKey, rolloutFlag string, cacheHit bool, legacyOnly bool) logrus.Fields {
|
||||
func RequestFields(hub, domain, hubType, authMode, moduleKey string, cacheHit bool) logrus.Fields {
|
||||
return logrus.Fields{
|
||||
"hub": hub,
|
||||
"domain": domain,
|
||||
"hub_type": hubType,
|
||||
"auth_mode": authMode,
|
||||
"cache_hit": cacheHit,
|
||||
"legacy_only": legacyOnly,
|
||||
"module_key": moduleKey,
|
||||
"rollout_flag": rolloutFlag,
|
||||
"hub": hub,
|
||||
"domain": domain,
|
||||
"hub_type": hubType,
|
||||
"auth_mode": authMode,
|
||||
"cache_hit": cacheHit,
|
||||
"module_key": moduleKey,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/gofiber/fiber/v3"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/any-hub/any-hub/internal/hubmodule"
|
||||
"github.com/any-hub/any-hub/internal/logging"
|
||||
"github.com/any-hub/any-hub/internal/server"
|
||||
)
|
||||
@@ -133,9 +132,7 @@ func (f *Forwarder) routeFields(route *server.HubRoute, requestID string) logrus
|
||||
route.Config.Type,
|
||||
route.Config.AuthMode(),
|
||||
route.ModuleKey,
|
||||
string(route.RolloutFlag),
|
||||
false,
|
||||
route.ModuleKey == hubmodule.DefaultModuleKey(),
|
||||
)
|
||||
if requestID != "" {
|
||||
fields["request_id"] = requestID
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/valyala/fasthttp"
|
||||
|
||||
"github.com/any-hub/any-hub/internal/config"
|
||||
"github.com/any-hub/any-hub/internal/hubmodule/legacy"
|
||||
"github.com/any-hub/any-hub/internal/server"
|
||||
)
|
||||
|
||||
@@ -103,7 +102,6 @@ func testRouteWithModule(moduleKey string) *server.HubRoute {
|
||||
Domain: "test.local",
|
||||
Type: "custom",
|
||||
},
|
||||
ModuleKey: moduleKey,
|
||||
RolloutFlag: legacy.RolloutModular,
|
||||
ModuleKey: moduleKey,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,6 @@ func buildHookContext(route *server.HubRoute, c fiber.Ctx) *hooks.RequestContext
|
||||
Domain: route.Config.Domain,
|
||||
HubType: route.Config.Type,
|
||||
ModuleKey: route.ModuleKey,
|
||||
RolloutFlag: string(route.RolloutFlag),
|
||||
UpstreamHost: baseHost,
|
||||
Method: c.Method(),
|
||||
}
|
||||
@@ -519,9 +518,7 @@ func (h *Handler) logResult(
|
||||
route.Config.Type,
|
||||
route.Config.AuthMode(),
|
||||
route.ModuleKey,
|
||||
string(route.RolloutFlag),
|
||||
cacheHit,
|
||||
route.ModuleKey == hubmodule.DefaultModuleKey(),
|
||||
)
|
||||
fields["action"] = "proxy"
|
||||
fields["upstream"] = upstream
|
||||
@@ -972,9 +969,7 @@ func (h *Handler) logAuthRetry(route *server.HubRoute, upstream string, requestI
|
||||
route.Config.Type,
|
||||
route.Config.AuthMode(),
|
||||
route.ModuleKey,
|
||||
string(route.RolloutFlag),
|
||||
false,
|
||||
route.ModuleKey == hubmodule.DefaultModuleKey(),
|
||||
)
|
||||
fields["action"] = "proxy_retry"
|
||||
fields["upstream"] = upstream
|
||||
@@ -993,9 +988,7 @@ func (h *Handler) logAuthFailure(route *server.HubRoute, upstream string, reques
|
||||
route.Config.Type,
|
||||
route.Config.AuthMode(),
|
||||
route.ModuleKey,
|
||||
string(route.RolloutFlag),
|
||||
false,
|
||||
route.ModuleKey == hubmodule.DefaultModuleKey(),
|
||||
)
|
||||
fields["action"] = "proxy"
|
||||
fields["upstream"] = upstream
|
||||
|
||||
@@ -13,7 +13,6 @@ type RequestContext struct {
|
||||
Domain string
|
||||
HubType string
|
||||
ModuleKey string
|
||||
RolloutFlag string
|
||||
UpstreamHost string
|
||||
Method string
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
|
||||
"github.com/any-hub/any-hub/internal/config"
|
||||
"github.com/any-hub/any-hub/internal/hubmodule"
|
||||
"github.com/any-hub/any-hub/internal/hubmodule/legacy"
|
||||
)
|
||||
|
||||
// HubRoute 将 Hub 配置与派生属性(如缓存 TTL、解析后的 Upstream/Proxy URL)
|
||||
@@ -31,8 +30,6 @@ type HubRoute struct {
|
||||
Module hubmodule.ModuleMetadata
|
||||
// CacheStrategy 代表模块默认策略与 hub 覆盖后的最终结果。
|
||||
CacheStrategy hubmodule.CacheStrategyProfile
|
||||
// RolloutFlag 反映当前 hub 的 legacy → modular 迁移状态,供日志/诊断使用。
|
||||
RolloutFlag legacy.RolloutFlag
|
||||
}
|
||||
|
||||
// HubRegistry 提供 Host/Host:port 到 HubRoute 的查询能力,所有 Hub 共享同一个监听端口。
|
||||
@@ -106,9 +103,11 @@ func (r *HubRegistry) List() []HubRoute {
|
||||
}
|
||||
|
||||
func buildHubRoute(cfg *config.Config, hub config.HubConfig) (*HubRoute, error) {
|
||||
flag := hub.RolloutFlagValue()
|
||||
effectiveKey := config.EffectiveModuleKey(hub.Module, flag)
|
||||
meta, err := moduleMetadataForKey(effectiveKey)
|
||||
moduleKey := strings.ToLower(strings.TrimSpace(hub.Type))
|
||||
if moduleKey == "" {
|
||||
return nil, fmt.Errorf("hub %s: 缺少 Type", hub.Name)
|
||||
}
|
||||
meta, err := moduleMetadataForKey(moduleKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("hub %s: %w", hub.Name, err)
|
||||
}
|
||||
@@ -127,8 +126,7 @@ func buildHubRoute(cfg *config.Config, hub config.HubConfig) (*HubRoute, error)
|
||||
}
|
||||
|
||||
effectiveTTL := cfg.EffectiveCacheTTL(hub)
|
||||
runtime := config.BuildHubRuntime(hub, meta, effectiveTTL, flag)
|
||||
legacy.RecordAdapterState(hub.Name, runtime.Module.Key, flag)
|
||||
runtime := config.BuildHubRuntime(hub, meta, effectiveTTL)
|
||||
|
||||
return &HubRoute{
|
||||
Config: hub,
|
||||
@@ -139,7 +137,6 @@ func buildHubRoute(cfg *config.Config, hub config.HubConfig) (*HubRoute, error)
|
||||
ModuleKey: runtime.Module.Key,
|
||||
Module: runtime.Module,
|
||||
CacheStrategy: runtime.CacheStrategy,
|
||||
RolloutFlag: runtime.Rollout,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/any-hub/any-hub/internal/config"
|
||||
"github.com/any-hub/any-hub/internal/hubmodule/legacy"
|
||||
)
|
||||
|
||||
func TestHubRegistryLookupByHost(t *testing.T) {
|
||||
@@ -55,8 +54,8 @@ func TestHubRegistryLookupByHost(t *testing.T) {
|
||||
if route.CacheStrategy.ValidationMode == "" {
|
||||
t.Fatalf("cache strategy validation mode should not be empty")
|
||||
}
|
||||
if route.RolloutFlag != legacy.RolloutModular {
|
||||
t.Fatalf("default rollout flag should be modular")
|
||||
if route.ModuleKey != "docker" {
|
||||
t.Fatalf("expected docker module, got %s", route.ModuleKey)
|
||||
}
|
||||
|
||||
if route.UpstreamURL.String() != "https://registry-1.docker.io" {
|
||||
|
||||
@@ -65,8 +65,6 @@ type hubBindingPayload struct {
|
||||
ModuleKey string `json:"module_key"`
|
||||
Domain string `json:"domain"`
|
||||
Port int `json:"port"`
|
||||
Rollout string `json:"rollout_flag"`
|
||||
Legacy bool `json:"legacy_only"`
|
||||
}
|
||||
|
||||
func encodeModules(mods []hubmodule.ModuleMetadata, status map[string]string) []modulePayload {
|
||||
@@ -118,8 +116,6 @@ func encodeHubBindings(routes []server.HubRoute) []hubBindingPayload {
|
||||
ModuleKey: route.ModuleKey,
|
||||
Domain: route.Config.Domain,
|
||||
Port: route.ListenPort,
|
||||
Rollout: string(route.RolloutFlag),
|
||||
Legacy: route.ModuleKey == hubmodule.DefaultModuleKey(),
|
||||
})
|
||||
}
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user