From 0f97efb760a7bbba3b0f4f311cbc712612e36cbd Mon Sep 17 00:00:00 2001 From: hoangndst Date: Tue, 20 Aug 2024 22:43:05 +0700 Subject: [PATCH] feat: viettel cloud dbaas mysql --- modules/mysql/src/mysql.go | 17 ++++ modules/mysql/src/mysql_test.go | 16 ++++ modules/mysql/src/viettelcloud_dbaas.go | 98 ++++++++++++++++++++ modules/mysql/src/viettelcloud_dbaas_test.go | 69 ++++++++++++++ 4 files changed, 200 insertions(+) create mode 100644 modules/mysql/src/viettelcloud_dbaas.go create mode 100644 modules/mysql/src/viettelcloud_dbaas_test.go diff --git a/modules/mysql/src/mysql.go b/modules/mysql/src/mysql.go index 2fa318c..1a55b91 100644 --- a/modules/mysql/src/mysql.go +++ b/modules/mysql/src/mysql.go @@ -65,6 +65,8 @@ type MySQL struct { Version string `json:"version,omitempty" yaml:"version,omitempty"` // The type of the MySQL instance. InstanceType string `json:"instanceType,omitempty" yaml:"instanceType,omitempty"` + // The type of the MySQL volume. + VolumeType string // The allocated storage size of the MySQL instance. Size int `json:"size,omitempty" yaml:"size,omitempty"` // The edition of the MySQL instance provided by the cloud vendor. @@ -73,6 +75,8 @@ type MySQL struct { Username string `json:"username,omitempty" yaml:"username,omitempty"` // The list of IP addresses allowed to access the MySQL instance provided by the cloud vendor. SecurityIPs []string `json:"securityIPs,omitempty" yaml:"securityIPs,omitempty"` + // The VPC ID associated with the cloud MySQL instance will be created in. + VPC string `json:"vpc,omitempty" yaml:"vpc,omitempty"` // The virtual subnet ID associated with the VPC that the cloud MySQL instance will be created in. SubnetID string `json:"subnetID,omitempty" yaml:"subnetID,omitempty"` // Whether the host address of the cloud MySQL instance for the workload to connect with is via @@ -131,6 +135,11 @@ func (mysql *MySQL) Generate(_ context.Context, request *module.GeneratorRequest if err != nil { return nil, err } + case "viettelcloud": + resources, patcher, err = mysql.GenerateViettelCloudResources(request) + if err != nil { + return nil, err + } default: return nil, fmt.Errorf("unsupported cloud provider type: %s", providerType) } @@ -200,6 +209,14 @@ func (mysql *MySQL) GetCompleteConfig(devConfig apiv1.Accessory, platformConfig mysql.InstanceType = instanceType.(string) } + if volumeType, ok := platformConfig["volumeType"]; ok { + mysql.VolumeType = volumeType.(string) + } + + if vpc, ok := platformConfig["vpc"]; ok { + mysql.VPC = vpc.(string) + } + if subnetID, ok := platformConfig["subnetID"]; ok { mysql.SubnetID = subnetID.(string) } diff --git a/modules/mysql/src/mysql_test.go b/modules/mysql/src/mysql_test.go index 6d18453..9fabf0b 100644 --- a/modules/mysql/src/mysql_test.go +++ b/modules/mysql/src/mysql_test.go @@ -74,6 +74,22 @@ func TestMySQLModule_Generator(t *testing.T) { }, expectedErr: nil, }, + { + name: "Generate ViettelCloud MySQL DBaaS", + devModuleConfig: apiv1.Accessory{ + "type": "cloud", + "version": "8.0", + }, + platformConfig: apiv1.GenericConfig{ + "cloud": "viettelcloud", + "size": 20, + "instanceType": "DBAAS_1vCPU_1_RAM", + "volumeType": "ssd", + "vpc": "vpc-test-name", + "subnetID": "subnet-test-name", + }, + expectedErr: nil, + }, { name: "Unsupported MySQL type", devModuleConfig: apiv1.Accessory{ diff --git a/modules/mysql/src/viettelcloud_dbaas.go b/modules/mysql/src/viettelcloud_dbaas.go new file mode 100644 index 0000000..3abce4e --- /dev/null +++ b/modules/mysql/src/viettelcloud_dbaas.go @@ -0,0 +1,98 @@ +package main + +import ( + "os" + + "kusionstack.io/kusion-module-framework/pkg/module" + apiv1 "kusionstack.io/kusion/pkg/apis/api.kusion.io/v1" + "kusionstack.io/kusion/pkg/modules" +) + +const ( + DefaultViettelCloudRegion = "vn-central-1" +) + +var ( + viettelCloudRegionEnv = "VIETTEL_CLOUD_REGION" + viettelCloudDBInstance = "viettelcloud_db_instance" +) + +var defaultViettelCloudProviderCfg = module.ProviderConfig{ + Source: "hashicorp/viettelcloud", + Version: "1.0.0-dev", +} + +// GenerateViettelCloudResources generates the ViettelCloud provided MySQL database instance. +func (mysql *MySQL) GenerateViettelCloudResources(request *module.GeneratorRequest) ([]apiv1.Resource, *apiv1.Patcher, error) { + var resources []apiv1.Resource + + // Set the ViettelCloud provider with the default provider config. + viettelCloudProviderCfg := defaultViettelCloudProviderCfg + + // Get the ViettelCloud Terraform provider region, which should not be empty. + var region string + if region = module.TerraformProviderRegion(viettelCloudProviderCfg); region == "" { + region = os.Getenv(viettelCloudRegionEnv) + } + if region == "" { + region = DefaultViettelCloudRegion + } + + // Build random_password resource. + randomPasswordRes, randomPasswordID, err := mysql.GenerateTFRandomPassword(request) + if err != nil { + return nil, nil, err + } + resources = append(resources, *randomPasswordRes) + + // Build viettelCloud_db_instance resource. + viettelCloudDBInstance, viettelCloudDBInstanceID, err := mysql.generateViettelCloudDBInstance(viettelCloudProviderCfg, region, randomPasswordID) + if err != nil { + return nil, nil, err + } + resources = append(resources, *viettelCloudDBInstance) + + hostAddress := modules.KusionPathDependency(viettelCloudDBInstanceID, "private_url") + password := modules.KusionPathDependency(randomPasswordID, "result") + + // Build Kubernetes Secret with the hostAddress, username and password of the ViettelCloud provided MySQL instance, + // and inject the credentials as the environment variable patcher. + dbSecret, patcher, err := mysql.GenerateDBSecret(request, hostAddress, mysql.Username, password) + if err != nil { + return nil, nil, err + } + resources = append(resources, *dbSecret) + + return resources, patcher, nil +} + +// generateViettelCloudDBInstance generates viettelCloud_db_instance resource for the ViettelCloud provided MySQL database instance. +func (mysql *MySQL) generateViettelCloudDBInstance(viettelCloudProviderCfg module.ProviderConfig, region, randomPasswordID string) (*apiv1.Resource, string, error) { + resAttrs := map[string]interface{}{ + "database_type": dbEngine, + "region": region, + "name": mysql.DatabaseName, + "db_version": mysql.Version, + "flavor": mysql.InstanceType, + "volume_type": mysql.VolumeType, + "disk_size": mysql.Size, + "vpc_name": mysql.VPC, + "subnet": mysql.SubnetID, + "solution": mysql.Category, + "root_password": modules.KusionPathDependency(randomPasswordID, "result"), + "enable_auto_backup": false, + } + + id, err := module.TerraformResourceID(viettelCloudProviderCfg, viettelCloudDBInstance, mysql.DatabaseName) + if err != nil { + return nil, "", err + } + + viettelCloudProviderCfg.ProviderMeta = map[string]any{} + resource, err := module.WrapTFResourceToKusionResource(viettelCloudProviderCfg, viettelCloudDBInstance, id, resAttrs, nil) + if err != nil { + return nil, "", err + } + + return resource, id, nil +} diff --git a/modules/mysql/src/viettelcloud_dbaas_test.go b/modules/mysql/src/viettelcloud_dbaas_test.go new file mode 100644 index 0000000..c63ae4d --- /dev/null +++ b/modules/mysql/src/viettelcloud_dbaas_test.go @@ -0,0 +1,69 @@ +package main + +import ( + "github.com/bytedance/mockey" + "github.com/stretchr/testify/assert" + "kusionstack.io/kusion-module-framework/pkg/module" + v1 "kusionstack.io/kusion/pkg/apis/api.kusion.io/v1" + "os" + "testing" +) + +func TestMySQLModule_GenerateViettelCloudResources(t *testing.T) { + r := &module.GeneratorRequest{ + Project: "test-project", + Stack: "test-stack", + App: "test-app", + Workload: &v1.Workload{ + Header: v1.Header{ + Type: "Service", + }, + Service: &v1.Service{}, + }, + } + + mysql := &MySQL{ + Type: "local", + Version: "8.0", + DatabaseName: "test-database", + Username: defaultUsername, + Category: defaultCategory, + Size: defaultSize, + InstanceType: "DBAAS_1vCPU_1_RAM", + VolumeType: "ssd", + VPC: "vpc-new", + SubnetID: "subnet", + } + + mockey.PatchConvey("set viettelcloud region env", t, func() { + mockey.Mock(os.Getenv).Return("test-region").Build() + + resources, patchers, err := mysql.GenerateViettelCloudResources(r) + + assert.Equal(t, 3, len(resources)) + assert.NotNil(t, patchers) + assert.NoError(t, err) + }) +} + +func TestMySQLModule_GenerateViettelCloudDBInstance(t *testing.T) { + mysql := &MySQL{ + Type: "local", + Version: "8.0", + DatabaseName: "test-database", + Username: defaultUsername, + Category: defaultCategory, + Size: defaultSize, + InstanceType: "DBAAS_1vCPU_1_RAM", + VolumeType: "ssd", + VPC: "vpc-new", + SubnetID: "subnet", + } + + res, id, err := mysql.generateViettelCloudDBInstance(defaultViettelCloudProviderCfg, "test-region", + "random_password_id") + + assert.NotNil(t, res) + assert.NotEqual(t, id, "") + assert.NoError(t, err) +}