diff --git a/internal/tests/provider_test.go b/internal/tests/provider_test.go new file mode 100644 index 00000000..d2d63c11 --- /dev/null +++ b/internal/tests/provider_test.go @@ -0,0 +1,74 @@ +package tests + +import ( + "context" + "sync" + "testing" + "time" + + "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" + apierrors "k8s.io/apimachinery/pkg/api/errors" + + "github.com/harvester/terraform-provider-harvester/internal/provider" +) + +var ( + testAccProviders map[string]*schema.Provider + testAccProviderFactories map[string]func() (*schema.Provider, error) + testAccProvider *schema.Provider + testAccProviderConfigure sync.Once +) + +const ( + ProviderNameHarvester = "harvester" + + testAccResourceStateRemoved = "removed" + testAccResourceStateExist = "exist" +) + +func init() { + testAccProvider = provider.Provider() + + testAccProviders = map[string]*schema.Provider{ + ProviderNameHarvester: testAccProvider, + } + + testAccProviderFactories = map[string]func() (*schema.Provider, error){ + ProviderNameHarvester: func() (*schema.Provider, error) { return provider.Provider(), nil }, //nolint:unparam + } +} + +func testAccPreCheck(t *testing.T) { + testAccProviderConfigure.Do(func() { + err := testAccProvider.Configure(context.Background(), terraform.NewResourceConfigRaw(nil)) + if err != nil { + t.Fatal(err) + } + }) +} + +func getStateChangeConf(refresh resource.StateRefreshFunc) *resource.StateChangeConf { + return &resource.StateChangeConf{ + Pending: []string{testAccResourceStateExist}, + Target: []string{testAccResourceStateRemoved}, + Refresh: refresh, + Timeout: 1 * time.Minute, + Delay: 1 * time.Second, + MinTimeout: 3 * time.Second, + } +} + +func getResourceStateRefreshFunc(getResourceFunc func() (interface{}, error)) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + obj, err := getResourceFunc() + if err != nil { + if apierrors.IsNotFound(err) { + return obj, testAccResourceStateRemoved, nil + } + return nil, "", err + } + return obj, testAccResourceStateExist, nil + } +} diff --git a/internal/tests/resource_clusternetwork_test.go b/internal/tests/resource_clusternetwork_test.go new file mode 100644 index 00000000..26530972 --- /dev/null +++ b/internal/tests/resource_clusternetwork_test.go @@ -0,0 +1,98 @@ +package tests + +import ( + "context" + "fmt" + "testing" + + harvsternetworkv1 "github.com/harvester/harvester-network-controller/pkg/apis/network.harvesterhci.io/v1beta1" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/harvester/terraform-provider-harvester/pkg/client" + "github.com/harvester/terraform-provider-harvester/pkg/constants" + "github.com/harvester/terraform-provider-harvester/pkg/helper" +) + +const ( + testAccClusterNetworkNamespace = "harvester-system" + testAccClusterNetworkName = "vlan" + testAccClusterNetworkResourceName = constants.ResourceTypeClusterNetwork + "." + testAccClusterNetworkName + testAccClusterNetworkDescription = "Terraform Harvester ClusterNetwork acceptance test" + + testAccClusterNetworkEnable = "true" + testAccClusterNetworkDefaultPhysicalNIC = "eth0" + + testAccClusterNetworkConfigTemplate = ` +resource %s "%s" { + namespace = harvester-system + %s = "%s" + %s = "%s" + %s = %s + %s = "%s" +} +` +) + +func buildClusterNetworkConfig(name, description, enable, defaultPhysicalNIC string) string { + return fmt.Sprintf(testAccClusterNetworkConfigTemplate, constants.ResourceTypeClusterNetwork, name, + constants.FieldCommonName, name, + constants.FieldCommonDescription, description, + constants.FieldClusterNetworkEnable, enable, + constants.FieldClusterNetworkDefaultPhysicalNIC, defaultPhysicalNIC) +} + +func TestAccClusterNetwork_basic(t *testing.T) { + var ( + clusterNetwork *harvsternetworkv1.ClusterNetwork + ctx = context.Background() + ) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + ResourceName: testAccClusterNetworkResourceName, + ImportState: true, + ImportStateId: testAccClusterNetworkNamespace + "/" + testAccClusterNetworkName, + Destroy: false, + Config: buildClusterNetworkConfig(testAccClusterNetworkName, testAccClusterNetworkDescription, testAccClusterNetworkEnable, testAccClusterNetworkDefaultPhysicalNIC), + Check: resource.ComposeTestCheckFunc( + testAccClusterNetworkExists(ctx, testAccClusterNetworkResourceName, clusterNetwork), + resource.TestCheckResourceAttr(testAccClusterNetworkResourceName, constants.FieldCommonName, testAccClusterNetworkName), + resource.TestCheckResourceAttr(testAccClusterNetworkResourceName, constants.FieldCommonDescription, testAccClusterNetworkDescription), + resource.TestCheckResourceAttr(testAccClusterNetworkResourceName, constants.FieldClusterNetworkEnable, testAccClusterNetworkEnable), + resource.TestCheckResourceAttr(testAccClusterNetworkResourceName, constants.FieldClusterNetworkDefaultPhysicalNIC, testAccClusterNetworkDefaultPhysicalNIC), + ), + }, + }, + }) +} + +func testAccClusterNetworkExists(ctx context.Context, n string, clusterNetwork *harvsternetworkv1.ClusterNetwork) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Resource %s not found. ", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("Resource %s ID not set. ", n) + } + + id := rs.Primary.ID + c := testAccProvider.Meta().(*client.Client) + + namespace, name, err := helper.IDParts(id) + if err != nil { + return err + } + foundClusterNetwork, err := c.HarvesterNetworkClient.NetworkV1beta1().ClusterNetworks(namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return err + } + clusterNetwork = foundClusterNetwork + return nil + } +} diff --git a/internal/tests/resource_image_test.go b/internal/tests/resource_image_test.go new file mode 100644 index 00000000..8eefa570 --- /dev/null +++ b/internal/tests/resource_image_test.go @@ -0,0 +1,127 @@ +package tests + +import ( + "context" + "fmt" + "regexp" + "testing" + + harvsterv1 "github.com/harvester/harvester/pkg/apis/harvesterhci.io/v1beta1" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/harvester/terraform-provider-harvester/pkg/client" + "github.com/harvester/terraform-provider-harvester/pkg/constants" + "github.com/harvester/terraform-provider-harvester/pkg/helper" +) + +const ( + testAccImageName = "test-acc-foo" + testAccImageResourceName = constants.ResourceTypeImage + "." + testAccImageName + testAccImageDescription = "Terraform Harvester image acceptance test" + testAccImageDisplayName = "foo" + + testAccImageURL = "http://cloud-images.ubuntu.com/releases/focal/release/ubuntu-20.04-server-cloudimg-amd64.img" + + testAccImageConfigTemplate = ` +resource %s "%s" { + %s = "%s" + %s = "%s" + %s = "%s" + %s = "%s" +} +` +) + +func buildImageConfig(name, description, displayName, url string) string { + return fmt.Sprintf(testAccImageConfigTemplate, constants.ResourceTypeImage, name, + constants.FieldCommonName, name, + constants.FieldCommonDescription, description, + constants.FieldImageDisplayName, displayName, + constants.FieldImageURL, url) +} + +func TestAccImage_basic(t *testing.T) { + var ( + image *harvsterv1.VirtualMachineImage + ctx = context.Background() + ) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckImageDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: buildImageConfig(testAccImageName, testAccImageDescription, "", testAccImageURL), + ExpectError: regexp.MustCompile(fmt.Sprintf(`%s must not be empty`, constants.FieldImageDisplayName)), + }, + { + Config: buildImageConfig(testAccImageName, testAccImageDescription, testAccImageDisplayName, ""), + ExpectError: regexp.MustCompile(fmt.Sprintf(`expected "%s" url to not be empty`, constants.FieldImageURL)), + }, + { + Config: buildImageConfig(testAccImageName, testAccImageDescription, testAccImageDisplayName, testAccImageURL), + Check: resource.ComposeTestCheckFunc( + testAccImageExists(ctx, testAccImageResourceName, image), + resource.TestCheckResourceAttr(testAccImageResourceName, constants.FieldCommonName, testAccImageName), + resource.TestCheckResourceAttr(testAccImageResourceName, constants.FieldCommonDescription, testAccImageDescription), + resource.TestCheckResourceAttr(testAccImageResourceName, constants.FieldImageURL, testAccImageURL), + ), + }, + }, + }) +} + +func testAccImageExists(ctx context.Context, n string, image *harvsterv1.VirtualMachineImage) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Resource %s not found. ", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("Resource %s ID not set. ", n) + } + + id := rs.Primary.ID + c := testAccProvider.Meta().(*client.Client) + + namespace, name, err := helper.IDParts(id) + if err != nil { + return err + } + foundImage, err := c.HarvesterClient.HarvesterhciV1beta1().VirtualMachineImages(namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return err + } + image = foundImage + return nil + } +} + +func testAccCheckImageDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != constants.ResourceTypeImage { + continue + } + + c := testAccProvider.Meta().(*client.Client) + namespace, name, err := helper.IDParts(rs.Primary.ID) + if err != nil { + return err + } + + imageStateRefreshFunc := getResourceStateRefreshFunc(func() (interface{}, error) { + return c.HarvesterClient.HarvesterhciV1beta1().VirtualMachineImages(namespace).Get(ctx, name, metav1.GetOptions{}) + }) + stateConf := getStateChangeConf(imageStateRefreshFunc) + if _, err = stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf( + "[ERROR] waiting for image (%s) to be removed: %s", rs.Primary.ID, err) + } + } + return nil + } +} diff --git a/internal/tests/resource_keypair_test.go b/internal/tests/resource_keypair_test.go new file mode 100644 index 00000000..2a056895 --- /dev/null +++ b/internal/tests/resource_keypair_test.go @@ -0,0 +1,130 @@ +package tests + +import ( + "context" + "fmt" + "regexp" + "testing" + + harvsterv1 "github.com/harvester/harvester/pkg/apis/harvesterhci.io/v1beta1" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/harvester/terraform-provider-harvester/pkg/client" + "github.com/harvester/terraform-provider-harvester/pkg/constants" + "github.com/harvester/terraform-provider-harvester/pkg/helper" +) + +const ( + testAccKeyPairName = "test-acc-foo" + testAccKeyPairResourceName = constants.ResourceTypeKeyPair + "." + testAccKeyPairName + testAccKeyPairDescription = "Terraform Harvester keyPair acceptance test" + + testAccKeyPairPublicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDtlMZYPhQCRU+PgYe8c+YAQghMi7cJpj60HpIGZKgPQ9uLWAIWm6e38mhzNXtbPU4OiFgML9tTJ0UoHzZYueeo1H58R7O65rcQRSCbICj645B/x4MYJhrLMpSLIo1SAT1RiWTaAnffwYPfbQ8r8R1YxizoPZGe4dj4BHwBmEAQhUs4ymxWs5TNmATv/XcF2FnUJvUNATk4WGX+wBNDp6T47wgnMz/D9WOcum776SDozXvfbcZDDpdNNjZyzq3e/admx2+wIatjCFHTQmwgpyphMeOeYKT/w1YitHFlaipys2L4ABQv2gzHjpGVa6YE9B25Ez9vly/hbjz3LN28gf9oBGujWeisU9QZ1Gn97Ao6rF/zStE1c8Glphg60DuYa1JrlbrhmHtMmR9VZ+xROuc//2MixeBPd9ysFoY0Vki+GNLc/gzAGbN0GF6uviFx57pWQ50zoSA9en9Ss9Gji2eYd2yVOSCR4y6NsP/bzDUefnGe87fFfvMAu6LjXIjLwcM=" + testAccKeyPairPublicKeyUpdate = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC8gVNwltyQ/aHbZATnp1lcD4mLHyRjpF3AozA6wcqidDjQ3sda3OIqLLwr1hGcxc5g21mkyt9H6jyoEDnbiYgUfXdmtjCxRUHoHqQYq401ApnF3Nt3KF+aIwtHCJlCIPCRieEBgAc7N/MP6w9fFD1/eJIpUGN50jAH/tVCKq7vlHSATYXsKyhHqv3KJztBctskLDVXoDNaeV9szkGr/fwjTWIgkGZ3YsI74sv+MRcc1ainmQk6Ji89cIml7EOMQJdX38ieUyhXX6vwoa9FnjSysYn/pQZVgyNCy80b3ZkVQPaCslNQGXHactl6SB+S3eiu5gY4LzSW1fzJhN5UT9MUbZLqXowbi71Z453qQbS+FCAch7Pnj7QbW93dN/m8HG1E3OyLpX5q4xtB2OTYatYMO9NQy6z5loCYBuciJGl/sfTci6YFhvyuLA/qpvWmKN3neTMhcglyN+0JmnVSJ39pcCoM3vH4wHdQupeiAl/laIyMN/vBwbH49bTbT3WFT9c=" + + testAccKeyPairConfigTemplate = ` +resource %s "%s" { + %s = "%s" + %s = "%s" + %s = "%s" +} +` +) + +func buildKeyPairConfig(name, description, publicKey string) string { + return fmt.Sprintf(testAccKeyPairConfigTemplate, constants.ResourceTypeKeyPair, name, + constants.FieldCommonName, name, + constants.FieldCommonDescription, description, + constants.FieldKeyPairPublicKey, publicKey) +} + +func TestAccKeyPair_basic(t *testing.T) { + var ( + keyPair *harvsterv1.KeyPair + ctx = context.Background() + ) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckKeyPairDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: buildKeyPairConfig(testAccKeyPairName, testAccKeyPairDescription, ""), + ExpectError: regexp.MustCompile(fmt.Sprintf(`%s must not be empty`, constants.FieldKeyPairPublicKey)), + }, + { + Config: buildKeyPairConfig(testAccKeyPairName, testAccKeyPairDescription, testAccKeyPairPublicKey), + Check: resource.ComposeTestCheckFunc( + testAccKeyPairExists(ctx, testAccKeyPairResourceName, keyPair), + resource.TestCheckResourceAttr(testAccKeyPairResourceName, constants.FieldCommonName, testAccKeyPairName), + resource.TestCheckResourceAttr(testAccKeyPairResourceName, constants.FieldCommonDescription, testAccKeyPairDescription), + resource.TestCheckResourceAttr(testAccKeyPairResourceName, constants.FieldKeyPairPublicKey, testAccKeyPairPublicKey), + ), + }, + { + Config: buildKeyPairConfig(testAccKeyPairName, testAccKeyPairDescription, testAccKeyPairPublicKeyUpdate), + Check: resource.ComposeTestCheckFunc( + testAccKeyPairExists(ctx, testAccKeyPairResourceName, keyPair), + resource.TestCheckResourceAttr(testAccKeyPairResourceName, constants.FieldCommonName, testAccKeyPairName), + resource.TestCheckResourceAttr(testAccKeyPairResourceName, constants.FieldCommonDescription, testAccKeyPairDescription), + resource.TestCheckResourceAttr(testAccKeyPairResourceName, constants.FieldKeyPairPublicKey, testAccKeyPairPublicKeyUpdate), + ), + }, + }, + }) +} + +func testAccKeyPairExists(ctx context.Context, n string, keyPair *harvsterv1.KeyPair) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Resource %s not found. ", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("Resource %s ID not set. ", n) + } + + id := rs.Primary.ID + c := testAccProvider.Meta().(*client.Client) + + namespace, name, err := helper.IDParts(id) + if err != nil { + return err + } + foundKeyPair, err := c.HarvesterClient.HarvesterhciV1beta1().KeyPairs(namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return err + } + keyPair = foundKeyPair + return nil + } +} + +func testAccCheckKeyPairDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != constants.ResourceTypeKeyPair { + continue + } + + c := testAccProvider.Meta().(*client.Client) + namespace, name, err := helper.IDParts(rs.Primary.ID) + if err != nil { + return err + } + + keyPairStateRefreshFunc := getResourceStateRefreshFunc(func() (interface{}, error) { + return c.HarvesterClient.HarvesterhciV1beta1().KeyPairs(namespace).Get(ctx, name, metav1.GetOptions{}) + }) + stateConf := getStateChangeConf(keyPairStateRefreshFunc) + if _, err = stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf( + "[ERROR] waiting for keyPair (%s) to be removed: %s", rs.Primary.ID, err) + } + } + return nil + } +} diff --git a/internal/tests/resource_network_test.go b/internal/tests/resource_network_test.go new file mode 100644 index 00000000..26c9342c --- /dev/null +++ b/internal/tests/resource_network_test.go @@ -0,0 +1,120 @@ +package tests + +import ( + "context" + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + nadv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/harvester/terraform-provider-harvester/pkg/client" + "github.com/harvester/terraform-provider-harvester/pkg/constants" + "github.com/harvester/terraform-provider-harvester/pkg/helper" +) + +const ( + testAccNetworkName = "test-acc-foo" + testAccNetworkResourceName = constants.ResourceTypeNetwork + "." + testAccNetworkName + testAccNetworkDescription = "Terraform Harvester Network acceptance test" + + testAccNetworkVlanID = "10" + + testAccNetworkConfigTemplate = ` +resource %s "%s" { + %s = "%s" + %s = "%s" + %s = %s +} +` +) + +func buildNetworkConfig(name, description, vlanID string) string { + return fmt.Sprintf(testAccNetworkConfigTemplate, constants.ResourceTypeNetwork, name, + constants.FieldCommonName, name, + constants.FieldCommonDescription, description, + constants.FieldNetworkVlanID, vlanID) +} + +func TestAccNetwork_basic(t *testing.T) { + var ( + network *nadv1.NetworkAttachmentDefinition + ctx = context.Background() + ) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckNetworkDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: buildNetworkConfig(testAccNetworkName, testAccNetworkDescription, "0"), + ExpectError: regexp.MustCompile(fmt.Sprintf(`expected %s to be in the range \(1 - 4094\)`, constants.FieldNetworkVlanID)), + }, + { + Config: buildNetworkConfig(testAccNetworkName, testAccNetworkDescription, testAccNetworkVlanID), + Check: resource.ComposeTestCheckFunc( + testAccNetworkExists(ctx, testAccNetworkResourceName, network), + resource.TestCheckResourceAttr(testAccNetworkResourceName, constants.FieldCommonName, testAccNetworkName), + resource.TestCheckResourceAttr(testAccNetworkResourceName, constants.FieldCommonDescription, testAccNetworkDescription), + resource.TestCheckResourceAttr(testAccNetworkResourceName, constants.FieldNetworkVlanID, testAccNetworkVlanID), + ), + }, + }, + }) +} + +func testAccNetworkExists(ctx context.Context, n string, network *nadv1.NetworkAttachmentDefinition) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Resource %s not found. ", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("Resource %s ID not set. ", n) + } + + id := rs.Primary.ID + c := testAccProvider.Meta().(*client.Client) + + namespace, name, err := helper.IDParts(id) + if err != nil { + return err + } + foundNetwork, err := c.HarvesterClient.K8sCniCncfIoV1().NetworkAttachmentDefinitions(namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return err + } + network = foundNetwork + return nil + } +} + +func testAccCheckNetworkDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != constants.ResourceTypeNetwork { + continue + } + + c := testAccProvider.Meta().(*client.Client) + namespace, name, err := helper.IDParts(rs.Primary.ID) + if err != nil { + return err + } + + networkStateRefreshFunc := getResourceStateRefreshFunc(func() (interface{}, error) { + return c.HarvesterClient.K8sCniCncfIoV1().NetworkAttachmentDefinitions(namespace).Get(ctx, name, metav1.GetOptions{}) + }) + stateConf := getStateChangeConf(networkStateRefreshFunc) + if _, err = stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf( + "[ERROR] waiting for Network (%s) to be removed: %s", rs.Primary.ID, err) + } + } + return nil + } +} diff --git a/internal/tests/resource_virtualmachine_test.go b/internal/tests/resource_virtualmachine_test.go new file mode 100644 index 00000000..34d1d2b8 --- /dev/null +++ b/internal/tests/resource_virtualmachine_test.go @@ -0,0 +1,143 @@ +package tests + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kubevirtv1 "kubevirt.io/client-go/api/v1" + + "github.com/harvester/terraform-provider-harvester/pkg/client" + "github.com/harvester/terraform-provider-harvester/pkg/constants" + "github.com/harvester/terraform-provider-harvester/pkg/helper" +) + +const ( + testAccVirtualMachineName = "test-acc-foo" + testAccVirtualMachineResourceName = constants.ResourceTypeVirtualMachine + "." + testAccVirtualMachineName + testAccVirtualMachineDescription = "Terraform Harvester vm acceptance test" + + testAccVirtualMachineMemory = "1Gi" + testAccVirtualMachineMemoryUpdate = "2Gi" + + testAccVirtualMachineConfigTemplate = ` +resource %s "%s" { + %s = "%s" + %s = "%s" + + cpu = 1 + %s = "%s" + + start = true + machine_type = "q35" + + network_interface { + name = "default" + } + + disk { + name = "rootdisk" + type = "disk" + bus = "virtio" + boot_order = 1 + + container_image_name = "kubevirt/fedora-cloud-container-disk-demo:v0.35.0" + } +} +` +) + +func buildVirtualMachineConfig(name, description, memory string) string { + return fmt.Sprintf(testAccVirtualMachineConfigTemplate, constants.ResourceTypeVirtualMachine, name, + constants.FieldCommonName, name, + constants.FieldCommonDescription, description, + constants.FieldVirtualMachineMemory, memory) +} + +func TestAccVirtualMachine_basic(t *testing.T) { + var ( + vm *kubevirtv1.VirtualMachine + ctx = context.Background() + ) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckVirtualMachineDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: buildVirtualMachineConfig(testAccVirtualMachineName, testAccVirtualMachineDescription, testAccVirtualMachineMemory), + Check: resource.ComposeTestCheckFunc( + testAccVirtualMachineExists(ctx, testAccVirtualMachineResourceName, vm), + resource.TestCheckResourceAttr(testAccVirtualMachineResourceName, constants.FieldCommonName, testAccVirtualMachineName), + resource.TestCheckResourceAttr(testAccVirtualMachineResourceName, constants.FieldCommonDescription, testAccVirtualMachineDescription), + resource.TestCheckResourceAttr(testAccVirtualMachineResourceName, constants.FieldVirtualMachineMemory, testAccVirtualMachineMemory), + ), + }, + { + Config: buildVirtualMachineConfig(testAccVirtualMachineName, testAccVirtualMachineDescription, testAccVirtualMachineMemoryUpdate), + Check: resource.ComposeTestCheckFunc( + testAccVirtualMachineExists(ctx, testAccVirtualMachineResourceName, vm), + resource.TestCheckResourceAttr(testAccVirtualMachineResourceName, constants.FieldCommonName, testAccVirtualMachineName), + resource.TestCheckResourceAttr(testAccVirtualMachineResourceName, constants.FieldCommonDescription, testAccVirtualMachineDescription), + resource.TestCheckResourceAttr(testAccVirtualMachineResourceName, constants.FieldVirtualMachineMemory, testAccVirtualMachineMemoryUpdate), + ), + }, + }, + }) +} + +func testAccVirtualMachineExists(ctx context.Context, n string, vm *kubevirtv1.VirtualMachine) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Resource %s not found. ", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("Resource %s ID not set. ", n) + } + + id := rs.Primary.ID + c := testAccProvider.Meta().(*client.Client) + + namespace, name, err := helper.IDParts(id) + if err != nil { + return err + } + foundVM, err := c.HarvesterClient.KubevirtV1().VirtualMachines(namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return err + } + vm = foundVM + return nil + } +} + +func testAccCheckVirtualMachineDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != constants.ResourceTypeVirtualMachine { + continue + } + + c := testAccProvider.Meta().(*client.Client) + namespace, name, err := helper.IDParts(rs.Primary.ID) + if err != nil { + return err + } + + virtualMachineStateRefreshFunc := getResourceStateRefreshFunc(func() (interface{}, error) { + return c.HarvesterClient.KubevirtV1().VirtualMachines(namespace).Get(ctx, name, metav1.GetOptions{}) + }) + stateConf := getStateChangeConf(virtualMachineStateRefreshFunc) + if _, err = stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf( + "[ERROR] waiting for virtual machine (%s) to be removed: %s", rs.Primary.ID, err) + } + } + return nil + } +} diff --git a/internal/tests/resource_volume_test.go b/internal/tests/resource_volume_test.go new file mode 100644 index 00000000..aa8c4314 --- /dev/null +++ b/internal/tests/resource_volume_test.go @@ -0,0 +1,115 @@ +package tests + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + cdiv1beta1 "kubevirt.io/containerized-data-importer/pkg/apis/core/v1beta1" + + "github.com/harvester/terraform-provider-harvester/pkg/client" + "github.com/harvester/terraform-provider-harvester/pkg/constants" + "github.com/harvester/terraform-provider-harvester/pkg/helper" +) + +const ( + testAccVolumeName = "test-acc-foo" + testAccVolumeResourceName = constants.ResourceTypeVolume + "." + testAccVolumeName + testAccVolumeDescription = "Terraform Harvester volume acceptance test" + + testAccVolumeSize = "1Gi" + + testAccVolumeConfigTemplate = ` +resource %s "%s" { + %s = "%s" + %s = "%s" + %s = "%s" +} +` +) + +func buildVolumeConfig(name, description, size string) string { + return fmt.Sprintf(testAccVolumeConfigTemplate, constants.ResourceTypeVolume, name, + constants.FieldCommonName, name, + constants.FieldCommonDescription, description, + constants.FieldVolumeSize, size) +} + +func TestAccVolume_basic(t *testing.T) { + var ( + volume *cdiv1beta1.DataVolume + ctx = context.Background() + ) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckVolumeDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: buildVolumeConfig(testAccVolumeName, testAccVolumeDescription, testAccVolumeSize), + Check: resource.ComposeTestCheckFunc( + testAccVolumeExists(ctx, testAccVolumeResourceName, volume), + resource.TestCheckResourceAttr(testAccVolumeResourceName, constants.FieldCommonName, testAccVolumeName), + resource.TestCheckResourceAttr(testAccVolumeResourceName, constants.FieldCommonDescription, testAccVolumeDescription), + resource.TestCheckResourceAttr(testAccVolumeResourceName, constants.FieldVolumeSize, testAccVolumeSize), + ), + }, + }, + }) +} + +func testAccVolumeExists(ctx context.Context, n string, volume *cdiv1beta1.DataVolume) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Resource %s not found. ", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("Resource %s ID not set. ", n) + } + + id := rs.Primary.ID + c := testAccProvider.Meta().(*client.Client) + + namespace, name, err := helper.IDParts(id) + if err != nil { + return err + } + foundVolume, err := c.HarvesterClient.CdiV1beta1().DataVolumes(namespace).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return err + } + volume = foundVolume + return nil + } +} + +func testAccCheckVolumeDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != constants.ResourceTypeVolume { + continue + } + + c := testAccProvider.Meta().(*client.Client) + namespace, name, err := helper.IDParts(rs.Primary.ID) + if err != nil { + return err + } + + volumeStateRefreshFunc := getResourceStateRefreshFunc(func() (interface{}, error) { + return c.HarvesterClient.CdiV1beta1().DataVolumes(namespace).Get(ctx, name, metav1.GetOptions{}) + }) + stateConf := getStateChangeConf(volumeStateRefreshFunc) + if _, err = stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf( + "[ERROR] waiting for volume (%s) to be removed: %s", rs.Primary.ID, err) + } + } + return nil + } +}