diff --git a/go.mod b/go.mod index b64a65404..23f044244 100644 --- a/go.mod +++ b/go.mod @@ -82,6 +82,9 @@ require ( github.com/volcengine/volcengine-go-sdk v1.2.42 github.com/wneessen/go-mail v0.8.1 github.com/xhit/go-str2duration/v2 v2.1.0 + github.com/yandex-cloud/go-genproto v0.99.0 + github.com/yandex-cloud/go-sdk/services/certificatemanager v0.0.81 + github.com/yandex-cloud/go-sdk/v2 v2.142.0 github.com/yunify/qingcloud-sdk-go v2.0.0-alpha.38+incompatible github.com/zenlayer/zenlayercloud-sdk-go v0.2.46 gitlab.ecloud.com/ecloud/ecloudsdkcloudcore v1.0.0 @@ -257,9 +260,7 @@ require ( github.com/spf13/cast v1.10.0 // indirect github.com/tinylib/msgp v1.6.4 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect - github.com/yandex-cloud/go-genproto v0.84.0 // indirect github.com/yandex-cloud/go-sdk/services/dns v0.0.65 // indirect - github.com/yandex-cloud/go-sdk/v2 v2.114.0 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect github.com/zeebo/xxh3 v1.1.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect diff --git a/go.sum b/go.sum index dd9afcde6..85b5f987f 100644 --- a/go.sum +++ b/go.sum @@ -953,12 +953,14 @@ github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6 github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= -github.com/yandex-cloud/go-genproto v0.84.0 h1:lzptMyo1wleGBGNkkEb8bzpgbnW3xPlha0f94g4prBE= -github.com/yandex-cloud/go-genproto v0.84.0/go.mod h1:0LDD/IZLIUIV4iPH+YcF+jysO3jkSvADFGm4dCAuwQo= +github.com/yandex-cloud/go-genproto v0.99.0 h1:mRJPHWj186U0Ww1sE8E0U727MvWM2gv1MqUjBbiVgjE= +github.com/yandex-cloud/go-genproto v0.99.0/go.mod h1:0LDD/IZLIUIV4iPH+YcF+jysO3jkSvADFGm4dCAuwQo= +github.com/yandex-cloud/go-sdk/services/certificatemanager v0.0.81 h1:tMdRkO7LakoH2PsUixoGuCt5cdQDUdFo+7sxaItNUjA= +github.com/yandex-cloud/go-sdk/services/certificatemanager v0.0.81/go.mod h1:VKW98WxZFwD04FSgniL5uObKaguR9zBhvwv770Ot2SM= github.com/yandex-cloud/go-sdk/services/dns v0.0.65 h1:7jhZMHsK1GThGzAKC7Mjq8wu/Xbr/TbhRUoNFzot4K4= github.com/yandex-cloud/go-sdk/services/dns v0.0.65/go.mod h1:Qm5HbUbmFI2Y8eDv8tcwr7OHgNumHuN/RtK3icd3bYU= -github.com/yandex-cloud/go-sdk/v2 v2.114.0 h1:gx03giWYrzMhgpFK4NJNuOnASSgdCuv4nkLUFejnCSs= -github.com/yandex-cloud/go-sdk/v2 v2.114.0/go.mod h1:XrF79/a3olVf0YUUCMYePI8Ik0m4KDcQlTooHGGZJB0= +github.com/yandex-cloud/go-sdk/v2 v2.142.0 h1:4l02xAC/B0GNWLs+SAWiLNX2iah/DHb7t1k62mYl8tU= +github.com/yandex-cloud/go-sdk/v2 v2.142.0/go.mod h1:cvtog6S0f82ju6ZqzQ6eMYz96P4cflml6R+W4/wDFEE= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= diff --git a/internal/certmgmt/deployers/sp_yandexcloud_certificatemanager.go b/internal/certmgmt/deployers/sp_yandexcloud_certificatemanager.go new file mode 100644 index 000000000..a51bc4ea7 --- /dev/null +++ b/internal/certmgmt/deployers/sp_yandexcloud_certificatemanager.go @@ -0,0 +1,26 @@ +package deployers + +import ( + "fmt" + + "github.com/certimate-go/certimate/internal/domain" + "github.com/certimate-go/certimate/pkg/core" + dplyimpl "github.com/certimate-go/certimate/pkg/core/deployer/providers/yandexcloud-certificatemanager" + xmaps "github.com/certimate-go/certimate/pkg/utils/maps" +) + +func init() { + Registries.MustRegister(domain.DeploymentProviderTypeYandexCloudCertificateManager, func(options *ProviderFactoryOptions) (core.Deployer, error) { + credentials := domain.AccessConfigForYandexCloud{} + if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + provider, err := dplyimpl.NewDeployer(&dplyimpl.DeployerConfig{ + FolderId: credentials.FolderId, + ServiceAccountKey: credentials.ServiceAccountKey, + CertificateId: xmaps.GetString(options.ProviderExtendedConfig, "certificateId"), + }) + return provider, err + }) +} diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 0d88f5dcc..707705cbb 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -467,6 +467,7 @@ const ( DeploymentProviderTypeWangsuCDNPro = DeploymentProviderType(AccessProviderTypeWangsu + "-cdnpro") DeploymentProviderTypeWangsuCertificate = DeploymentProviderType(AccessProviderTypeWangsu + "-certificate") DeploymentProviderTypeWebhook = DeploymentProviderType(AccessProviderTypeWebhook) + DeploymentProviderTypeYandexCloudCertificateManager = DeploymentProviderType(AccessProviderTypeYandexCloud + "-certificatemanager") DeploymentProviderTypeZenlayerCDN = DeploymentProviderType(AccessProviderTypeZenlayer + "-cdn") DeploymentProviderTypeZenlayerGA = DeploymentProviderType(AccessProviderTypeZenlayer + "-ga") ) diff --git a/pkg/core/certmgr/providers/googlecloud-certificatemanager/googlecloud_certificatemanager_test.go b/pkg/core/certmgr/providers/googlecloud-certificatemanager/googlecloud_certificatemanager_test.go index 48af2cdb6..389c11a18 100644 --- a/pkg/core/certmgr/providers/googlecloud-certificatemanager/googlecloud_certificatemanager_test.go +++ b/pkg/core/certmgr/providers/googlecloud-certificatemanager/googlecloud_certificatemanager_test.go @@ -1,6 +1,7 @@ package googlecloudcertificatemanager_test import ( + "os" "testing" impl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/googlecloud-certificatemanager" @@ -11,12 +12,14 @@ var ( fp = tester.Args("GOOGLECLOUDCERTIFICATEMANAGER_") fTestCertPath string fTestKeyPath string + fProjectId string fServiceAccountKey string ) func init() { fp.DefineString(&fTestCertPath, "TESTCERTPATH") fp.DefineString(&fTestKeyPath, "TESTKEYPATH") + fp.DefineString(&fProjectId, "PROJECTID") fp.DefineString(&fServiceAccountKey, "SERVICEACCOUNTKEY") } @@ -26,13 +29,20 @@ Shell command to run this test: go test -v ./googlecloud_certificatemanager_test.go -args \ --GOOGLECLOUDCERTIFICATEMANAGER_TESTCERTPATH="/path/to/your-test-cert.pem" \ --GOOGLECLOUDCERTIFICATEMANAGER_TESTKEYPATH="/path/to/your-test-key.pem" \ + --GOOGLECLOUDCERTIFICATEMANAGER_PROJECTID="your-project-id" \ --GOOGLECLOUDCERTIFICATEMANAGER_SERVICEACCOUNTKEY="{...}" */ func TestProvider(t *testing.T) { fp.Parse() + if fServiceAccountKeyStat, err := os.Stat(fServiceAccountKey); err == nil && !fServiceAccountKeyStat.IsDir() { + fServiceAccountKeyBytes, _ := os.ReadFile(fServiceAccountKey) + fServiceAccountKey = string(fServiceAccountKeyBytes) + } + t.Run("Upload", func(t *testing.T) { provider, err := impl.NewCertmgr(&impl.CertmgrConfig{ + ProjectId: fProjectId, ServiceAccountKey: fServiceAccountKey, }) if err != nil { diff --git a/pkg/core/certmgr/providers/yandexcloud-certificatemanager/yandexcloud_certificatemanager.go b/pkg/core/certmgr/providers/yandexcloud-certificatemanager/yandexcloud_certificatemanager.go new file mode 100644 index 000000000..92dcf6be2 --- /dev/null +++ b/pkg/core/certmgr/providers/yandexcloud-certificatemanager/yandexcloud_certificatemanager.go @@ -0,0 +1,227 @@ +package yandexcloudcertificatemanager + +import ( + "context" + "encoding/json" + "fmt" + "log/slog" + "strings" + "time" + + yccertificatemanagerproto "github.com/yandex-cloud/go-genproto/yandex/cloud/certificatemanager/v1" + yccertificatemanager "github.com/yandex-cloud/go-sdk/services/certificatemanager/v1" + ycsdk "github.com/yandex-cloud/go-sdk/v2" + yccreds "github.com/yandex-cloud/go-sdk/v2/credentials" + yciamkey "github.com/yandex-cloud/go-sdk/v2/pkg/iamkey" + ycoptions "github.com/yandex-cloud/go-sdk/v2/pkg/options" + + "github.com/certimate-go/certimate/pkg/core" + xcert "github.com/certimate-go/certimate/pkg/utils/cert" +) + +type ( + Provider = core.Certmgr + UploadResult = core.CertmgrUploadResult + ReplaceResult = core.CertmgrReplaceResult +) + +type CertmgrConfig struct { + // Yandex Cloud 文件夹 ID。 + FolderId string `json:"folderId"` + // Yandex Cloud 服务账号授权密钥。 + ServiceAccountKey string `json:"serviceAccountKey"` +} + +type Certmgr struct { + config *CertmgrConfig + logger *slog.Logger + sdkClient yccertificatemanager.CertificateClient +} + +var _ Provider = (*Certmgr)(nil) + +func NewCertmgr(config *CertmgrConfig) (*Certmgr, error) { + if config == nil { + return nil, fmt.Errorf("the configuration of the certmgr provider is nil") + } + + client, err := createSDKClient(config.ServiceAccountKey) + if err != nil { + return nil, fmt.Errorf("could not create client: %w", err) + } + + return &Certmgr{ + config: config, + logger: slog.Default(), + sdkClient: client, + }, nil +} + +func (c *Certmgr) SetLogger(logger *slog.Logger) { + if logger == nil { + c.logger = slog.New(slog.DiscardHandler) + } else { + c.logger = logger + } +} + +func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*UploadResult, error) { + // 解析证书内容 + certX509, err := xcert.ParseCertificateFromPEM(certPEM) + if err != nil { + return nil, err + } + + // 提取服务器证书和中间证书 + serverCertPEM, issuerCertPEM, err := xcert.ExtractCertificatesFromPEM(certPEM) + if err != nil { + return nil, fmt.Errorf("failed to extract certs: %w", err) + } + + // 获取证书列表,避免重复上传 + // REF: https://yandex.cloud/en/docs/certificate-manager/api-ref/Certificate/list + listCertificatesPageToken := "" + listCertificatesPageSize := 100 + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + + listCertificatesReq := &yccertificatemanagerproto.ListCertificatesRequest{ + FolderId: c.config.FolderId, + PageSize: int64(listCertificatesPageSize), + PageToken: listCertificatesPageToken, + } + listCertificatesResp, err := c.sdkClient.List(ctx, listCertificatesReq) + c.logger.Debug("sdk request 'certificatemanager.certificate.list'", slog.Any("request", listCertificatesReq), slog.Any("response", listCertificatesResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'certificatemanager.certificate.list': %w", err) + } + + for _, certItem := range listCertificatesResp.Certificates { + // 对比证书备用名称 + if !strings.EqualFold(strings.Join(certX509.DNSNames, ","), strings.Join(certItem.Domains, ",")) { + continue + } + + // 对比证书主题 DN + if certX509.Subject.String() != certItem.Subject { + continue + } + + // 对比证书颁发者 DN + if certX509.Issuer.String() != certItem.Issuer { + continue + } + + // 对比证书序列号 + if !strings.EqualFold(certX509.SerialNumber.Text(16), certItem.Serial) { + continue + } + + // 对比证书有效期 + if certItem.NotBefore != nil && certX509.NotBefore.Unix() != certItem.NotBefore.AsTime().Unix() { + continue + } else if certItem.NotAfter != nil && certX509.NotAfter.Unix() != certItem.NotAfter.AsTime().Unix() { + continue + } + + // 如果以上信息都一致,则视为已存在相同证书,直接返回 + c.logger.Info("ssl certificate already exists") + return &UploadResult{ + CertId: certItem.Id, + CertName: certItem.Name, + }, nil + } + + if len(listCertificatesResp.Certificates) == 0 || listCertificatesResp.NextPageToken == "" { + break + } + + listCertificatesPageToken = listCertificatesResp.NextPageToken + } + + // 创建证书 + // REF: https://yandex.cloud/en/docs/certificate-manager/api-ref/Certificate/create + createCertificateReq := &yccertificatemanagerproto.CreateCertificateRequest{ + FolderId: c.config.FolderId, + Name: fmt.Sprintf("certimate-%d", time.Now().UnixMilli()), + Description: "upload from Certimate", + Certificate: serverCertPEM, + Chain: issuerCertPEM, + PrivateKey: privkeyPEM, + } + createCertificateResp, err := c.sdkClient.Create(ctx, createCertificateReq) + c.logger.Debug("sdk request 'certificatemanager.certificate.create'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'certificatemanager.certificate.create': %w", err) + } + + return &UploadResult{ + CertId: createCertificateResp.Response().Id, + CertName: createCertificateResp.Response().Name, + }, nil +} + +func (c *Certmgr) Replace(ctx context.Context, certIdOrName string, certPEM, privkeyPEM string) (*ReplaceResult, error) { + // 提取服务器证书和中间证书 + serverCertPEM, issuerCertPEM, err := xcert.ExtractCertificatesFromPEM(certPEM) + if err != nil { + return nil, fmt.Errorf("failed to extract certs: %w", err) + } + + // 获取证书 + // REF: https://yandex.cloud/en/docs/certificate-manager/api-ref/Certificate/get + getCertificateReq := &yccertificatemanagerproto.GetCertificateRequest{ + CertificateId: certIdOrName, + View: yccertificatemanagerproto.CertificateView_BASIC, + } + getCertificateResp, err := c.sdkClient.Get(ctx, getCertificateReq) + c.logger.Debug("sdk request 'certificatemanager.certificate.get'", slog.Any("request", getCertificateReq), slog.Any("response", getCertificateResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'certificatemanager.certificate.get': %w", err) + } + + // 更新证书 + // REF: https://yandex.cloud/en/docs/certificate-manager/api-ref/Certificate/update + updateCertificateReq := &yccertificatemanagerproto.UpdateCertificateRequest{ + CertificateId: certIdOrName, + Name: getCertificateResp.Name, + Description: getCertificateResp.Description, + Labels: getCertificateResp.Labels, + Certificate: serverCertPEM, + Chain: issuerCertPEM, + PrivateKey: privkeyPEM, + DeletionProtection: getCertificateResp.DeletionProtection, + } + updateCertificateResp, err := c.sdkClient.Update(ctx, updateCertificateReq) + c.logger.Debug("sdk request 'certificatemanager.certificate.update'", slog.Any("request", updateCertificateReq), slog.Any("response", updateCertificateResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'certificatemanager.certificate.update': %w", err) + } + + return &ReplaceResult{}, nil +} + +func createSDKClient(serviceAccountKey string) (yccertificatemanager.CertificateClient, error) { + saKey := []byte(serviceAccountKey) + saConf := &yciamkey.Key{} + if err := json.Unmarshal(saKey, saConf); err != nil { + return nil, fmt.Errorf("unable to acquire service account config: %w", err) + } + + creds, err := yccreds.ServiceAccountKey(saConf) + if err != nil { + return nil, err + } + + sdk, err := ycsdk.Build(context.Background(), ycoptions.WithCredentials(creds)) + if err != nil { + return nil, err + } + + client := yccertificatemanager.NewCertificateClient(sdk) + return client, nil +} diff --git a/pkg/core/certmgr/providers/yandexcloud-certificatemanager/yandexcloud_certificatemanager_test.go b/pkg/core/certmgr/providers/yandexcloud-certificatemanager/yandexcloud_certificatemanager_test.go new file mode 100644 index 000000000..798e50925 --- /dev/null +++ b/pkg/core/certmgr/providers/yandexcloud-certificatemanager/yandexcloud_certificatemanager_test.go @@ -0,0 +1,55 @@ +package yandexcloudcertificatemanager_test + +import ( + "os" + "testing" + + impl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/yandexcloud-certificatemanager" + tester "github.com/certimate-go/certimate/pkg/core/certmgr/testing" +) + +var ( + fp = tester.Args("YANDEXCLOUDCERTIFICATEMANAGER_") + fTestCertPath string + fTestKeyPath string + fFolderId string + fServiceAccountKey string +) + +func init() { + fp.DefineString(&fTestCertPath, "TESTCERTPATH") + fp.DefineString(&fTestKeyPath, "TESTKEYPATH") + fp.DefineString(&fFolderId, "FOLDERID") + fp.DefineString(&fServiceAccountKey, "SERVICEACCOUNTKEY") +} + +/* +Shell command to run this test: + + go test -v ./yandexcloud_certificatemanager_test.go -args \ + --YANDEXCLOUDCERTIFICATEMANAGER_TESTCERTPATH="/path/to/your-test-cert.pem" \ + --YANDEXCLOUDCERTIFICATEMANAGER_TESTKEYPATH="/path/to/your-test-key.pem" \ + --YANDEXCLOUDCERTIFICATEMANAGER_FOLDERID="your-folder-id" \ + --YANDEXCLOUDCERTIFICATEMANAGER_SERVICEACCOUNTKEY="{...}" +*/ +func TestProvider(t *testing.T) { + fp.Parse() + + if fServiceAccountKeyStat, err := os.Stat(fServiceAccountKey); err == nil && !fServiceAccountKeyStat.IsDir() { + fServiceAccountKeyBytes, _ := os.ReadFile(fServiceAccountKey) + fServiceAccountKey = string(fServiceAccountKeyBytes) + } + + t.Run("Upload", func(t *testing.T) { + provider, err := impl.NewCertmgr(&impl.CertmgrConfig{ + FolderId: fFolderId, + ServiceAccountKey: fServiceAccountKey, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + tester.TestUpload(t, provider, tester.TestUploadArgs{CertPath: fTestCertPath, KeyPath: fTestKeyPath}) + }) +} diff --git a/pkg/core/deployer/providers/k8s-secret/k8s_secret_test.go b/pkg/core/deployer/providers/k8s-secret/k8s_secret_test.go index 3c4141ad5..f63f14484 100644 --- a/pkg/core/deployer/providers/k8s-secret/k8s_secret_test.go +++ b/pkg/core/deployer/providers/k8s-secret/k8s_secret_test.go @@ -1,6 +1,7 @@ package k8ssecret_test import ( + "os" "testing" impl "github.com/certimate-go/certimate/pkg/core/deployer/providers/k8s-secret" @@ -11,6 +12,7 @@ var ( fp = tester.Args("K8SSECRET_") fTestCertPath string fTestKeyPath string + fKubeConfig string fNamespace string fSecretName string fSecretDataKeyForCrt string @@ -20,6 +22,7 @@ var ( func init() { fp.DefineString(&fTestCertPath, "TESTCERTPATH") fp.DefineString(&fTestKeyPath, "TESTKEYPATH") + fp.DefineString(&fKubeConfig, "KUBECONFIG") fp.DefineString(&fNamespace, "NAMESPACE", "default") fp.DefineString(&fSecretName, "SECRETNAME") fp.DefineString(&fSecretDataKeyForCrt, "SECRETDATAKEYFORCRT", "tls.crt") @@ -32,6 +35,7 @@ Shell command to run this test: go test -v ./k8s_secret_test.go -args \ --K8SSECRET_TESTCERTPATH="/path/to/your-test-cert.pem" \ --K8SSECRET_TESTKEYPATH="/path/to/your-test-key.pem" \ + --K8SSECRET_KUBECONFIG="..." \ --K8SSECRET_NAMESPACE="default" \ --K8SSECRET_SECRETNAME="secret" \ --K8SSECRET_SECRETDATAKEYFORCRT="tls.crt" \ @@ -40,8 +44,14 @@ Shell command to run this test: func TestProvider(t *testing.T) { fp.Parse() + if fKubeConfigStat, err := os.Stat(fKubeConfig); err == nil && !fKubeConfigStat.IsDir() { + fKubeConfigBytes, _ := os.ReadFile(fKubeConfig) + fKubeConfig = string(fKubeConfigBytes) + } + t.Run("Deploy", func(t *testing.T) { provider, err := impl.NewDeployer(&impl.DeployerConfig{ + KubeConfig: fKubeConfig, Namespace: fNamespace, SecretName: fSecretName, SecretDataKeyForCrt: fSecretDataKeyForCrt, diff --git a/pkg/core/deployer/providers/yandexcloud-certificatemanager/yandexcloud_certificatemanager.go b/pkg/core/deployer/providers/yandexcloud-certificatemanager/yandexcloud_certificatemanager.go new file mode 100644 index 000000000..52e24f036 --- /dev/null +++ b/pkg/core/deployer/providers/yandexcloud-certificatemanager/yandexcloud_certificatemanager.go @@ -0,0 +1,85 @@ +package yandexcloudcertificatemanager + +import ( + "context" + "fmt" + "log/slog" + + "github.com/certimate-go/certimate/pkg/core" + cmgrimpl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/yandexcloud-certificatemanager" +) + +type ( + Provider = core.Deployer + DeployResult = core.DeployerDeployResult +) + +type DeployerConfig struct { + // Yandex Cloud 文件夹 ID。 + FolderId string `json:"folderId"` + // Yandex Cloud 服务账号授权密钥。 + ServiceAccountKey string `json:"serviceAccountKey"` + // 证书管理器证书 ID。 + // 选填。零值时表示新建证书;否则表示更新证书。 + CertificateId string `json:"certificateId,omitempty"` +} + +type Deployer struct { + config *DeployerConfig + logger *slog.Logger + sdkCertmgr core.Certmgr +} + +var _ Provider = (*Deployer)(nil) + +func NewDeployer(config *DeployerConfig) (*Deployer, error) { + if config == nil { + return nil, fmt.Errorf("the configuration of the deployer provider is nil") + } + + pcertmgr, err := cmgrimpl.NewCertmgr(&cmgrimpl.CertmgrConfig{ + FolderId: config.FolderId, + ServiceAccountKey: config.ServiceAccountKey, + }) + if err != nil { + return nil, fmt.Errorf("could not create certmgr: %w", err) + } + + return &Deployer{ + config: config, + logger: slog.Default(), + sdkCertmgr: pcertmgr, + }, nil +} + +func (d *Deployer) SetLogger(logger *slog.Logger) { + if logger == nil { + d.logger = slog.New(slog.DiscardHandler) + } else { + d.logger = logger + } + + d.sdkCertmgr.SetLogger(logger) +} + +func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*DeployResult, error) { + if d.config.CertificateId == "" { + // 上传证书 + upres, err := d.sdkCertmgr.Upload(ctx, certPEM, privkeyPEM) + if err != nil { + return nil, fmt.Errorf("failed to upload certificate file: %w", err) + } else { + d.logger.Info("ssl certificate uploaded", slog.Any("result", upres)) + } + } else { + // 替换证书 + rplres, err := d.sdkCertmgr.Replace(ctx, d.config.CertificateId, certPEM, privkeyPEM) + if err != nil { + return nil, fmt.Errorf("failed to replace certificate file: %w", err) + } else { + d.logger.Info("ssl certificate replaced", slog.Any("result", rplres)) + } + } + + return &DeployResult{}, nil +} diff --git a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx index 618095fb4..bc8fc8273 100644 --- a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx +++ b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx @@ -144,6 +144,7 @@ import BizDeployNodeConfigFieldsProviderWangsuCDN from "./BizDeployNodeConfigFie import BizDeployNodeConfigFieldsProviderWangsuCDNPro from "./BizDeployNodeConfigFieldsProviderWangsuCDNPro"; import BizDeployNodeConfigFieldsProviderWangsuCertificate from "./BizDeployNodeConfigFieldsProviderWangsuCertificate"; import BizDeployNodeConfigFieldsProviderWebhook from "./BizDeployNodeConfigFieldsProviderWebhook"; +import BizDeployNodeConfigFieldsProviderYandexCloudCertificateManager from "./BizDeployNodeConfigFieldsProviderYandexCloudCertificateManager"; import BizDeployNodeConfigFieldsProviderZenlayerCDN from "./BizDeployNodeConfigFieldsProviderZenlayerCDN"; import BizDeployNodeConfigFieldsProviderZenlayerGA from "./BizDeployNodeConfigFieldsProviderZenlayerGA"; @@ -294,6 +295,7 @@ const providerComponentMap: Partial { + const { i18n, t } = useTranslation(); + + const { parentNamePath } = useFormNestedFieldsContext(); + const formSchema = z.object({ + [parentNamePath]: getSchema({ i18n }), + }); + const formRule = createSchemaFieldRule(formSchema); + const initialValues = getInitialValues(); + + return ( + <> + + + + + ); +}; + +const getInitialValues = (): Nullish>> => { + return {}; +}; + +const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => { + const { t: _ } = i18n; + + return z.object({ + certificateId: z.string().nullish(), + }); +}; + +const _default = Object.assign(BizDeployNodeConfigFieldsProviderYandexCloudCertificateManager, { + getInitialValues, + getSchema, +}); + +export default _default; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 6efb6fcf6..09786ddf6 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -184,6 +184,7 @@ export const accessProvidersMap: Map