From fc7975972d975beeddd89d78a251c1fe851caa15 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Thu, 4 Jun 2026 18:50:05 +0800 Subject: [PATCH] feat(provider): new deployment provider: aws clb --- go.mod | 13 +- go.sum | 18 ++- internal/certmgmt/deployers/sp_aws_clb.go | 29 ++++ .../certmgmt/deployers/sp_aws_cloudfront.go | 2 +- internal/domain/provider.go | 1 + pkg/core/certmgr/providers/aws-acm/aws_acm.go | 16 +- pkg/core/certmgr/providers/aws-iam/aws_iam.go | 22 ++- .../deployer/providers/aws-clb/aws_clb.go | 146 ++++++++++++++++++ .../providers/aws-clb/aws_clb_test.go | 62 ++++++++ pkg/core/deployer/providers/aws-clb/consts.go | 6 + .../aws-cloudfront/aws_cloudfront.go | 6 +- .../aws-cloudfront/aws_cloudfront_test.go | 13 +- .../BizDeployNodeConfigFieldsProvider.tsx | 4 +- ...izDeployNodeConfigFieldsProviderAWSCLB.tsx | 92 +++++++++++ ui/src/domain/provider.ts | 2 + ui/src/i18n/resources/en/nls.provider.json | 1 + .../i18n/resources/en/nls.workflow.nodes.json | 19 +++ ui/src/i18n/resources/zh/nls.provider.json | 1 + .../i18n/resources/zh/nls.workflow.nodes.json | 19 +++ 19 files changed, 434 insertions(+), 38 deletions(-) create mode 100644 internal/certmgmt/deployers/sp_aws_clb.go create mode 100644 pkg/core/deployer/providers/aws-clb/aws_clb.go create mode 100644 pkg/core/deployer/providers/aws-clb/aws_clb_test.go create mode 100644 pkg/core/deployer/providers/aws-clb/consts.go create mode 100644 ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderAWSCLB.tsx diff --git a/go.mod b/go.mod index ecc5e1a0..2b0e3a0b 100644 --- a/go.mod +++ b/go.mod @@ -30,11 +30,12 @@ require ( github.com/alibabacloud-go/vod-20170321/v4 v4.11.3 github.com/alibabacloud-go/waf-openapi-20211001/v7 v7.7.0 github.com/aliyun/alibabacloud-oss-go-sdk-v2 v1.5.1 - github.com/aws/aws-sdk-go-v2 v1.41.8 + github.com/aws/aws-sdk-go-v2 v1.41.11 github.com/aws/aws-sdk-go-v2/config v1.32.19 github.com/aws/aws-sdk-go-v2/credentials v1.19.18 github.com/aws/aws-sdk-go-v2/service/acm v1.38.3 github.com/aws/aws-sdk-go-v2/service/cloudfront v1.63.0 + github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.34.2 github.com/aws/aws-sdk-go-v2/service/iam v1.53.10 github.com/aws/aws-sdk-go-v2/service/lightsail v1.54.1 github.com/baidubce/bce-sdk-go v0.9.267 @@ -84,8 +85,10 @@ require ( gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1 gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.0 golang.org/x/crypto v0.52.0 + golang.org/x/oauth2 v0.36.0 golang.org/x/sync v0.20.0 golang.org/x/sys v0.45.0 + google.golang.org/api v0.280.0 google.golang.org/protobuf v1.36.11 k8s.io/api v0.35.3 k8s.io/apimachinery v0.35.3 @@ -186,8 +189,8 @@ require ( github.com/aliyun/credentials-go v1.4.12 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.24 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.24 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.24 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.27 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.27 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.25 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.24 // indirect @@ -195,7 +198,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.30.18 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.1 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.42.2 // indirect - github.com/aws/smithy-go v1.25.1 // indirect + github.com/aws/smithy-go v1.27.0 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/bodgit/gssapi v0.0.3 // indirect github.com/bodgit/tsig v1.3.1 // indirect @@ -277,12 +280,10 @@ require ( golang.org/x/image v0.40.0 // indirect golang.org/x/mod v0.36.0 // indirect golang.org/x/net v0.55.0 // indirect - golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/term v0.43.0 // indirect golang.org/x/text v0.37.0 // indirect golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.44.0 // indirect - google.golang.org/api v0.280.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 // indirect google.golang.org/grpc v1.81.1 // indirect gopkg.in/ini.v1 v1.67.2 // indirect diff --git a/go.sum b/go.sum index aade26cc..c8e3fc93 100644 --- a/go.sum +++ b/go.sum @@ -203,18 +203,18 @@ github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ= github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk= github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2 v1.41.8 h1:sRs7nG6/RiEBZ/K5UO2sNw0w40U02Nmz1VtARloTZXk= -github.com/aws/aws-sdk-go-v2 v1.41.8/go.mod h1:4LAfZOPHNVNQEckOACQx60Y8pSRjIkNZQz1w92xpMJc= +github.com/aws/aws-sdk-go-v2 v1.41.11 h1:9PRf7jyTMEUM6fuNRAJa2mO/skJfrF50rENJwf2LXqw= +github.com/aws/aws-sdk-go-v2 v1.41.11/go.mod h1:iiUX27gOXRuYaoeUVXhUpPwjJHzISfPAjjcuhUbLSVs= github.com/aws/aws-sdk-go-v2/config v1.32.19 h1:qRhIJMbevHUvIE7X4TK8N8zye5+5AhapcslPrvB+qKE= github.com/aws/aws-sdk-go-v2/config v1.32.19/go.mod h1:RbJ24nfoya63+Mf5VI+CGCGk9vEdv28xPeii+gojRYs= github.com/aws/aws-sdk-go-v2/credentials v1.19.18 h1:GcXQz2M/0ZvMo0v5DakUqbDBeBM1ZNaivkolEF4Esgw= github.com/aws/aws-sdk-go-v2/credentials v1.19.18/go.mod h1:sHJ06tMGcD3ZpmMyJqV+VBsGilhSIZPIN+ZFy5Dg0C4= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.24 h1:FQm5ApnyzkuJdXLGskPce83CK1CQKC4RUnIHKVe4BU4= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.24/go.mod h1:JsC7dqQc55MlZ5mvNsDMMge71u8pVcSzU3RNz2h/5yQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.24 h1:u6kJU2i0va1AgtJsH3RdWKWqHULlTh7zHwb35Womf74= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.24/go.mod h1:7GY+xLcXOFUpCkNwDReft9qOAVg54A4/AnjHIU7sSAY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.24 h1:Xhbcf3KugX6vX7SDyUK205Oicyfg7EGuvoVNyP5L6DM= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.24/go.mod h1:rwDgb2HNOGZsnTHylOUedM7Vnl+bCfnXDqUNPsFWYfk= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.27 h1:8sPbKi1/KRHwl5oR3qN9mUXestCeHuaRutxylnr/eVY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.27/go.mod h1:QV9IVIopJ1dpQUno0f9VYDUwOEjj8u0iEJ4JiZVre3Y= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.27 h1:9d8AoASQY9UwrOSmiJ7uSM0MGUPFhnenwSvpaFfat2c= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.27/go.mod h1:x0rldpsnUQaQIs4Rh+Vwm9Z/0vI6BxadGtsgJfZFb8s= github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.25 h1:54CTMmlJ71Rk2dYvM9qZOob+39wjlVja2zDLxCu69Ew= github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.25/go.mod h1:BZaHqxsS9vN1fvV5EfEl0OBLOk5+AajWsMu6MjqnZB4= github.com/aws/aws-sdk-go-v2/service/acm v1.38.3 h1:Fzab84hCu3rw9R9Y3mH7SHfr/cSEHnCB0Mq1JCdr9t0= @@ -222,6 +222,8 @@ github.com/aws/aws-sdk-go-v2/service/acm v1.38.3/go.mod h1:yCteizCNPaHt0SnNusoGG github.com/aws/aws-sdk-go-v2/service/cloudfront v1.63.0 h1:YNpvY2mAGhoTHPEvsqPuDb8K5JBjorbVMaitP8i4OXI= github.com/aws/aws-sdk-go-v2/service/cloudfront v1.63.0/go.mod h1:brhMG/gR2xEB5lezxL2Cx+hqsEzGUn4LhNUtu7+ePFE= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= +github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.34.2 h1:N69Uy8towDrupiVOPgH3ADlstEQEhXv5QCXOqEfvmJ0= +github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.34.2/go.mod h1:1Yr+5r8qPhVQbDlN/kgxzjc++aNkxcKQZpMckhk4+94= github.com/aws/aws-sdk-go-v2/service/iam v1.53.10 h1:kcN3I3llO7VwIY5w3Pc5FmEonpsr23Ou7Cwk4qf7dik= github.com/aws/aws-sdk-go-v2/service/iam v1.53.10/go.mod h1:1vkJzjCYC3byO0kIrBqLPzvZpuvYhPXkuyARs6E7tM4= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 h1:FLudkZLt5ci0ozzgkVo8BJGwvqNaZbTWb3UcucAateA= @@ -241,8 +243,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.1/go.mod h1:er0SFJfdV89Rit5hI github.com/aws/aws-sdk-go-v2/service/sts v1.42.2 h1:XKnxlM4KZH1gktcsh3zSWc7GW4KivEv/OkifmHOhCUY= github.com/aws/aws-sdk-go-v2/service/sts v1.42.2/go.mod h1:KJYmkQaFB3SUW2j3aBkPsxNmAb4ZsSOvbvCpuxzHJA0= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/aws/smithy-go v1.25.1 h1:J8ERsGSU7d+aCmdQur5Txg6bVoYelvQJgtZehD12GkI= -github.com/aws/smithy-go v1.25.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/aws/smithy-go v1.27.0 h1:ZoFioDKJxkSIW2otF9T0aPtNlUwhdVCcuZh/rzH9Hus= +github.com/aws/smithy-go v1.27.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/baidubce/bce-sdk-go v0.9.267 h1:0l3UipFvWVsEv+FcontxWRs6ny8xvBeRISLbdNtO/bw= diff --git a/internal/certmgmt/deployers/sp_aws_clb.go b/internal/certmgmt/deployers/sp_aws_clb.go new file mode 100644 index 00000000..7f2a72f5 --- /dev/null +++ b/internal/certmgmt/deployers/sp_aws_clb.go @@ -0,0 +1,29 @@ +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/aws-clb" + xmaps "github.com/certimate-go/certimate/pkg/utils/maps" +) + +func init() { + Registries.MustRegister(domain.DeploymentProviderTypeAWSCLB, func(options *ProviderFactoryOptions) (core.Deployer, error) { + credentials := domain.AccessConfigForAWS{} + 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{ + AccessKeyId: credentials.AccessKeyId, + SecretAccessKey: credentials.SecretAccessKey, + Region: xmaps.GetString(options.ProviderExtendedConfig, "region"), + LoadbalancerName: xmaps.GetString(options.ProviderExtendedConfig, "loadbalancerName"), + LoadbalancerPort: xmaps.GetOrDefaultInt32(options.ProviderExtendedConfig, "loadbalancerPort", 443), + CertificateSource: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "certificateSource", dplyimpl.CERTIFICATE_SOURCE_ACM), + }) + return provider, err + }) +} diff --git a/internal/certmgmt/deployers/sp_aws_cloudfront.go b/internal/certmgmt/deployers/sp_aws_cloudfront.go index ae744769..f94ce20a 100644 --- a/internal/certmgmt/deployers/sp_aws_cloudfront.go +++ b/internal/certmgmt/deployers/sp_aws_cloudfront.go @@ -21,7 +21,7 @@ func init() { SecretAccessKey: credentials.SecretAccessKey, Region: xmaps.GetString(options.ProviderExtendedConfig, "region"), DistributionId: xmaps.GetString(options.ProviderExtendedConfig, "distributionId"), - CertificateSource: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "certificateSource", "ACM"), + CertificateSource: xmaps.GetOrDefaultString(options.ProviderExtendedConfig, "certificateSource", dplyimpl.CERTIFICATE_SOURCE_ACM), }) return provider, err }) diff --git a/internal/domain/provider.go b/internal/domain/provider.go index 11602a04..44f548ae 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -326,6 +326,7 @@ const ( DeploymentProviderTypeAliyunWAF = DeploymentProviderType(AccessProviderTypeAliyun + "-waf") DeploymentProviderTypeAPISIX = DeploymentProviderType(AccessProviderTypeAPISIX) DeploymentProviderTypeAWSACM = DeploymentProviderType(AccessProviderTypeAWS + "-acm") + DeploymentProviderTypeAWSCLB = DeploymentProviderType(AccessProviderTypeAWS + "-clb") DeploymentProviderTypeAWSCloudFront = DeploymentProviderType(AccessProviderTypeAWS + "-cloudfront") DeploymentProviderTypeAWSIAM = DeploymentProviderType(AccessProviderTypeAWS + "-iam") DeploymentProviderTypeAzureKeyVault = DeploymentProviderType(AccessProviderTypeAzure + "-keyvault") diff --git a/pkg/core/certmgr/providers/aws-acm/aws_acm.go b/pkg/core/certmgr/providers/aws-acm/aws_acm.go index 5fd3e223..fcb8056b 100644 --- a/pkg/core/certmgr/providers/aws-acm/aws_acm.go +++ b/pkg/core/certmgr/providers/aws-acm/aws_acm.go @@ -77,8 +77,8 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*Uplo } // 获取证书列表,避免重复上传 - // REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_ListCertificates.html - // REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_GetCertificate.html + // REF: https://docs.aws.amazon.com/acm/latest/APIReference/API_ListCertificates.html + // REF: https://docs.aws.amazon.com/acm/latest/APIReference/API_GetCertificate.html listCertificatesNextToken := (*string)(nil) for { select { @@ -127,7 +127,10 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*Uplo // 如果以上信息都一致,则视为已存在相同证书,直接返回 c.logger.Info("ssl certificate already exists") return &UploadResult{ - CertId: *certItem.CertificateArn, + CertId: aws.ToString(certItem.CertificateArn), + ExtendedData: map[string]any{ + "Arn": aws.ToString(certItem.CertificateArn), + }, }, nil } @@ -139,7 +142,7 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*Uplo } // 导入证书 - // REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_ImportCertificate.html + // REF: https://docs.aws.amazon.com/acm/latest/APIReference/API_ImportCertificate.html importCertificateReq := &awsacm.ImportCertificateInput{ Certificate: ([]byte)(serverCertPEM), CertificateChain: ([]byte)(intermediaCertPEM), @@ -153,6 +156,9 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*Uplo return &UploadResult{ CertId: aws.ToString(importCertificateResp.CertificateArn), + ExtendedData: map[string]any{ + "Arn": aws.ToString(importCertificateResp.CertificateArn), + }, }, nil } @@ -164,7 +170,7 @@ func (c *Certmgr) Replace(ctx context.Context, certIdOrName string, certPEM, pri } // 导入证书 - // REF: https://docs.aws.amazon.com/en_us/acm/latest/APIReference/API_ImportCertificate.html + // REF: https://docs.aws.amazon.com/acm/latest/APIReference/API_ImportCertificate.html importCertificateReq := &awsacm.ImportCertificateInput{ CertificateArn: aws.String(certIdOrName), Certificate: ([]byte)(serverCertPEM), diff --git a/pkg/core/certmgr/providers/aws-iam/aws_iam.go b/pkg/core/certmgr/providers/aws-iam/aws_iam.go index af69cbe1..1d99587c 100644 --- a/pkg/core/certmgr/providers/aws-iam/aws_iam.go +++ b/pkg/core/certmgr/providers/aws-iam/aws_iam.go @@ -1,6 +1,7 @@ package awsiam import ( + "cmp" "context" "fmt" "log/slog" @@ -80,8 +81,8 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*Uplo } // 获取证书列表,避免重复上传 - // REF: https://docs.aws.amazon.com/en_us/IAM/latest/APIReference/API_ListServerCertificates.html - // REF: https://docs.aws.amazon.com/en_us/IAM/latest/APIReference/API_GetServerCertificate.html + // REF: https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListServerCertificates.html + // REF: https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetServerCertificate.html listServerCertificatesMarker := (*string)(nil) for { select { @@ -132,6 +133,10 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*Uplo return &UploadResult{ CertId: aws.ToString(certItem.ServerCertificateId), CertName: aws.ToString(certItem.ServerCertificateName), + ExtendedData: map[string]any{ + "Arn": aws.ToString(certItem.Arn), + "Path": aws.ToString(certItem.Path), + }, }, nil } @@ -146,17 +151,14 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*Uplo certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli()) // 导入证书 - // REF: https://docs.aws.amazon.com/en_us/IAM/latest/APIReference/API_UploadServerCertificate.html + // REF: https://docs.aws.amazon.com/IAM/latest/APIReference/API_UploadServerCertificate.html uploadServerCertificateReq := &awsiam.UploadServerCertificateInput{ ServerCertificateName: aws.String(certName), - Path: aws.String(c.config.CertificatePath), + Path: aws.String(cmp.Or(c.config.CertificatePath, "/")), CertificateBody: aws.String(serverCertPEM), CertificateChain: aws.String(intermediaCertPEM), PrivateKey: aws.String(privkeyPEM), } - if c.config.CertificatePath == "" { - uploadServerCertificateReq.Path = aws.String("/") - } uploadServerCertificateResp, err := c.sdkClient.UploadServerCertificate(ctx, uploadServerCertificateReq) c.logger.Debug("sdk request 'iam.UploadServerCertificate'", slog.Any("request", uploadServerCertificateReq), slog.Any("response", uploadServerCertificateResp)) if err != nil { @@ -165,7 +167,11 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*Uplo return &UploadResult{ CertId: aws.ToString(uploadServerCertificateResp.ServerCertificateMetadata.ServerCertificateId), - CertName: certName, + CertName: aws.ToString(uploadServerCertificateResp.ServerCertificateMetadata.ServerCertificateName), + ExtendedData: map[string]any{ + "Arn": aws.ToString(uploadServerCertificateResp.ServerCertificateMetadata.Arn), + "Path": aws.ToString(uploadServerCertificateResp.ServerCertificateMetadata.Path), + }, }, nil } diff --git a/pkg/core/deployer/providers/aws-clb/aws_clb.go b/pkg/core/deployer/providers/aws-clb/aws_clb.go new file mode 100644 index 00000000..ff348ec4 --- /dev/null +++ b/pkg/core/deployer/providers/aws-clb/aws_clb.go @@ -0,0 +1,146 @@ +package awsclb + +import ( + "context" + "fmt" + "log/slog" + + aws "github.com/aws/aws-sdk-go-v2/aws" + awscfg "github.com/aws/aws-sdk-go-v2/config" + awscred "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing" + + "github.com/certimate-go/certimate/pkg/core" + cmgrimplacm "github.com/certimate-go/certimate/pkg/core/certmgr/providers/aws-acm" + cmgrimpliam "github.com/certimate-go/certimate/pkg/core/certmgr/providers/aws-iam" +) + +type ( + Provider = core.Deployer + DeployResult = core.DeployerDeployResult +) + +type DeployerConfig struct { + // AWS AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // AWS SecretAccessKey。 + SecretAccessKey string `json:"secretAccessKey"` + // AWS 区域。 + Region string `json:"region"` + // AWS CLB 负载均衡器名称。 + LoadbalancerName string `json:"loadbalancerName"` + // AWS CLB 负载均衡器端口。 + LoadbalancerPort int32 `json:"loadbalancerPort"` + // AWS CLB 证书来源。 + // 可取值 "ACM"、"IAM"。 + CertificateSource string `json:"certificateSource"` +} + +type Deployer struct { + config *DeployerConfig + logger *slog.Logger + sdkClient *elasticloadbalancing.Client + 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") + } + + client, err := createSDKClient(config.AccessKeyId, config.SecretAccessKey, config.Region) + if err != nil { + return nil, fmt.Errorf("could not create client: %w", err) + } + + var pcertmgr core.Certmgr + switch config.CertificateSource { + case CERTIFICATE_SOURCE_ACM: + pcertmgr, err = cmgrimplacm.NewCertmgr(&cmgrimplacm.CertmgrConfig{ + AccessKeyId: config.AccessKeyId, + SecretAccessKey: config.SecretAccessKey, + Region: config.Region, + }) + if err != nil { + return nil, fmt.Errorf("could not create certmgr: %w", err) + } + + case CERTIFICATE_SOURCE_IAM: + pcertmgr, err = cmgrimpliam.NewCertmgr(&cmgrimpliam.CertmgrConfig{ + AccessKeyId: config.AccessKeyId, + SecretAccessKey: config.SecretAccessKey, + Region: config.Region, + CertificatePath: "/elb/", + }) + if err != nil { + return nil, fmt.Errorf("could not create certmgr: %w", err) + } + + default: + return nil, fmt.Errorf("unsupported certificate source: '%s'", config.CertificateSource) + } + + return &Deployer{ + config: config, + logger: slog.Default(), + sdkClient: client, + 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.LoadbalancerName == "" { + return nil, fmt.Errorf("config `loadbalancerName` is required") + } + if d.config.LoadbalancerPort == 0 { + return nil, fmt.Errorf("config `loadbalancerPort` is required") + } + + // 上传证书 + 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)) + } + + // 替换 HTTPS 侦听器 SSL 证书 + // REF: https://docs.aws.amazon.com/elasticloadbalancing/2012-06-01/APIReference/API_SetLoadBalancerListenerSSLCertificate.html + setLoadBalancerListenerSSLCertificateReq := &elasticloadbalancing.SetLoadBalancerListenerSSLCertificateInput{ + LoadBalancerName: aws.String(d.config.LoadbalancerName), + LoadBalancerPort: d.config.LoadbalancerPort, + SSLCertificateId: aws.String(upres.ExtendedData["Arn"].(string)), + } + setLoadBalancerListenerSSLCertificateResp, err := d.sdkClient.SetLoadBalancerListenerSSLCertificate(ctx, setLoadBalancerListenerSSLCertificateReq) + d.logger.Debug("sdk request 'elasticloadbalancing.SetLoadBalancerListenerSSLCertificate'", slog.Any("request", setLoadBalancerListenerSSLCertificateReq), slog.Any("response", setLoadBalancerListenerSSLCertificateResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'elasticloadbalancing.SetLoadBalancerListenerSSLCertificate': %w", err) + } + + return &DeployResult{}, nil +} + +func createSDKClient(accessKeyId, secretAccessKey, region string) (*elasticloadbalancing.Client, error) { + cfg, err := awscfg.LoadDefaultConfig(context.Background()) + if err != nil { + return nil, err + } + + client := elasticloadbalancing.NewFromConfig(cfg, func(o *elasticloadbalancing.Options) { + o.Region = region + o.Credentials = aws.NewCredentialsCache(awscred.NewStaticCredentialsProvider(accessKeyId, secretAccessKey, "")) + }) + return client, nil +} diff --git a/pkg/core/deployer/providers/aws-clb/aws_clb_test.go b/pkg/core/deployer/providers/aws-clb/aws_clb_test.go new file mode 100644 index 00000000..aac05dbf --- /dev/null +++ b/pkg/core/deployer/providers/aws-clb/aws_clb_test.go @@ -0,0 +1,62 @@ +package awsclb_test + +import ( + "testing" + + "github.com/certimate-go/certimate/pkg/core/deployer/internal/tester" + impl "github.com/certimate-go/certimate/pkg/core/deployer/providers/aws-clb" +) + +var ( + fp = tester.Args("AWSCLB_") + fTestCertPath string + fTestKeyPath string + fAccessKeyId string + fSecretAccessKey string + fRegion string + fLoadbalancerName string + fLoadbalancerPort int +) + +func init() { + fp.DefineString(&fTestCertPath, "TESTCERTPATH") + fp.DefineString(&fTestKeyPath, "TESTKEYPATH") + fp.DefineString(&fAccessKeyId, "ACCESSKEYID") + fp.DefineString(&fSecretAccessKey, "SECRETACCESSKEY") + fp.DefineString(&fRegion, "REGION") + fp.DefineString(&fLoadbalancerName, "LOADBALANCERNAME") + fp.DefineInt(&fLoadbalancerPort, "LOADBALANCERPORT") +} + +/* +Shell command to run this test: + + go test -v ./aws_clb_test.go -args \ + --AWSCLB_TESTCERTPATH="/path/to/your-test-cert.pem" \ + --AWSCLB_TESTKEYPATH="/path/to/your-test-key.pem" \ + --AWSCLB_ACCESSKEYID="your-access-key-id" \ + --AWSCLB_SECRETACCESSKEY="your-secret-access-id" \ + --AWSCLB_REGION="us-east-1" \ + --AWSCLB_LOADBALANCERNAME="your-loadbalancer-name" \ + --AWSCLB_LOADBALANCERPORT=443 +*/ +func TestProvider(t *testing.T) { + fp.Parse() + + t.Run("Deploy", func(t *testing.T) { + provider, err := impl.NewDeployer(&impl.DeployerConfig{ + AccessKeyId: fAccessKeyId, + SecretAccessKey: fSecretAccessKey, + Region: fRegion, + LoadbalancerName: fLoadbalancerName, + LoadbalancerPort: int32(fLoadbalancerPort), + CertificateSource: impl.CERTIFICATE_SOURCE_ACM, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + tester.TestDeploy(t, provider, tester.TestDeployArgs{CertPath: fTestCertPath, KeyPath: fTestKeyPath}) + }) +} diff --git a/pkg/core/deployer/providers/aws-clb/consts.go b/pkg/core/deployer/providers/aws-clb/consts.go new file mode 100644 index 00000000..c0e3fec6 --- /dev/null +++ b/pkg/core/deployer/providers/aws-clb/consts.go @@ -0,0 +1,6 @@ +package awsclb + +const ( + CERTIFICATE_SOURCE_ACM = "ACM" + CERTIFICATE_SOURCE_IAM = "IAM" +) diff --git a/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront.go b/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront.go index a922090f..5de4fa7b 100644 --- a/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront.go +++ b/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront.go @@ -101,7 +101,7 @@ func (d *Deployer) SetLogger(logger *slog.Logger) { func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*DeployResult, error) { if d.config.DistributionId == "" { - return nil, fmt.Errorf("config `distribuitionId` is required") + return nil, fmt.Errorf("config `distributionId` is required") } // 上传证书 @@ -113,7 +113,7 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*Dep } // 获取分配配置 - // REF: https://docs.aws.amazon.com/en_us/cloudfront/latest/APIReference/API_GetDistributionConfig.html + // REF: https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_GetDistributionConfig.html getDistributionConfigReq := &cloudfront.GetDistributionConfigInput{ Id: aws.String(d.config.DistributionId), } @@ -124,7 +124,7 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*Dep } // 更新分配配置 - // REF: https://docs.aws.amazon.com/zh_cn/cloudfront/latest/APIReference/API_UpdateDistribution.html + // REF: https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_UpdateDistribution.html updateDistributionReq := &cloudfront.UpdateDistributionInput{ Id: aws.String(d.config.DistributionId), DistributionConfig: getDistributionConfigResp.DistributionConfig, diff --git a/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront_test.go b/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront_test.go index 69507907..9226d72c 100644 --- a/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront_test.go +++ b/pkg/core/deployer/providers/aws-cloudfront/aws_cloudfront_test.go @@ -14,7 +14,7 @@ var ( fAccessKeyId string fSecretAccessKey string fRegion string - fDistribuitionId string + fDistributionId string ) func init() { @@ -23,7 +23,7 @@ func init() { fp.DefineString(&fAccessKeyId, "ACCESSKEYID") fp.DefineString(&fSecretAccessKey, "SECRETACCESSKEY") fp.DefineString(&fRegion, "REGION") - fp.DefineString(&fDistribuitionId, "DISTRIBUTIONID") + fp.DefineString(&fDistributionId, "DISTRIBUTIONID") } /* @@ -42,10 +42,11 @@ func TestProvider(t *testing.T) { t.Run("Deploy", func(t *testing.T) { provider, err := impl.NewDeployer(&impl.DeployerConfig{ - AccessKeyId: fAccessKeyId, - SecretAccessKey: fSecretAccessKey, - Region: fRegion, - DistributionId: fDistribuitionId, + AccessKeyId: fAccessKeyId, + SecretAccessKey: fSecretAccessKey, + Region: fRegion, + DistributionId: fDistributionId, + CertificateSource: impl.CERTIFICATE_SOURCE_ACM, }) if err != nil { t.Errorf("err: %+v", err) diff --git a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx index fa453aec..67fbfb04 100644 --- a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx +++ b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { DEPLOYMENT_PROVIDERS, type DeploymentProviderType } from "@/domain/provider"; @@ -23,6 +23,7 @@ import BizDeployNodeConfigFieldsProviderAliyunVOD from "./BizDeployNodeConfigFie import BizDeployNodeConfigFieldsProviderAliyunWAF from "./BizDeployNodeConfigFieldsProviderAliyunWAF"; import BizDeployNodeConfigFieldsProviderAPISIX from "./BizDeployNodeConfigFieldsProviderAPISIX"; import BizDeployNodeConfigFieldsProviderAWSACM from "./BizDeployNodeConfigFieldsProviderAWSACM"; +import BizDeployNodeConfigFieldsProviderAWSCLB from "./BizDeployNodeConfigFieldsProviderAWSCLB"; import BizDeployNodeConfigFieldsProviderAWSCloudFront from "./BizDeployNodeConfigFieldsProviderAWSCloudFront"; import BizDeployNodeConfigFieldsProviderAWSIAM from "./BizDeployNodeConfigFieldsProviderAWSIAM"; import BizDeployNodeConfigFieldsProviderAzureKeyVault from "./BizDeployNodeConfigFieldsProviderAzureKeyVault"; @@ -157,6 +158,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 ( + <> + } + > + + + + } + > + + + + } + > + + + + +