refactor(config): remove unused mixed-mode environment config
This commit is contained in:
@@ -130,6 +130,8 @@
|
||||
- 旧 Dispatcher 本地配置解析:旧 `LoadDispatcherFile` 独占读取上游 v1 `dispatcher-config.schema.json`,没有现行 Dispatcher CLI 调用者;删除实现与旧文件专属测试。现行入口仍从部署环境独立读取 Dispatcher 身份、SaaS 只读配置端点及受控 OSS 配置,并保留当前校验和错误报告。历史上游 Schema 未改,删除旧入口不代表真实配置来源已联调。
|
||||
- 本地发布与合同脚本:先运行发布检查复现旧校验器因受保护第三方文档的历史 SHA-256 不匹配而中断,未覆盖用户修改、未改写历史 manifest/hash;改由当前合同正反例验证作准入,单独保留上游 v1 文件 SHA-256 的历史来源核对,删除会重新生成/校验过时代次的两个脚本。发布清单只记录当前 local 合同、MQ 拓扑、Proto 及历史来源 manifest 的实际哈希,不再宣称旧 v0.1/v0.3 草案是现行。发布检查先因旧清单格式失败,改完后仍因旧 CLI 错误文案失败;以当前 `dispatcher --mode mixed` 实测确认其在创建 SQLite 前拒绝,再修正检查文本。最终本地发布及打包、防覆盖、mixed/real 资源前拒绝和当前合同/队列检查均通过;仅生成并删除自有临时产物,未部署、未拨号、未处理现存业务数据。
|
||||
|
||||
- 旧全局环境配置:旧 `Config`/`FromEnv` 同时配置 Mock、mixed/real、旧 Dispatcher SQLite 和 OSS 环境凭据,已无现行业务调用;删除实现和专属测试,保留当前分角色环境及批准文件/受控引用读取。把「旧 OSS 环境变量不可覆盖配置文件与受控凭据」迁至现行 `LoadCurrentOSSConfig` 隔离测试;原旧结构的 mTLS 专属测试删除,现行 Dispatcher/Agent mTLS 校验测试仍在。全仓 Go 测试通过;不更改实际部署配置或现存数据。
|
||||
|
||||
## 验收台账
|
||||
|
||||
P01–P06 的项目内隔离证据见上;P07 全仓命名与唯一入口尚在清理,A01–A12 和 K01–K16 的最终对照仍待 P08。不得用本地 Mock 冒充外部签收。
|
||||
|
||||
@@ -52,14 +52,3 @@ func TestParseCertificateFingerprints(t *testing.T) {
|
||||
t.Fatal("expected invalid fingerprint to be rejected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigRequiresDispatcherMTLSForEndpointInventory(t *testing.T) {
|
||||
cfg := Config{Mode: "mock", DBPath: ":memory:", AgentEndpointsFile: "agents.json"}
|
||||
if err := cfg.Validate("dispatcher"); err == nil {
|
||||
t.Fatal("expected Dispatcher mTLS requirement")
|
||||
}
|
||||
cfg.MTLSCAFile, cfg.MTLSCertFile, cfg.MTLSKeyFile = "ca.pem", "dispatcher.pem", "dispatcher.key"
|
||||
if err := cfg.Validate("dispatcher"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,208 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Mode string
|
||||
DBPath string
|
||||
SpoolRoot string
|
||||
RabbitURL string
|
||||
Exchange string
|
||||
OutboxBatch int
|
||||
AgentID string
|
||||
DispatcherID string
|
||||
Version string
|
||||
CellID string
|
||||
GRPCListen string
|
||||
DispatcherGRPCListen string
|
||||
DispatcherGRPCEndpoint string
|
||||
DispatcherGRPCServerName string
|
||||
DispatcherGRPCAllowedAgentIDs string
|
||||
MTLSCAFile string
|
||||
MTLSCertFile string
|
||||
MTLSKeyFile string
|
||||
MTLSServerName string
|
||||
MTLSPeerCertificateFingerprints string
|
||||
OSSRegion string
|
||||
OSSEndpoint string
|
||||
OSSBucket string
|
||||
OSSAccessKeyID string
|
||||
OSSAccessKeySecret string
|
||||
OSSKeyPrefix string
|
||||
OSSGrantTTL time.Duration
|
||||
OSSMaxAssetBytes int64
|
||||
StaticArtifactPath string
|
||||
AgentEndpointsFile string
|
||||
CallLogPath string
|
||||
CallLogPhoneKey string
|
||||
ARIURL string
|
||||
ARIWebsocketURL string
|
||||
ARIApplication string
|
||||
ARIUsername string
|
||||
ARIPassword string
|
||||
CallTarget string
|
||||
CallTrunkID string
|
||||
CallCallerID string
|
||||
CallMediaBind string
|
||||
CallMediaPort int
|
||||
CallRecordingDirectory string
|
||||
CallAISnapshotPath string
|
||||
CallTenantID string
|
||||
CallTenantKey string
|
||||
CallTaskID string
|
||||
CallTaskItemID string
|
||||
CallExecutionID string
|
||||
}
|
||||
|
||||
func FromEnv() (Config, error) {
|
||||
mediaPort, err := envInt("AGENT_CALL_MEDIA_PORT", 12000)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
return Config{
|
||||
Mode: envOr("SIP_GO_AGENT_MODE", "mock"),
|
||||
DBPath: envOr("DISPATCHER_DB", "dispatcher.db"),
|
||||
SpoolRoot: envOr("AGENT_SPOOL", "./spool"),
|
||||
RabbitURL: os.Getenv("RABBITMQ_URL"),
|
||||
Exchange: envOr("RABBITMQ_EXCHANGE", "agent-call.commands.v1"),
|
||||
OutboxBatch: 50,
|
||||
AgentID: envOr("AGENT_ID", "agent-local"),
|
||||
DispatcherID: envOr("DISPATCHER_ID", "dispatcher-local"),
|
||||
Version: envOr("AGENT_VERSION", "dev"),
|
||||
CellID: envOr("CELL_ID", "cell-local"),
|
||||
GRPCListen: os.Getenv("AGENT_GRPC_LISTEN"),
|
||||
DispatcherGRPCListen: os.Getenv("DISPATCHER_GRPC_LISTEN"),
|
||||
DispatcherGRPCEndpoint: os.Getenv("DISPATCHER_GRPC_ENDPOINT"),
|
||||
DispatcherGRPCServerName: envOr("DISPATCHER_GRPC_SERVER_NAME", os.Getenv("MTLS_SERVER_NAME")),
|
||||
DispatcherGRPCAllowedAgentIDs: os.Getenv("DISPATCHER_ALLOWED_AGENT_IDS"),
|
||||
MTLSCAFile: os.Getenv("MTLS_CA_FILE"),
|
||||
MTLSCertFile: os.Getenv("MTLS_CERT_FILE"),
|
||||
MTLSKeyFile: os.Getenv("MTLS_KEY_FILE"),
|
||||
MTLSServerName: os.Getenv("MTLS_SERVER_NAME"),
|
||||
MTLSPeerCertificateFingerprints: os.Getenv("MTLS_PEER_CERT_FINGERPRINTS"),
|
||||
OSSMaxAssetBytes: 64 << 20,
|
||||
StaticArtifactPath: os.Getenv("AGENT_STATIC_ARTIFACT"),
|
||||
AgentEndpointsFile: os.Getenv("DISPATCHER_AGENT_ENDPOINTS_FILE"),
|
||||
CallLogPath: os.Getenv("AGENT_CALL_BUSINESS_LOG"),
|
||||
CallLogPhoneKey: os.Getenv("AGENT_CALL_PHONE_LOG_KEY"),
|
||||
ARIURL: envOr("ARI_URL", "http://127.0.0.1:8088/ari"),
|
||||
ARIWebsocketURL: envOr("ARI_WS_URL", "ws://127.0.0.1:8088/ari/events"),
|
||||
ARIApplication: envOr("ARI_APPLICATION", "agent-call"),
|
||||
ARIUsername: os.Getenv("ARI_USERNAME"),
|
||||
ARIPassword: os.Getenv("ARI_PASSWORD"),
|
||||
CallTarget: os.Getenv("AGENT_CALL_TARGET"),
|
||||
CallTrunkID: os.Getenv("AGENT_CALL_TRUNK_ID"),
|
||||
CallCallerID: os.Getenv("AGENT_CALL_CALLER_ID"),
|
||||
CallMediaBind: envOr("AGENT_CALL_MEDIA_BIND", "127.0.0.1"),
|
||||
CallMediaPort: mediaPort,
|
||||
CallRecordingDirectory: envOr("AGENT_CALL_RECORDING_DIR", "./recordings"),
|
||||
CallAISnapshotPath: os.Getenv("AGENT_CALL_AI_SNAPSHOT"),
|
||||
CallTenantID: os.Getenv("AGENT_CALL_TENANT_ID"),
|
||||
CallTenantKey: os.Getenv("AGENT_CALL_TENANT_KEY"),
|
||||
CallTaskID: os.Getenv("AGENT_CALL_TASK_ID"),
|
||||
CallTaskItemID: os.Getenv("AGENT_CALL_TASK_ITEM_ID"),
|
||||
CallExecutionID: os.Getenv("AGENT_CALL_EXECUTION_ID"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c Config) Validate(role string) error {
|
||||
if c.Mode != "mock" && c.Mode != "mixed" && c.Mode != "real" {
|
||||
return fmt.Errorf("unsupported mode %q", c.Mode)
|
||||
}
|
||||
if role == "dispatcher" && strings.TrimSpace(c.DBPath) == "" {
|
||||
return errors.New("dispatcher DB path is required")
|
||||
}
|
||||
if role == "agent" && strings.TrimSpace(c.SpoolRoot) == "" {
|
||||
return errors.New("agent spool root is required")
|
||||
}
|
||||
if role == "agent" && strings.TrimSpace(c.CallLogPath) != "" && strings.TrimSpace(c.CallLogPhoneKey) == "" {
|
||||
return errors.New("AGENT_CALL_PHONE_LOG_KEY is required when AGENT_CALL_BUSINESS_LOG is set")
|
||||
}
|
||||
if role == "agent" && strings.TrimSpace(c.CallLogPhoneKey) != "" && len([]byte(c.CallLogPhoneKey)) < 16 {
|
||||
return errors.New("AGENT_CALL_PHONE_LOG_KEY must contain at least 16 bytes")
|
||||
}
|
||||
if role == "agent" && strings.TrimSpace(c.GRPCListen) != "" {
|
||||
for name, value := range map[string]string{"MTLS_CA_FILE": c.MTLSCAFile, "MTLS_CERT_FILE": c.MTLSCertFile, "MTLS_KEY_FILE": c.MTLSKeyFile} {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return fmt.Errorf("%s is required when AGENT_GRPC_LISTEN is enabled", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if role == "agent" && c.Mode == "real" && strings.TrimSpace(c.GRPCListen) == "" {
|
||||
return errors.New("real agent mode requires AGENT_GRPC_LISTEN")
|
||||
}
|
||||
if role == "agent" && c.Mode == "real" && strings.TrimSpace(c.StaticArtifactPath) == "" {
|
||||
return errors.New("real agent mode requires AGENT_STATIC_ARTIFACT")
|
||||
}
|
||||
if role == "agent" && strings.TrimSpace(c.MTLSPeerCertificateFingerprints) != "" {
|
||||
if _, err := ParseCertificateFingerprints(c.MTLSPeerCertificateFingerprints); err != nil {
|
||||
return fmt.Errorf("invalid MTLS_PEER_CERT_FINGERPRINTS: %w", err)
|
||||
}
|
||||
}
|
||||
if role == "agent" && strings.TrimSpace(c.DispatcherGRPCEndpoint) != "" {
|
||||
for name, value := range map[string]string{"MTLS_CA_FILE": c.MTLSCAFile, "MTLS_CERT_FILE": c.MTLSCertFile, "MTLS_KEY_FILE": c.MTLSKeyFile} {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return fmt.Errorf("%s is required when DISPATCHER_GRPC_ENDPOINT is enabled", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if role == "dispatcher" && strings.TrimSpace(c.DispatcherGRPCListen) != "" {
|
||||
for name, value := range map[string]string{"MTLS_CA_FILE": c.MTLSCAFile, "MTLS_CERT_FILE": c.MTLSCertFile, "MTLS_KEY_FILE": c.MTLSKeyFile} {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return fmt.Errorf("%s is required when DISPATCHER_GRPC_LISTEN is enabled", name)
|
||||
}
|
||||
}
|
||||
for name, value := range map[string]string{"oss.region": c.OSSRegion, "oss.endpoint": c.OSSEndpoint, "oss.bucket": c.OSSBucket, "oss.access_key_id_env reference": c.OSSAccessKeyID, "oss.access_key_secret_env reference": c.OSSAccessKeySecret} {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return fmt.Errorf("%s must be supplied by the Dispatcher configuration file", name)
|
||||
}
|
||||
}
|
||||
if strings.TrimSpace(c.DispatcherGRPCAllowedAgentIDs) == "" {
|
||||
return errors.New("DISPATCHER_ALLOWED_AGENT_IDS is required when Dispatcher gRPC is enabled")
|
||||
}
|
||||
fingerprints, err := ParseCertificateFingerprints(c.MTLSPeerCertificateFingerprints)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid MTLS_PEER_CERT_FINGERPRINTS: %w", err)
|
||||
}
|
||||
if len(fingerprints) == 0 {
|
||||
return errors.New("MTLS_PEER_CERT_FINGERPRINTS is required when Dispatcher gRPC is enabled")
|
||||
}
|
||||
}
|
||||
if role == "dispatcher" && strings.TrimSpace(c.AgentEndpointsFile) != "" {
|
||||
for name, value := range map[string]string{"MTLS_CA_FILE": c.MTLSCAFile, "MTLS_CERT_FILE": c.MTLSCertFile, "MTLS_KEY_FILE": c.MTLSKeyFile} {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return fmt.Errorf("%s is required when DISPATCHER_AGENT_ENDPOINTS_FILE is enabled", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if c.Mode == "real" && strings.TrimSpace(c.RabbitURL) == "" && role == "dispatcher" {
|
||||
return errors.New("real dispatcher mode requires RABBITMQ_URL")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func envOr(key, fallback string) string {
|
||||
if value := strings.TrimSpace(os.Getenv(key)); value != "" {
|
||||
return value
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
func envInt(key string, fallback int) (int, error) {
|
||||
value := strings.TrimSpace(os.Getenv(key))
|
||||
if value == "" {
|
||||
return fallback, nil
|
||||
}
|
||||
parsed, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("invalid %s: %w", key, err)
|
||||
}
|
||||
return parsed, nil
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConfigRejectsRealDispatcherWithoutBroker(t *testing.T) {
|
||||
c, err := FromEnv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c.Mode, c.RabbitURL, c.DBPath = "real", "", ":memory:"
|
||||
if err := c.Validate("dispatcher"); err == nil {
|
||||
t.Fatal("expected real-mode broker requirement")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromEnvRejectsInvalidMediaPort(t *testing.T) {
|
||||
t.Setenv("AGENT_CALL_MEDIA_PORT", "not-a-port")
|
||||
if _, err := FromEnv(); err == nil || !strings.Contains(err.Error(), "AGENT_CALL_MEDIA_PORT") {
|
||||
t.Fatalf("expected explicit media port error, got %v", err)
|
||||
}
|
||||
t.Setenv("AGENT_CALL_MEDIA_PORT", "43210")
|
||||
cfg, err := FromEnv()
|
||||
if err != nil || cfg.CallMediaPort != 43210 {
|
||||
t.Fatalf("expected configured port, got %d, %v", cfg.CallMediaPort, err)
|
||||
}
|
||||
t.Setenv("AGENT_CALL_MEDIA_PORT", "")
|
||||
cfg, err = FromEnv()
|
||||
if err != nil || cfg.CallMediaPort != 12000 {
|
||||
t.Fatalf("expected default port, got %d, %v", cfg.CallMediaPort, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigAcceptsMockAgent(t *testing.T) {
|
||||
c := Config{Mode: "mock", SpoolRoot: t.TempDir()}
|
||||
if err := c.Validate("agent"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigLoadsUnifiedDispatcherGRPCSettings(t *testing.T) {
|
||||
t.Setenv("DISPATCHER_GRPC_LISTEN", "127.0.0.1:19443")
|
||||
t.Setenv("DISPATCHER_GRPC_ENDPOINT", "dispatcher.test:19443")
|
||||
t.Setenv("DISPATCHER_GRPC_SERVER_NAME", "dispatcher.test")
|
||||
t.Setenv("DISPATCHER_ALLOWED_AGENT_IDS", "agent-cell-a,agent-cell-b")
|
||||
c, err := FromEnv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if c.DispatcherGRPCListen != "127.0.0.1:19443" || c.DispatcherGRPCEndpoint != "dispatcher.test:19443" || c.DispatcherGRPCServerName != "dispatcher.test" || c.DispatcherGRPCAllowedAgentIDs != "agent-cell-a,agent-cell-b" {
|
||||
t.Fatalf("unified Dispatcher gRPC settings were not loaded: %+v", c)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigRequiresPhoneLogPair(t *testing.T) {
|
||||
c := Config{Mode: "mock", SpoolRoot: t.TempDir(), CallLogPath: "calls.jsonl"}
|
||||
if err := c.Validate("agent"); err == nil {
|
||||
t.Fatal("expected phone log key requirement")
|
||||
}
|
||||
c.CallLogPhoneKey = "0123456789abcdef"
|
||||
if err := c.Validate("agent"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c.CallLogPath = ""
|
||||
if err := c.Validate("agent"); err != nil {
|
||||
t.Fatalf("key-only configuration should use spool fallback: %v", err)
|
||||
}
|
||||
c.CallLogPhoneKey = "short"
|
||||
if err := c.Validate("agent"); err == nil {
|
||||
t.Fatal("expected phone log key length requirement")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigRequiresAgentTLSFilesWhenRPCIsEnabled(t *testing.T) {
|
||||
c := Config{Mode: "mock", SpoolRoot: t.TempDir(), GRPCListen: "127.0.0.1:19090"}
|
||||
if err := c.Validate("agent"); err == nil {
|
||||
t.Fatal("expected mTLS file requirement")
|
||||
}
|
||||
c.MTLSCAFile, c.MTLSCertFile, c.MTLSKeyFile = "ca.pem", "agent.pem", "agent.key"
|
||||
if err := c.Validate("agent"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c.Mode, c.GRPCListen = "real", ""
|
||||
if err := c.Validate("agent"); err == nil {
|
||||
t.Fatal("expected real agent listener requirement")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigRequiresStaticArtifactForRealAgent(t *testing.T) {
|
||||
c := Config{Mode: "real", SpoolRoot: t.TempDir(), GRPCListen: "127.0.0.1:19090", MTLSCAFile: "ca.pem", MTLSCertFile: "agent.pem", MTLSKeyFile: "agent.key"}
|
||||
if err := c.Validate("agent"); err == nil {
|
||||
t.Fatal("expected static artifact requirement")
|
||||
}
|
||||
c.StaticArtifactPath = "artifact.json"
|
||||
if err := c.Validate("agent"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,21 @@ func TestLoadCurrentOSSConfigRequiresBoundLocalHTTPSAndExplicitCredentials(t *te
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadCurrentOSSConfigIgnoresLegacyOSSEnvironment(t *testing.T) {
|
||||
for _, key := range []string{"DISPATCHER_OSS_REGION", "DISPATCHER_OSS_ENDPOINT", "DISPATCHER_OSS_BUCKET", "DISPATCHER_OSS_ACCESS_KEY_ID", "DISPATCHER_OSS_ACCESS_KEY_SECRET", "DISPATCHER_OSS_KEY_PREFIX", "DISPATCHER_OSS_GRANT_TTL_SECONDS"} {
|
||||
t.Setenv(key, "legacy-value")
|
||||
}
|
||||
t.Setenv("MOCK_OSS_KEY_ID", "isolated-id")
|
||||
t.Setenv("MOCK_OSS_KEY_SECRET", "isolated-secret")
|
||||
configuration, err := LoadCurrentOSSConfig(writeMockOSSDeployment(t, mockOSSDeployment), dispatcherFixtureID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if configuration.Endpoint != "https://127.0.0.1:19445" || configuration.Region != "cn-mock" || configuration.Bucket != "mock-bucket" || configuration.KeyPrefix != "approved" || configuration.GrantTTL != 15*time.Minute || configuration.AccessKeyID != "isolated-id" || configuration.AccessKeySecret != "isolated-secret" {
|
||||
t.Fatal("legacy OSS environment overrode the approved file and credential references")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadCurrentOSSConfigRejectsUnapprovedDeploymentWithoutLeakingCredentials(t *testing.T) {
|
||||
t.Setenv("MOCK_OSS_KEY_ID", "isolated-id")
|
||||
t.Setenv("MOCK_OSS_KEY_SECRET", "isolated-secret")
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package config
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestLegacyOSSEnvironmentIsNotAConfigurationSource(t *testing.T) {
|
||||
for _, key := range []string{"DISPATCHER_OSS_REGION", "DISPATCHER_OSS_ENDPOINT", "DISPATCHER_OSS_BUCKET", "DISPATCHER_OSS_ACCESS_KEY_ID", "DISPATCHER_OSS_ACCESS_KEY_SECRET", "DISPATCHER_OSS_KEY_PREFIX"} {
|
||||
t.Setenv(key, "legacy-value")
|
||||
}
|
||||
t.Setenv("DISPATCHER_OSS_GRANT_TTL_SECONDS", "1")
|
||||
cfg, err := FromEnv()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cfg.OSSRegion != "" || cfg.OSSEndpoint != "" || cfg.OSSBucket != "" || cfg.OSSAccessKeyID != "" || cfg.OSSAccessKeySecret != "" || cfg.OSSKeyPrefix != "" || cfg.OSSGrantTTL != 0 {
|
||||
t.Fatal("legacy environment still supplies OSS settings before the required JSON file")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user