fix(acceptance): 物化验收任务的最终媒体
仅对已认证 Acceptance Run 强制下载并持久化 URL 型结果,确保集群外压测端能验证最终视频;普通生产任务继续遵循现有文件存储策略。压测器仅对 Gateway 自有地址做双入口重写,避免向第三方媒体域名泄露验收凭据。\n\n同时修复本地集群重复使用同一快照文件时的幂等复制失败。验证:Go 全量测试、go vet、gofmt、bash -n、ShellCheck 和 git diff --check 通过。
This commit is contained in:
@@ -760,7 +760,7 @@ func pollVideoTask(ctx context.Context, client *http.Client, opts options, taskI
|
||||
if mediaURL == "" {
|
||||
return fmt.Errorf("video task %s succeeded without a media URL", taskID)
|
||||
}
|
||||
return validateVideoAsset(ctx, client, mediaURL)
|
||||
return validateVideoAsset(ctx, client, opts, mediaURL, index)
|
||||
case "failed", "cancelled", "canceled":
|
||||
return fmt.Errorf("video task %s finished with status %s", taskID, status)
|
||||
}
|
||||
@@ -780,15 +780,34 @@ func pollVideoTask(ctx context.Context, client *http.Client, opts options, taskI
|
||||
func findMediaURL(value any) string {
|
||||
switch typed := value.(type) {
|
||||
case map[string]any:
|
||||
for key, item := range typed {
|
||||
preferredKeys := []string{"video_url", "url", "data", "content", "output", "result", "upload"}
|
||||
visited := make(map[string]struct{}, len(typed))
|
||||
for _, key := range preferredKeys {
|
||||
item, ok := typed[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
visited[key] = struct{}{}
|
||||
normalized := strings.ToLower(strings.TrimSpace(key))
|
||||
if (normalized == "url" || normalized == "video_url") && strings.HasPrefix(strings.TrimSpace(fmt.Sprint(item)), "http") {
|
||||
if (normalized == "url" || normalized == "video_url") && acceptanceMediaURL(strings.TrimSpace(fmt.Sprint(item))) {
|
||||
return strings.TrimSpace(fmt.Sprint(item))
|
||||
}
|
||||
if mediaURL := findMediaURL(item); mediaURL != "" {
|
||||
return mediaURL
|
||||
}
|
||||
}
|
||||
keys := make([]string, 0, len(typed)-len(visited))
|
||||
for key := range typed {
|
||||
if _, ok := visited[key]; !ok {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, key := range keys {
|
||||
if mediaURL := findMediaURL(typed[key]); mediaURL != "" {
|
||||
return mediaURL
|
||||
}
|
||||
}
|
||||
case []any:
|
||||
for _, item := range typed {
|
||||
if mediaURL := findMediaURL(item); mediaURL != "" {
|
||||
@@ -799,11 +818,22 @@ func findMediaURL(value any) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func validateVideoAsset(ctx context.Context, client *http.Client, mediaURL string) error {
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, mediaURL, nil)
|
||||
func acceptanceMediaURL(value string) bool {
|
||||
return strings.HasPrefix(value, "/") || strings.HasPrefix(value, "http://") || strings.HasPrefix(value, "https://")
|
||||
}
|
||||
|
||||
func validateVideoAsset(ctx context.Context, client *http.Client, opts options, mediaURL string, index int) error {
|
||||
requestURL, gatewayRequest, err := acceptanceMediaRequestURL(opts, mediaURL, index)
|
||||
if err != nil {
|
||||
return fmt.Errorf("resolve final video: %w", err)
|
||||
}
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if gatewayRequest {
|
||||
opts.setHeaders(request, index, false)
|
||||
}
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
return fmt.Errorf("download final video: %w", err)
|
||||
@@ -824,6 +854,46 @@ func validateVideoAsset(ctx context.Context, client *http.Client, mediaURL strin
|
||||
return nil
|
||||
}
|
||||
|
||||
func acceptanceMediaRequestURL(opts options, mediaURL string, index int) (string, bool, error) {
|
||||
mediaURL = strings.TrimSpace(mediaURL)
|
||||
if mediaURL == "" {
|
||||
return "", false, errors.New("empty media URL")
|
||||
}
|
||||
if strings.HasPrefix(mediaURL, "/") {
|
||||
if len(opts.gateways) == 0 {
|
||||
return "", false, errors.New("no gateway URL configured")
|
||||
}
|
||||
base, err := url.Parse(opts.gateways[index%len(opts.gateways)])
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
reference, err := url.Parse(mediaURL)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
return base.ResolveReference(reference).String(), true, nil
|
||||
}
|
||||
parsed, err := url.Parse(mediaURL)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
if opts.gatewayTLSName == "" || !strings.EqualFold(parsed.Hostname(), opts.gatewayTLSName) {
|
||||
return parsed.String(), false, nil
|
||||
}
|
||||
if len(opts.gateways) == 0 {
|
||||
return "", false, errors.New("no gateway URL configured")
|
||||
}
|
||||
base, err := url.Parse(opts.gateways[index%len(opts.gateways)])
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
base.Path = parsed.Path
|
||||
base.RawPath = parsed.RawPath
|
||||
base.RawQuery = parsed.RawQuery
|
||||
base.Fragment = parsed.Fragment
|
||||
return base.String(), true, nil
|
||||
}
|
||||
|
||||
func (o options) setHeaders(request *http.Request, requestIndex int, realUpstream bool) {
|
||||
if o.gatewayTLSName != "" {
|
||||
request.Host = o.gatewayTLSName
|
||||
|
||||
Reference in New Issue
Block a user