우선, cloud_mssql.go를 먼저 작성하기로 하였다.
2개의 API 문서를 참고하였다.
- ncloud API 문서 : https://api.ncloud-docs.com/docs/database-vmssql-createcloudmssqlslaveinstance
- github cloud API 문서 : https://github.com/NaverCloudPlatform/ncloud-sdk-go-v2/blob/master/services/vmssql/README.md
[ResourceNcloudMssql]
Schema 작성하기 - 우선 required parameter만 작성하기로 하였다.
Schema: map[string]*schema.Schema{
},
required 인 것
1) vpc_no
"vpc_no": {
Type: schema.TypeString,
Required: true,
Description: "VPC Number of Cloud DB for MSSQL instances",
},
2) subnet_no
"subnet_no": {
Type: schema.TypeString,
Required: true,
Description: "Subnet Number of Cloud DB for MSSQL instance",
},
3) service_name
여기는 제약 사항이 있어서 문서를 참고했다.
https://pkg.go.dev/github.com/hashicorp/terraform-plugin-sdk/v2@v2.26.1/helper/validation
"service_name": {
Type: schema.TypeString,
Required: true,
ValidateDiagFunc: ToDiagFunc(validation.All(
validation.StringLenBetween(3, 15),
validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9\\-가-힣]+$`), "Composed of alphabets, numbers, korean, hyphen (-)."),
)),
Description: "Name of Cloud DB for MSSQL instance",
},
4) is_ha
"is_ha": {
Type: schema.TypeBool,
Required: true,
Description: "Choice of High Availability",
},
5) user_name
"user_name": {
Type: schema.TypeString,
Required: true,
ValidateDiagFunc: ToDiagFunc(validation.All(
validation.StringLenBetween(4, 16),
validation.StringMatch(regexp.MustCompile(`^[a-zA-Z]+.+`), "starts with an alphabets."),
validation.StringMatch(regexp.MustCompile(`^[a-zA-Z]+[a-zA-Z0-9-\\\\_]+$`), "Composed of alphabets, numbers, hyphen (-), (\\\\), (_)."),
)),
Description: "Access username, which will be used for DB admin",
},
6) user_password
"user_password": {
Type: schema.TypeString,
Required: true,
ValidateDiagFunc: ToDiagFunc(validation.All(
validation.StringLenBetween(8, 20),
validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9~!@#$%^*()\\-_=\\[\\]\\{\\};:,.<>?]{8,20}$`), "Must Combine at least one each of alphabets, numbers, special characters except ` & + \\\\ \\" ' / and white space."),
validation.StringMatch(regexp.MustCompile(`.+[a-zA-Z]{1,}.+|.+[a-zA-Z]{1,}|[a-zA-Z]{1,}.+`), "Must have at least 1 alphabet."),
validation.StringMatch(regexp.MustCompile(`.+[0-9]{1,}.+|.+[0-9]{1,}|[0-9]{1,}.+`), "Must have at least 1 Number."),
validation.StringMatch(regexp.MustCompile(`.+[~!@#$%^*()\\-_=\\[\\]\\{\\};:,.<>?].+|.+[~!@#$%^*()\\-_=\\[\\]\\{\\};:,.<>?]|[~!@#$%^*()\\-_=\\[\\]\\{\\};:,.<>?].+`), "Must have at least 1 special characters except ` & + \\\\ \\" ' / and white space."),
)),
Description: "Access password for user, which will be used for DB admin",
},
optional 인 것
1) region_code
"region_code": {
Type: schema.TypeString,
Optional: true,
Description: "Region code to determine the region of Cloud DB for MSSQL instance",
},
2) mirror_subnet_no
"mirror_subnet_no": {
Type: schema.TypeString,
Optional: true,
Description: "Subnet number of Mirror Server. Required when isMultiZone is true.",
},
3) config_group_no
"config_group_no": {
Type: schema.TypeString,
Optional: true,
Description: "Config Group number of Cloud DB for MSSQL instance.",
Default: 0,
},
4) image_product_code
"image_product_code": {
Type: schema.TypeString,
Optional: true,
Description: "Image Product Code of Cloud DB for MSSQL instance.",
},
5) product_code
"product_code": {
Type: schema.TypeString,
Optional: true,
Description: "Product Code of Cloud DB for MSSQL instance.",
},
6) data_storage_type_code
"data_storage_type_code": {
Type: schema.TypeString,
Optional: true,
Description: "Data Storage Type Code.",
Default: "SSD",
},
7) is_multi_zone
"is_multi_zone": {
Type: schema.TypeBool,
Optional: true,
Description: "Multi Zone option. Required when isHa is true.",
Default: false,
},
8) backup_file_retention_period
"backup_file_retention_period": {
Type: schema.TypeInt,
Optional: true,
ValidateDiagFunc: ToDiagFunc(validation.IntBetween(1, 30)),
Description: "Retention period of back-up files.",
Default: 1,
},
9) backup_time
"backup_time": {
Type: schema.TypeString,
Optional: true,
ValidateDiagFunc: ToDiagFunc(validation.All(
validation.StringMatch(regexp.MustCompile(`^(0[0-9]|1[0-9]|2[0-3])([0-5][0-9])$`), "Must be in the format HHMM."),
validation.StringMatch(regexp.MustCompile(`^(0[0-9]|1[0-9]|2[0-3])([0-5][0-9])(00|15|30|45)$`), "Must be in 15-minute intervals."),
)),
Description: "Back-up time. Required when isAutomaticBackup is false.",
},
10) is_automatic_backup
"is_automatic_backup": {
Type: schema.TypeBool,
Optional: true,
Description: "Automatic backup time.",
},
11) port
"port": {
Type: schema.TypeInt,
Optional: true,
ValidateDiagFunc: ToDiagFunc(validation.Any(
//validation.
validation.IntBetween(10000, 20000),
)),
Description: "Port of Cloud DB for MSSQL instance.",
Default: 1433,
},
12) character_set_name
"character_set_name": {
Type: schema.TypeString,
Optional: true,
Description: "DB character set.",
Default: "Korean_Wansung_CI_AS",
},
13) response_format_type
"response_format_type": {
Type: schema.TypeString,
Optional: true,
Description: "Response format type.",
Default: "xml",
},
resourceNcloudMssqlCreate, resourceNcloudMssqlRead, resourceNcloudMssqlDelete는 기존에 개발되었던 코드들을 참고하면서 작성해보았다. 주로 classicLoadBalancer, lb 2개를 많이 참고하였다.
[resourceNcloudMssqlCreate]
func resourceNcloudMssqlCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*conn.ProviderConfig).Client
reqParams, err := buildCreateCloudMssqlInstanceParams(client, d)
if err != nil {
return err
}
LogCommonRequest("CreateCloudMssqlInstance", reqParams)
resp, err := client.Vmssql.V2Api.CreateCloudMssqlInstance(reqParams)
if err != nil {
LogErrorResponse("CreateCloudMssqlInstance", err, reqParams)
return err
}
LogCommonResponse("CreateCloudMssqlInstance", GetCommonResponse(resp))
cloudMssqlInstance := resp.CloudMssqlInstanceList.CloudMssqlInstanceList[0]
d.SetId(*cloudMssqlInstance.CloudMssqlInstanceNo)
stateConf := &resource.StateChangeConf{
Pending: []string{"INIT", "USE"},
Target: []string{"USED"},
Refresh: func() (interface{}, string, error) {
instance, err := GetCloudMssqlInstance(client, ncloud.StringValue(cloudMssqlInstance.CloudMssqlInstanceNo))
if err != nil {
return 0, "", err
}
if ncloud.StringValue(instance.CloudMssqlInstanceOperation.Code) == "NULL" {
return instance, ncloud.StringValue(instance.CloudMssqlInstanceOperation.Code), nil
}
return instance, ncloud.StringValue(instance.CloudMssqlInstanceOperation.Code), nil
},
Timeout: conn.DefaultCreateTimeout,
Delay: 2 * time.Second,
MinTimeout: 3 * time.Second,
}
_, err = stateConf.WaitForState()
if err != nil {
return fmt.Errorf("Error waiting for CloudMssqlInstanceStatus state to be \\"USED\\": %s", err)
}
return resourceNcloudMssqlRead(d, meta)
}
[resourceNcloudMssqlRead]
func resourceNcloudMssqlRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*conn.ProviderConfig).Client
ms, err := GetCloudMssqlInstance(client, d.Id())
if err != nil {
return err
}
if ms != nil {
d.Set("service_name", ms.CloudMssqlServiceName)
d.Set("instance_status_name", ms.CloudMssqlInstanceStatusName)
d.Set("image_product_code", ms.CloudMssqlImageProductCode)
d.Set("is_ha", ms.IsHa)
d.Set("port", ms.CloudMssqlPort)
d.Set("backup_file_retention_period", ms.BackupFileRetentionPeriod)
d.Set("backup_time", ms.BackupTime)
d.Set("config_group_no", ms.ConfigGroupNo)
d.Set("engine_version", ms.EngineVersion)
d.Set("create_date", ms.CreateDate)
d.Set("db_collation", ms.DbCollation)
d.Set("access_control_group_no_list", ms.AccessControlGroupNoList)
if instanceStatus := FlattenCommonCode(ms.CloudMssqlInstanceStatus); instanceStatus["code"] != nil {
d.Set("instance_status", instanceStatus["code"])
}
if instanceOperation := FlattenCommonCode(ms.CloudMssqlInstanceOperation); instanceOperation["code"] != nil {
d.Set("instance_operation", instanceOperation["code"])
}
if licenseCode := FlattenCommonCode(ms.LicenseCode); licenseCode["code"] != nil {
d.Set("license_code", licenseCode["code"])
}
if len(ms.CloudMssqlServerInstanceList) != 0 {
if err := d.Set("server_instance_list", flattenCloudMssqlServerInstanceList(ms.CloudMssqlServerInstanceList)); err != nil {
return err
}
} else {
d.Set("server_instance_list", nil)
}
} else {
log.Printf("unable to find resource: %s", d.Id())
d.SetId("") // resource not found
}
return nil
}
[resourceNcloudMssqlDelete]
func resourceNcloudMssqlDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*conn.ProviderConfig).Client
if err := deleteCloudMssqlInstance(client, d.Id()); err != nil {
return err
}
d.SetId("")
return nil
}
이제 test 코드를 작성해보자. 역시나, 기존에 작성되었던 코드들을 참고하였고, classicLoadBalancer, lb 2가지를 많이 참고하였다.
[cloud_mssql_test.go]
package cloudmssql_test
import (
"fmt"
"testing"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/ncloud"
"github.com/NaverCloudPlatform/ncloud-sdk-go-v2/services/vmssql"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
. "github.com/terraform-providers/terraform-provider-ncloud/internal/acctest"
"github.com/terraform-providers/terraform-provider-ncloud/internal/conn"
mssqlservice "github.com/terraform-providers/terraform-provider-ncloud/internal/service/cloudmssql"
)
func TestAccResourceNcloudMssql_vpc_basic(t *testing.T) {
var mssqlInstance vmssql.CloudMssqlInstance
testMssqlName := fmt.Sprintf("tf-mssql-%s", acctest.RandString(5))
resourceName := "ncloud_mssql.mssql"
resource.Test(t, resource.TestCase{
PreCheck: func() { TestAccPreCheck(t) },
Providers: GetTestAccProviders(true),
CheckDestroy: func(state *terraform.State) error {
return testAccCheckCloudMssqlDestroy(state, GetTestProvider(true))
},
Steps: []resource.TestStep{
{
Config: testAccMssqlVpcConfig(testMssqlName),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudMssqlExists(resourceName, &mssqlInstance, GetTestProvider(true)),
resource.TestCheckResourceAttr(resourceName, "service_name", testMssqlName),
resource.TestCheckResourceAttr(resourceName, "user_name", "test"),
resource.TestCheckResourceAttr(resourceName, "user_password", "qwer1234!"),
resource.TestCheckResourceAttr(resourceName, "is_ha", "true"),
resource.TestCheckResourceAttr(resourceName, "is_multi_zone", "false"),
resource.TestCheckResourceAttr(resourceName, "backup_file_retention_period", "1"),
resource.TestCheckResourceAttr(resourceName, "is_automatic_backup", "true"),
),
},
},
})
}
func testAccCheckCloudMssqlExists(n string, mssql *vmssql.CloudMssqlInstance, provider *schema.Provider) resource.TestCheckFunc {
return func(s *terraform.State) error {
resource, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("not found %s", n)
}
if resource.Primary.ID == "" {
return fmt.Errorf("no ID is set")
}
clinet := provider.Meta().(*conn.ProviderConfig).Client
mssqlInstance, err := mssqlservice.GetCloudMssqlInstance(clinet, resource.Primary.ID)
if err != nil {
return err
}
if mssqlInstance == nil {
return fmt.Errorf("Not found Mssql : %s", resource.Primary.ID)
}
mssql = mssqlInstance
return nil
}
}
func testAccCheckCloudMssqlDestroy(s *terraform.State, provider *schema.Provider) error {
client := provider.Meta().(*conn.ProviderConfig).Client
for _, rs := range s.RootModule().Resources {
if rs.Type != "ncloud_mssql" {
continue
}
cloudMssql, err := mssqlservice.GetCloudMssqlInstance(client, rs.Primary.ID)
if err != nil {
return err
}
if cloudMssql != nil {
return fmt.Errorf("CloudMssql(%s) still exists", ncloud.StringValue(cloudMssql.CloudMssqlInstanceNo))
}
}
return nil
}
func testAccMssqlVpcConfig(testMssqlName string) string {
return fmt.Sprintf(`
resource "ncloud_vpc" "test_vpc" {
name = "%[1]s"
ipv4_cidr_block = "10.0.0.0/16"
}
resource "ncloud_subnet" "test_subnet" {
vpc_no = ncloud_vpc.test_vpc.vpc_no
name = "%[1]s"
subnet = "10.0.0.0/24"
zone = "KR-2"
network_acl_no = ncloud_vpc.test_vpc.default_network_acl_no
subnet_type = "PUBLIC"
}
resource "ncloud_mssql" "mssql" {
vpc_no = ncloud_vpc.test_vpc.vpc_no
subnet_no = ncloud_subnet.test_subnet.id
service_name = "%[1]s"
is_ha = true
is_multi_zone = false
user_name = "test"
user_password = "qwer1234!"
}
`, testMssqlName)
}
기본적인 코드는 작성하였으니, 이제 test를 해보자..!
'💻 개발 > Terraform on NaverCloud' 카테고리의 다른 글
MSSQL 개발하기 (2) & pr 올리기 (0) | 2023.09.21 |
---|---|
MSSQL Test 하기 (0) | 2023.09.11 |
SourcePipeline 살펴보기 (2) & pr 올리기 (0) | 2023.08.31 |
이슈 선정 3 : MSSQL 리소스 개발하기 (0) | 2023.08.22 |
SourcePipelineProject 살펴보기 (1) (0) | 2023.08.21 |