diff --git a/pkg/core/certmgr/providers/baishan-cdn/baishan_cdn_test.go b/pkg/core/certmgr/providers/baishan-cdn/baishan_cdn_test.go index 1abbf1ba..51befb01 100644 --- a/pkg/core/certmgr/providers/baishan-cdn/baishan_cdn_test.go +++ b/pkg/core/certmgr/providers/baishan-cdn/baishan_cdn_test.go @@ -32,6 +32,10 @@ func TestProvider(t *testing.T) { fp.Parse() t.Run("Upload", func(t *testing.T) { + if fTestCertPath == "" || fTestKeyPath == "" || fApiToken == "" { + t.Skip("skip integration test because BAISHANCDN_TESTCERTPATH, BAISHANCDN_TESTKEYPATH or BAISHANCDN_APITOKEN is empty") + } + provider, err := impl.NewCertmgr(&impl.CertmgrConfig{ ApiToken: fApiToken, }) diff --git a/pkg/core/deployer/providers/baishan-cdn/baishan_cdn.go b/pkg/core/deployer/providers/baishan-cdn/baishan_cdn.go index fbf7ecaa..1291506e 100644 --- a/pkg/core/deployer/providers/baishan-cdn/baishan_cdn.go +++ b/pkg/core/deployer/providers/baishan-cdn/baishan_cdn.go @@ -5,6 +5,8 @@ import ( "encoding/json" "fmt" "log/slog" + "strings" + "time" "github.com/samber/lo" @@ -110,10 +112,12 @@ func (d *Deployer) deployToDomain(ctx context.Context, certPEM, privkeyPEM strin d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) } + getDomainConfigDomain := d.getFilteredDomainForAPI("cdn.GetDomainConfig", d.config.Domain) + // 查询域名配置 // REF: https://portal.baishancloud.com/track/document/api/1/1065 getDomainConfigReq := &baishansdk.GetDomainConfigRequest{ - Domains: lo.ToPtr(d.config.Domain), + Domains: lo.ToPtr(getDomainConfigDomain), Config: []*string{lo.ToPtr("https")}, } getDomainConfigResp, err := d.sdkClient.GetDomainConfigWithContext(ctx, getDomainConfigReq) @@ -121,13 +125,15 @@ func (d *Deployer) deployToDomain(ctx context.Context, certPEM, privkeyPEM strin if err != nil { return fmt.Errorf("failed to execute sdk request 'cdn.GetDomainConfig': %w", err) } else if len(getDomainConfigResp.Data) == 0 { - return fmt.Errorf("could not find domain '%s'", d.config.Domain) + return fmt.Errorf("could not find domain '%s'", getDomainConfigDomain) } + setDomainConfigDomain := d.getFilteredDomainForAPI("cdn.SetDomainConfig", d.config.Domain) + // 设置域名配置 // REF: https://portal.baishancloud.com/track/document/api/1/1045 setDomainConfigReq := &baishansdk.SetDomainConfigRequest{ - Domains: lo.ToPtr(d.config.Domain), + Domains: lo.ToPtr(setDomainConfigDomain), Config: &baishansdk.DomainConfig{ Https: &baishansdk.DomainConfigHttps{ CertId: json.Number(upres.CertId), @@ -146,6 +152,29 @@ func (d *Deployer) deployToDomain(ctx context.Context, certPEM, privkeyPEM strin return nil } +func (d *Deployer) getFilteredDomainForAPI(apiIdentifier string, domain string) string { + filteredDomain := filterWildcardDomainForBaishanAPI(domain) + if filteredDomain != domain { + d.logger.Info( + "baishan cdn wildcard domain prefix filtered", + slog.String("apiIdentifier", apiIdentifier), + slog.String("originalDomain", domain), + slog.String("filteredDomain", filteredDomain), + slog.Time("filterTime", time.Now().UTC()), + ) + } + + return filteredDomain +} + +func filterWildcardDomainForBaishanAPI(domain string) string { + if strings.HasPrefix(domain, "*.") { + return strings.TrimPrefix(domain, "*") + } + + return domain +} + func (d *Deployer) deployToCertificate(ctx context.Context, certPEM, privkeyPEM string) error { if d.config.CertificateId == "" { return fmt.Errorf("config `certificateId` is required") diff --git a/pkg/core/deployer/providers/baishan-cdn/baishan_cdn_test.go b/pkg/core/deployer/providers/baishan-cdn/baishan_cdn_test.go index 32ff9300..cae1497e 100644 --- a/pkg/core/deployer/providers/baishan-cdn/baishan_cdn_test.go +++ b/pkg/core/deployer/providers/baishan-cdn/baishan_cdn_test.go @@ -35,6 +35,10 @@ func TestProvider(t *testing.T) { fp.Parse() t.Run("Deploy", func(t *testing.T) { + if fTestCertPath == "" || fTestKeyPath == "" || fApiToken == "" || fDomain == "" { + t.Skip("skip integration test because BAISHANCDN_TESTCERTPATH, BAISHANCDN_TESTKEYPATH, BAISHANCDN_APITOKEN or BAISHANCDN_DOMAIN is empty") + } + provider, err := impl.NewDeployer(&impl.DeployerConfig{ ApiToken: fApiToken, DomainMatchPattern: impl.DOMAIN_MATCH_PATTERN_EXACT, diff --git a/pkg/core/deployer/providers/baishan-cdn/domain_filter_test.go b/pkg/core/deployer/providers/baishan-cdn/domain_filter_test.go new file mode 100644 index 00000000..3efeaab6 --- /dev/null +++ b/pkg/core/deployer/providers/baishan-cdn/domain_filter_test.go @@ -0,0 +1,120 @@ +package baishancdn + +import ( + "bytes" + "encoding/json" + "log/slog" + "testing" +) + +func TestFilterWildcardDomainForBaishanAPI(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + domain string + want string + }{ + { + name: "wildcard domain", + domain: "*.example.com", + want: ".example.com", + }, + { + name: "already compliant wildcard domain", + domain: ".example.com", + want: ".example.com", + }, + { + name: "normal domain", + domain: "example.com", + want: "example.com", + }, + { + name: "non wildcard prefix", + domain: "*example.com", + want: "*example.com", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + got := filterWildcardDomainForBaishanAPI(tt.domain) + if got != tt.want { + t.Fatalf("filterWildcardDomainForBaishanAPI(%q) = %q, want %q", tt.domain, got, tt.want) + } + }) + } +} + +func TestGetFilteredDomainForAPI(t *testing.T) { + t.Parallel() + + t.Run("logs filtered wildcard domain", func(t *testing.T) { + var logbuf bytes.Buffer + logger := slog.New(slog.NewJSONHandler(&logbuf, &slog.HandlerOptions{Level: slog.LevelDebug})) + + deployer, err := NewDeployer(&DeployerConfig{ + ApiToken: "test-token", + Domain: "*.example.com", + }) + if err != nil { + t.Fatalf("NewDeployer() error = %v", err) + } + deployer.SetLogger(logger) + + got := deployer.getFilteredDomainForAPI("cdn.GetDomainConfig", deployer.config.Domain) + if got != ".example.com" { + t.Fatalf("getFilteredDomainForAPI() = %q, want %q", got, ".example.com") + } + + var record map[string]any + if err := json.Unmarshal(logbuf.Bytes(), &record); err != nil { + t.Fatalf("failed to unmarshal log record: %v", err) + } + + if record["level"] != "INFO" { + t.Fatalf("log level = %v, want %q", record["level"], "INFO") + } + if record["msg"] != "baishan cdn wildcard domain prefix filtered" { + t.Fatalf("log msg = %v, want %q", record["msg"], "baishan cdn wildcard domain prefix filtered") + } + if record["apiIdentifier"] != "cdn.GetDomainConfig" { + t.Fatalf("log apiIdentifier = %v, want %q", record["apiIdentifier"], "cdn.GetDomainConfig") + } + if record["originalDomain"] != "*.example.com" { + t.Fatalf("log originalDomain = %v, want %q", record["originalDomain"], "*.example.com") + } + if record["filteredDomain"] != ".example.com" { + t.Fatalf("log filteredDomain = %v, want %q", record["filteredDomain"], ".example.com") + } + if _, ok := record["filterTime"]; !ok { + t.Fatal("log missing filterTime field") + } + }) + + t.Run("does not log for normal domain", func(t *testing.T) { + var logbuf bytes.Buffer + logger := slog.New(slog.NewJSONHandler(&logbuf, &slog.HandlerOptions{Level: slog.LevelDebug})) + + deployer, err := NewDeployer(&DeployerConfig{ + ApiToken: "test-token", + Domain: "example.com", + }) + if err != nil { + t.Fatalf("NewDeployer() error = %v", err) + } + deployer.SetLogger(logger) + + got := deployer.getFilteredDomainForAPI("cdn.GetDomainConfig", deployer.config.Domain) + if got != "example.com" { + t.Fatalf("getFilteredDomainForAPI() = %q, want %q", got, "example.com") + } + if logbuf.Len() != 0 { + t.Fatalf("expected no log output, got %s", logbuf.String()) + } + }) +}