diff --git a/internal/providers/terraform/ibm/is_instance.go b/internal/providers/terraform/ibm/is_instance.go index 3eb15baff5a..e36810f3de9 100644 --- a/internal/providers/terraform/ibm/is_instance.go +++ b/internal/providers/terraform/ibm/is_instance.go @@ -17,6 +17,7 @@ func getIsInstanceRegistryItem() *schema.RegistryItem { // valid profile values https://cloud.ibm.com/docs/vpc?topic=vpc-profiles&interface=ui // profile names in Global Catalog contain dots instead of dashes func newIsInstance(d *schema.ResourceData, u *schema.UsageData) *schema.Resource { + region := d.Get("region").String() profile := d.Get("profile").String() zone := d.Get("zone").String() @@ -25,26 +26,17 @@ func newIsInstance(d *schema.ResourceData, u *schema.UsageData) *schema.Resource isDedicated := !((dedicatedHost == "") && (dedicatedHostGroup == "")) name := d.Get("name").String() - boot_volume := make([]struct { - Name string - Size int64 - }, 0) + // Defaults + bootVolumeName := "Unnamed boot volume" + var bootVolumeSize int64 = 100 - boot_volume_parse := d.Get("boot_volume").Array() - if len(boot_volume_parse) > 0 { - for _, volume := range boot_volume_parse { - name := volume.Get("name").String() - if name == "" { - name = "Unnamed boot volume" - } - size := volume.Get("size").Int() - if size == 0 { - size = 100 - } - boot_volume = append(boot_volume, struct { - Name string - Size int64 - }{Name: name, Size: size}) + bv := d.Get("boot_volume").Array() + if len(bv) > 0 { + if bv[0].Get("name").String() != "" { + bootVolumeName = bv[0].Get("name").String() + } + if bv[0].Get("size").Int() != 0 { + bootVolumeSize = bv[0].Get("size").Int() } } @@ -54,7 +46,10 @@ func newIsInstance(d *schema.ResourceData, u *schema.UsageData) *schema.Resource Profile: profile, Zone: zone, IsDedicated: isDedicated, - BootVolume: boot_volume, + BootVolume: struct { + Name string + Size int64 + }{Name: bootVolumeName, Size: bootVolumeSize}, } r.PopulateUsage(u) diff --git a/internal/providers/terraform/ibm/testdata/is_instance_test/is_instance_test.golden b/internal/providers/terraform/ibm/testdata/is_instance_test/is_instance_test.golden index a0ccdc4dc0f..ca3bc797dd3 100644 --- a/internal/providers/terraform/ibm/testdata/is_instance_test/is_instance_test.golden +++ b/internal/providers/terraform/ibm/testdata/is_instance_test/is_instance_test.golden @@ -1,57 +1,34 @@ - Name Monthly Qty Unit Monthly Cost - - ibm_is_instance.testBalancedInstance - ├─ CPU hours (32 CPUs, us-east-1) (first 1 CPU hours) 1 CPU hours $0.31 - ├─ CPU hours (32 CPUs, us-east-1) (over 0 CPU hours) 1 CPU hours $0.02 - ├─ Memory hours (128 GB, us-east-1) (first 1 Memory hours) 1 Memory hours $0.57 - ├─ Memory hours (128 GB, us-east-1) (over 0 Memory hours) 1 Memory hours $0.01 - └─ Storage GB hours (600 GB * 2, us-east-1) 876,000 Storage GB hours $23,652.00 - - ibm_is_instance.testBalancedInstanceWithBootVolume - ├─ CPU hours (8 CPUs, us-east-1) (first 1 CPU hours) 1 CPU hours $0.31 - ├─ CPU hours (8 CPUs, us-east-1) (over 0 CPU hours) 1 CPU hours $0.02 - ├─ Memory hours (32 GB, us-east-1) (first 1 Memory hours) 1 Memory hours $0.57 - ├─ Memory hours (32 GB, us-east-1) (over 0 Memory hours) 1 Memory hours $0.01 - └─ Boot volume (boot-volume-label, 150 GB) 109,500 GB Hours $12.59 - - ibm_is_instance.testComputeInstance - ├─ CPU hours (2 CPUs, us-east-1) (first 1 CPU hours) 1 CPU hours $0.31 - ├─ CPU hours (2 CPUs, us-east-1) (over 0 CPU hours) 1 CPU hours $0.02 - └─ Memory hours (4 GB, us-east-1) (first 1 Memory hours) 1 Memory hours $0.84 - └─ Memory hours (4 GB, us-east-1) (over 0 Memory hours) 1 Memory hours $0.01 - - ibm_is_instance.testGpuInstance - ├─ CPU hours (16 CPUs, us-east-1) (first 1 CPU hours) 1 CPU hours $0.33 - ├─ CPU hours (16 CPUs, us-east-1) (over 0 CPU hours) 1 CPU hours $0.03 - ├─ Memory hours (128 GB, us-east-1) (first 1 Memory hours) 1 Memory hours $0.45 - ├─ Memory hours (128 GB, us-east-1) (over 0 Memory hours) 1 Memory hours $0.00 - └─ Gpu hours (2 GPUs, Tesla V100, us-east-1) 1,460 Gpu hours $3,051.40 - - ibm_is_instance.testIbmZInstance - ├─ CPU hours (16 CPUs, us-east-1) (first 1 CPU hours) 1 CPU hours $0.39 - ├─ CPU hours (16 CPUs, us-east-1) (over 0 CPU hours) 1 CPU hours $0.05 - └─ Memory hours (64 GB, us-east-1) (first 1 Memory hours) 1 Memory hours $0.56 - └─ Memory hours (64 GB, us-east-1) (over 0 Memory hours) 1 Memory hours $0.01 - - ibm_is_instance.testInstanceWithoutUsage - ├─ CPU hours (2 CPUs, us-east-1) (first 1 CPU hours) Monthly cost depends on usage: $0.31 per CPU hours - ├─ CPU hours (2 CPUs, us-east-1) (over 0 CPU hours) Monthly cost depends on usage: $0.0249876337 per CPU hours - └─ Memory hours (4 GB, us-east-1) (first 1 Memory hours) Monthly cost depends on usage: $0.84 per Memory hours - └─ Memory hours (4 GB, us-east-1) (over 0 Memory hours) Monthly cost depends on usage: $0.0075416569 per Memory hours - - ibm_is_vpc.testVpc - ├─ VPC instance 1 Instance $0.00 - ├─ VPC egress free allowance (first 5GB) Monthly cost depends on usage: $0.00 per GB - └─ VPC egress us-east (first 9995 GB) Monthly cost depends on usage: $0.090915 per GB - └─ VPC egress us-east (next 40000 GB) Monthly cost depends on usage: $0.086735 per GB - └─ VPC egress us-east (next 100000 GB) Monthly cost depends on usage: $0.07315 per GB - └─ VPC egress us-east (over 149995 GB) Monthly cost depends on usage: $0.05225 per GB - - OVERALL TOTAL $26,720.80 + Name Monthly Qty Unit Monthly Cost + + ibm_is_instance.vsi + ├─ Instance Hours (cx2-2x4) 730 Hours $64.97 + └─ Boot volume (Unnamed boot volume, 100 GB) 73,000 Hours $8.39 + + ibm_is_instance.vsi_boot_volume + ├─ Instance Hours (cx2-2x4) 730 Hours $64.97 + └─ Boot volume (boot-volume-label, 250 GB) 182,500 Hours $20.98 + + ibm_is_instance.vsi_dedicated_host + ├─ Instance Hours (cx2-2x4) 730 Hours $64.97 + └─ Boot volume (Unnamed boot volume, 100 GB) 73,000 Hours $8.39 + + ibm_is_vpc.vpc + ├─ VPC instance 1 Instance $0.00 + ├─ VPC egress free allowance (first 5GB) Monthly cost depends on usage: $0.00 per GB + └─ VPC egress us-south (first 9995 GB) Monthly cost depends on usage: $0.090915 per GB + └─ VPC egress us-south (next 40000 GB) Monthly cost depends on usage: $0.086735 per GB + └─ VPC egress us-south (next 100000 GB) Monthly cost depends on usage: $0.07315 per GB + └─ VPC egress us-south (over 149995 GB) Monthly cost depends on usage: $0.05225 per GB + + OVERALL TOTAL $232.67 ────────────────────────────────── 9 cloud resources were detected: -∙ 7 were estimated, all of which include usage-based costs, see https://infracost.io/usage-file -∙ 2 were free: +∙ 4 were estimated, all of which include usage-based costs, see https://infracost.io/usage-file +∙ 3 were free: ∙ 1 x ibm_is_ssh_key - ∙ 1 x ibm_is_subnet \ No newline at end of file + ∙ 1 x ibm_is_subnet + ∙ 1 x ibm_resource_group +∙ 2 are not supported yet, see https://infracost.io/requested-resources: + ∙ 1 x ibm_is_dedicated_host + ∙ 1 x ibm_is_dedicated_host_group \ No newline at end of file diff --git a/internal/providers/terraform/ibm/testdata/is_instance_test/is_instance_test.tf b/internal/providers/terraform/ibm/testdata/is_instance_test/is_instance_test.tf index bf33e413153..305302a2f75 100644 --- a/internal/providers/terraform/ibm/testdata/is_instance_test/is_instance_test.tf +++ b/internal/providers/terraform/ibm/testdata/is_instance_test/is_instance_test.tf @@ -4,145 +4,278 @@ terraform { ibm = { source = "IBM-Cloud/ibm" } + random = { + source = "hashicorp/random" + } } } provider "ibm" { - region = "us-east" + region = "us-south" } -resource "ibm_is_vpc" "testVpc" { - name = "test-vpc" +# Access random string generated with random_string.unique_identifier.result +resource "random_string" "unique_identifier" { + length = 6 + special = false + upper = false } -resource "ibm_is_subnet" "testSubnet" { - name = "test-subnet" - vpc = ibm_is_vpc.testVpc.id - zone = "us-east-1" - ipv4_cidr_block = "10.240.0.0/24" +resource "ibm_resource_group" "resource_group" { + name = "rg-${random_string.unique_identifier.result}" } -resource "ibm_is_ssh_key" "testSshKey" { - name = "test-ssh" - public_key = file("~/.ssh/id_rsa.pub") +resource "ibm_is_vpc" "vpc" { + name = "vpc-${random_string.unique_identifier.result}" + resource_group = ibm_resource_group.resource_group.id } -resource "ibm_is_instance" "testBalancedInstance" { - name = "test-instance-1" - image = "r006-f137ea64-0d27-4d81-afe0-353fd0557e81" - profile = "bx2d-32x128" - - primary_network_interface { - subnet = ibm_is_subnet.testSubnet.id - } - - network_interfaces { - name = "eth1" - subnet = ibm_is_subnet.testSubnet.id - } - - vpc = ibm_is_vpc.testVpc.id - zone = "us-east-1" - keys = [ibm_is_ssh_key.testSshKey.id] +resource "ibm_is_subnet" "subnet" { + name = "subnet-${random_string.unique_identifier.result}" + ipv4_cidr_block = "10.240.0.0/24" + resource_group = ibm_resource_group.resource_group.id + vpc = ibm_is_vpc.vpc.id + zone = "us-south-1" } -resource "ibm_is_instance" "testComputeInstance" { - name = "test-instance-2" - image = "r006-f137ea64-0d27-4d81-afe0-353fd0557e81" - profile = "cx2-2x4" - - primary_network_interface { - subnet = ibm_is_subnet.testSubnet.id - } - - network_interfaces { - name = "eth1" - subnet = ibm_is_subnet.testSubnet.id - } - - vpc = ibm_is_vpc.testVpc.id - zone = "us-east-1" - keys = [ibm_is_ssh_key.testSshKey.id] +resource "ibm_is_ssh_key" "ssh_key" { + name = "ssh-key-${random_string.unique_identifier.result}" + public_key = file("~/.ssh/id_ed25519.pub") + resource_group = ibm_resource_group.resource_group.id + type = "ed25519" } -resource "ibm_is_instance" "testGpuInstance" { - name = "test-instance-3" - image = "r006-f137ea64-0d27-4d81-afe0-353fd0557e81" - profile = "gx2-16x128x2v100" - +resource "ibm_is_instance" "vsi" { + for_each = toset(local.profiles) + name = "vsi-instance-${random_string.unique_identifier.result}-${each.key}" + # name = "vsi-instance-${random_string.unique_identifier.result}" + image = "r006-f137ea64-0d27-4d81-afe0-353fd0557e81" + keys = [ibm_is_ssh_key.ssh_key.id] + # profile = "cx2-2x4" + profile = each.key + resource_group = ibm_resource_group.resource_group.id + vpc = ibm_is_vpc.vpc.id + zone = "us-south-1" primary_network_interface { - subnet = ibm_is_subnet.testSubnet.id + subnet = ibm_is_subnet.subnet.id } - network_interfaces { name = "eth1" - subnet = ibm_is_subnet.testSubnet.id + subnet = ibm_is_subnet.subnet.id } - - vpc = ibm_is_vpc.testVpc.id - zone = "us-east-1" - keys = [ibm_is_ssh_key.testSshKey.id] } -resource "ibm_is_instance" "testIbmZInstance" { - name = "test-instance-4" - image = "r006-f137ea64-0d27-4d81-afe0-353fd0557e81" - profile = "bz2-16x64" - +resource "ibm_is_instance" "vsi_boot_volume" { + for_each = toset(local.profiles) + name = "vsi-instance-boot-volume-${random_string.unique_identifier.result}-${each.key}" + # name = "vsi-instance-boot-volume-${random_string.unique_identifier.result}" + image = "r006-f137ea64-0d27-4d81-afe0-353fd0557e81" + keys = [ibm_is_ssh_key.ssh_key.id] + # profile = "cx2-2x4" + profile = each.key + resource_group = ibm_resource_group.resource_group.id + vpc = ibm_is_vpc.vpc.id + zone = "us-south-1" primary_network_interface { - subnet = ibm_is_subnet.testSubnet.id + subnet = ibm_is_subnet.subnet.id } - network_interfaces { name = "eth1" - subnet = ibm_is_subnet.testSubnet.id + subnet = ibm_is_subnet.subnet.id + } + boot_volume { + name = "boot-volume-label" + size = 250 } - - vpc = ibm_is_vpc.testVpc.id - zone = "us-east-1" - keys = [ibm_is_ssh_key.testSshKey.id] } -resource "ibm_is_instance" "testInstanceWithoutUsage" { - name = "test-instance-5" - image = "r006-f137ea64-0d27-4d81-afe0-353fd0557e81" - profile = "cx2-2x4" - +resource "ibm_is_instance" "vsi_dedicated_host" { + for_each = toset(local.profiles) + name = "vsi-instance-dedicated-host-${random_string.unique_identifier.result}-${each.key}" + # name = "vsi-instance-dedicated-host-${random_string.unique_identifier.result}" + image = "r006-f137ea64-0d27-4d81-afe0-353fd0557e81" + keys = [ibm_is_ssh_key.ssh_key.id] + # profile = "cx2-2x4" + profile = each.key + resource_group = ibm_resource_group.resource_group.id + vpc = ibm_is_vpc.vpc.id + zone = "us-south-1" primary_network_interface { - subnet = ibm_is_subnet.testSubnet.id + subnet = ibm_is_subnet.subnet.id } - network_interfaces { name = "eth1" - subnet = ibm_is_subnet.testSubnet.id + subnet = ibm_is_subnet.subnet.id } - - vpc = ibm_is_vpc.testVpc.id - zone = "us-east-1" - keys = [ibm_is_ssh_key.testSshKey.id] + dedicated_host = ibm_is_dedicated_host.dedicated_host.id } +resource "ibm_is_dedicated_host" "dedicated_host" { + profile = "bx2d-host-152x608" + name = "example-dedicated-host-01" + host_group = ibm_is_dedicated_host_group.dedicated_host_group.id + resource_group = ibm_resource_group.resource_group.id +} -resource "ibm_is_instance" "testBalancedInstanceWithBootVolume" { - name = "test-instance-6" - image = "r006-f137ea64-0d27-4d81-afe0-353fd0557e81" - profile = "bx2-8x32" - - primary_network_interface { - subnet = ibm_is_subnet.testSubnet.id - } - - network_interfaces { - name = "eth1" - subnet = ibm_is_subnet.testSubnet.id - } - - vpc = ibm_is_vpc.testVpc.id - zone = "us-east-1" - keys = [ibm_is_ssh_key.testSshKey.id] +resource "ibm_is_dedicated_host_group" "dedicated_host_group" { + family = "compute" + class = "cx2" + zone = "us-south-1" + name = "example-dh-group-01" + resource_group = ibm_resource_group.resource_group.id +} - boot_volume { - name = "boot-volume-label" - size = 150 - } +locals { + profiles = [ + "cx2-2x4", + "cx2d-2x4", + "cx3d-2x5", + "cx3dc-2x5", + "bx2-2x8", + "bx2a-2x8", + "bx2d-2x8", + "bx3d-2x10", + "bx3dc-2x10", + "mx2-2x16", + "mx2d-2x16", + "ox2-2x16", + "mx3d-2x20", + "vx2d-2x28", + "ux2d-2x56", + "cx2-4x8", + "cx2d-4x8", + "cx3d-4x10", + "cx3dc-4x10", + "bx2-4x16", + "bx2a-4x16", + "bx2d-4x16", + "bx3d-4x20", + "bx3dc-4x20", + "mx2-4x32", + "mx2d-4x32", + "ox2-4x32", + "mx3d-4x40", + "vx2d-4x56", + "ux2d-4x112", + "cx2-8x16", + "cx2d-8x16", + "cx3d-8x20", + "cx3dc-8x20", + "bx2-8x32", + "bx2a-8x32", + "bx2d-8x32", + "bx3d-8x40", + "bx3dc-8x40", + "mx2-8x64", + "mx2d-8x64", + "ox2-8x64", + "gx2-8x64x1v100", + "mx3d-8x80", + "vx2d-8x112", + "ux2d-8x224", + "cx2-16x32", + "cx2d-16x32", + "cx3d-16x40", + "cx3dc-16x40", + "bx2-16x64", + "bx2a-16x64", + "bx2d-16x64", + "bx3d-16x80", + "gx3-16x80x1l4", + "bx3dc-16x80", + "mx2-16x128", + "mx2d-16x128", + "ox2-16x128", + "gx2-16x128x1v100", + "gx2-16x128x2v100", + "mx3d-16x160", + "vx2d-16x224", + "ux2d-16x448", + "cx3d-24x60", + "cx3dc-24x60", + "bx3d-24x120", + "bx3dc-24x120", + "gx3-24x120x1l40s", + "mx3d-24x240", + "cx2-32x64", + "cx2d-32x64", + "cx3d-32x80", + "cx3dc-32x80", + "bx2-32x128", + "bx2a-32x128", + "bx2d-32x128", + "bx3d-32x160", + "gx3-32x160x2l4", + "bx3dc-32x160", + "mx2-32x256", + "mx2d-32x256", + "ox2-32x256", + "gx2-32x256x2v100", + "mx3d-32x320", + "ux2d-36x1008", + "vx2d-44x616", + "cx2-48x96", + "cx2d-48x96", + "cx3d-48x120", + "cx3dc-48x120", + "bx2-48x192", + "bx2a-48x192", + "bx2d-48x192", + "bx3d-48x240", + "bx3dc-48x240", + "gx3-48x240x2l40s", + "mx2-48x384", + "mx2d-48x384", + "mx3d-48x480", + "ux2d-48x1344", + "cx2-64x128", + "cx2d-64x128", + "cx3d-64x160", + "cx3dc-64x160", + "bx2-64x256", + "bx2d-64x256", + "bx3d-64x320", + "gx3-64x320x4l4", + "bx3dc-64x320", + "mx2-64x512", + "mx2d-64x512", + "ox2-64x512", + "mx3d-64x640", + "ux2d-72x2016", + "vx2d-88x1232", + "cx2-96x192", + "cx2d-96x192", + "cx3d-96x240", + "cx3dc-96x240", + "bx2-96x384", + "bx2a-96x384", + "bx2d-96x384", + "bx3d-96x480", + "bx3dc-96x480", + "mx2-96x768", + "mx2d-96x768", + "ox2-96x768", + "mx3d-96x960", + "ux2d-100x2800", + "cx2-128x256", + "cx2d-128x256", + "cx3d-128x320", + "cx3dc-128x320", + "bx2-128x512", + "bx2a-128x512", + "bx2d-128x512", + "bx3d-128x640", + "mx2-128x1024", + "mx2d-128x1024", + "ox2-128x1024", + "mx3d-128x1280", + "vx2d-144x2016", + "gx3d-160x1792x8h100", + "cx3d-176x440", + "bx3d-176x880", + "mx3d-176x1760", + "vx2d-176x2464", + "ux2d-200x5600", + "bx2a-228x912" + ] } diff --git a/internal/providers/terraform/ibm/testdata/is_instance_test/is_instance_test.usage.yml b/internal/providers/terraform/ibm/testdata/is_instance_test/is_instance_test.usage.yml index c57593503e7..d5d445f811d 100644 --- a/internal/providers/terraform/ibm/testdata/is_instance_test/is_instance_test.usage.yml +++ b/internal/providers/terraform/ibm/testdata/is_instance_test/is_instance_test.usage.yml @@ -1,12 +1,4 @@ version: 0.1 resource_usage: - ibm_is_instance.testBalancedInstance: + ibm_is_instance: monthly_instance_hours: 730 - ibm_is_instance.testComputeInstance: - monthly_instance_hours: 730 - ibm_is_instance.testGpuInstance: - monthly_instance_hours: 730 - ibm_is_instance.testIbmZInstance: - monthly_instance_hours: 730 - ibm_is_instance.testBalancedInstanceWithBootVolume: - monthly_instance_hours: 730 \ No newline at end of file diff --git a/internal/resources/ibm/is_instance.go b/internal/resources/ibm/is_instance.go index ed31c7e589c..d73490b61bd 100644 --- a/internal/resources/ibm/is_instance.go +++ b/internal/resources/ibm/is_instance.go @@ -1,15 +1,11 @@ package ibm import ( - "encoding/json" "fmt" - "net/http" - "strings" "github.com/infracost/infracost/internal/resources" "github.com/infracost/infracost/internal/schema" "github.com/shopspring/decimal" - log "github.com/sirupsen/logrus" ) // IsInstance struct represents an IBM virtual server instance. @@ -22,7 +18,7 @@ type IsInstance struct { Profile string // should be values from CLI 'ibmcloud is instance-profiles' Zone string IsDedicated bool // will be true if a dedicated_host or dedicated_host_group is specified - BootVolume []struct { + BootVolume struct { Name string Size int64 } @@ -39,178 +35,32 @@ func (r *IsInstance) PopulateUsage(u *schema.UsageData) { resources.PopulateArgsWithUsage(r, u) } -type ArchType int64 - -const ( - x86 ArchType = iota - s390x -) - -func (s ArchType) toPlanName() string { - switch s { - case x86: - return "gen2-instance" - case s390x: - return "gen2-zvsi-instance" - default: - return "unknown" - } -} - -type Metadata struct { - Other Other `json:"other"` -} - -type Other struct { - Profile Profile `json:"profile"` -} - -type Profile struct { - DefaultConfig DefaultConfig `json:"default_config"` - Family string `json:"family"` - Generation string `json:"generation"` - ResourceType string `json:"resource_type"` -} - -type DefaultConfig struct { - AllowedProfileClasses []string `json:"allowed_profile_classes"` - Bandwidth int64 `json:"bandwidth"` - CPU int64 `json:"cpu"` - Freqency int64 `json:"freqency"` - GPUCount int64 `json:"gpu_count"` - GPUManufacturer string `json:"gpu_manufacturer"` - GPUMemory int64 `json:"gpu_memory"` - GPUModel string `json:"gpu_model"` - OSArchitecture []string `json:"os_architecture"` - PortSpeed int64 `json:"port_speed"` - RAM int64 `json:"ram"` - VcpuArchitecture string `json:"vcpu_architecture"` - VcpuManufacturer string `json:"vcpu_manufacturer"` - Disks []Disk `json:"disks"` -} - -type Disk struct { - DefaultInterfaceType string `json:"default_interface_type"` - DiskType string `json:"disk_type"` - Quantity int64 `json:"quantity"` - Size int64 `json:"size"` - SupportedInterfaceTypes []string `json:"supported_interface_types"` -} - -type CatalogInstance struct { - Id string `json:"id"` - Kind string `json:"kind"` - Metadata Metadata `json:"metadata"` -} - -type ProfileMultipliers struct { - Cpu decimal.Decimal - Memory decimal.Decimal -} - -var multipliers = map[string]ProfileMultipliers{ - "bx2": { - Cpu: decimal.NewFromFloat(0.902054794596651), - Memory: decimal.NewFromFloat(1.14870234843351), - }, - "bx2d": { - Cpu: decimal.NewFromFloat(0.902054794596651), - Memory: decimal.NewFromFloat(1.14870234843351), - }, - "cx2": { - Cpu: decimal.NewFromFloat(0.902054794596651), - Memory: decimal.NewFromFloat(1.70926497079), - }, - "cx2d": { - Cpu: decimal.NewFromFloat(0.902054794596651), - Memory: decimal.NewFromFloat(1.70926497079), - }, - "gx2": { - Cpu: decimal.NewFromFloat(0.963755342547062), - Memory: decimal.NewFromFloat(0.902054794596651), - }, - "mx2": { - Cpu: decimal.NewFromFloat(0.963755342547062), - Memory: decimal.NewFromFloat(0.902054794596651), - }, - "mx2d": { - Cpu: decimal.NewFromFloat(0.963755342547062), - Memory: decimal.NewFromFloat(0.902054794596651), - }, - "ux2d": { - Cpu: decimal.NewFromFloat(2.40343479472332), - Memory: decimal.NewFromFloat(0.902054794596651), - }, - "vx2d": { - Cpu: decimal.NewFromFloat(1.39565917819994), - Memory: decimal.NewFromFloat(0.902054794596651), - }, -} - -func getProfileMultiplier(profile string) ProfileMultipliers { - splitProfile := strings.Split(profile, "-") - multiplier := ProfileMultipliers{Cpu: decimal.NewFromInt(1), Memory: decimal.NewFromInt(1)} - if splitProfile[0] != "" { - val, ok := multipliers[splitProfile[0]] - if ok { - multiplier = val - } - } - return multiplier -} - -func getProfileFromCatalog(profile string) (CatalogInstance, error) { - var catalogProfile CatalogInstance - resp, err := http.Get(fmt.Sprintf("https://globalcatalog.cloud.ibm.com/api/v1/%s?include=metadata", profile)) - if err != nil { - log.Warn("Request to get instance profile failed", err) - return catalogProfile, err - } - decoder := json.NewDecoder(resp.Body) - err = decoder.Decode(&catalogProfile) - if err != nil { - log.Warn(err) - } - return catalogProfile, nil -} - -func parseArch(arch string) ArchType { - var archType ArchType - switch arch { - case "amd64": - archType = x86 - case "s390x": - archType = s390x - default: - archType = -1 - } - return archType -} - -func (r *IsInstance) storageCostComponent(arch ArchType, size int64, count int64) *schema.CostComponent { - var quantity *decimal.Decimal +func (r *IsInstance) instanceHoursCostComponent() *schema.CostComponent { + unit := "RESERVATION_HOURS_HOURLY" + planName := fmt.Sprintf("instance-%s", r.Profile) + unitMultiplier := int64(1) + var q *decimal.Decimal if r.MonthlyInstanceHours != nil { - quantity = decimalPtr(decimal.NewFromFloat(*r.MonthlyInstanceHours)) - quantity = decimalPtr(quantity.Mul(decimal.NewFromInt(size * count))) + q = decimalPtr(decimal.NewFromFloat(*r.MonthlyInstanceHours)) } - - var unit string - if arch == x86 { - unit = "IS_STORAGE_GIGABYTE_HOURS" + if r.IsDedicated { + q = decimalPtr(decimal.NewFromFloat(0)) + unitMultiplier = 0 } return &schema.CostComponent{ - Name: fmt.Sprintf("Storage GB hours (%d GB * %d, %s)", size, count, r.Zone), - Unit: "Storage GB hours", - UnitMultiplier: decimal.NewFromInt(1), - MonthlyQuantity: quantity, + Name: fmt.Sprintf("Instance Hours (%s)", r.Profile), + Unit: "Hours", + UnitMultiplier: decimal.NewFromInt(unitMultiplier), + MonthlyQuantity: q, ProductFilter: &schema.ProductFilter{ - VendorName: strPtr("ibm"), - Region: strPtr(r.Region), - Service: strPtr("is.instance"), + VendorName: strPtr("ibm"), + Region: strPtr(r.Region), + Service: strPtr("is.reservation"), + ProductFamily: strPtr("service"), AttributeFilters: []*schema.AttributeFilter{ - {Key: "planName", Value: strPtr(arch.toPlanName())}, + {Key: "planName", Value: &planName}, }, }, PriceFilter: &schema.PriceFilter{ @@ -219,181 +69,40 @@ func (r *IsInstance) storageCostComponent(arch ArchType, size int64, count int64 } } -func (r *IsInstance) gpuCostComponent(arch ArchType, gpuType string, gpuCount int64) *schema.CostComponent { - var quantity *decimal.Decimal +func (r *IsInstance) bootVolumeCostComponent() *schema.CostComponent { + var q *decimal.Decimal if r.MonthlyInstanceHours != nil { - quantity = decimalPtr(decimal.NewFromFloat(*r.MonthlyInstanceHours)) - quantity = decimalPtr(quantity.Mul(decimal.NewFromInt(gpuCount))) - } - - var unit string - if arch == s390x { - unit = "V100_HOURS_POWER" - } else { - unit = "V100_HOURS" + q = decimalPtr(decimal.NewFromFloat(float64(r.BootVolume.Size) * (*r.MonthlyInstanceHours))) } return &schema.CostComponent{ - Name: fmt.Sprintf("Gpu hours (%d GPUs, %s, %s)", gpuCount, gpuType, r.Zone), - Unit: "Gpu hours", + Name: fmt.Sprintf("Boot volume (%s, %d GB)", r.BootVolume.Name, r.BootVolume.Size), + Unit: "Hours", UnitMultiplier: decimal.NewFromInt(1), - MonthlyQuantity: quantity, + MonthlyQuantity: q, ProductFilter: &schema.ProductFilter{ - VendorName: strPtr("ibm"), - Region: strPtr(r.Region), - Service: strPtr("is.instance"), + VendorName: strPtr("ibm"), + ProductFamily: strPtr("service"), + Service: strPtr("is.volume"), + Region: strPtr(r.Region), AttributeFilters: []*schema.AttributeFilter{ - {Key: "planName", Value: strPtr(arch.toPlanName())}, + {Key: "planName", ValueRegex: regexPtr(("gen2-volume-general-purpose"))}, }, }, PriceFilter: &schema.PriceFilter{ - Unit: strPtr(unit), + Unit: strPtr("GIGABYTE_HOURS"), }, } } -func (r *IsInstance) memoryCostComponent(arch ArchType, memory int64, multiplier decimal.Decimal) *schema.CostComponent { - var quantity *decimal.Decimal - - if r.MonthlyInstanceHours != nil { - quantity = decimalPtr(decimal.NewFromFloat(*r.MonthlyInstanceHours)) - quantity = decimalPtr(quantity.Mul(decimal.NewFromInt(memory))) - } - - var unit = "MEMORY_HOURS" - - component := &schema.CostComponent{ - Name: fmt.Sprintf("Memory hours (%d GB, %s)", memory, r.Zone), - Unit: "Memory hours", - UnitMultiplier: decimal.NewFromInt(1), - MonthlyQuantity: quantity, - ProductFilter: &schema.ProductFilter{ - VendorName: strPtr("ibm"), - Region: strPtr(r.Region), - Service: strPtr("is.instance"), - AttributeFilters: []*schema.AttributeFilter{ - {Key: "planName", Value: strPtr(arch.toPlanName())}, - }, - }, - PriceFilter: &schema.PriceFilter{ - Unit: strPtr(unit), - }, - } - component.SetCustomPriceMultiplier(decimalPtr(multiplier)) - return component -} - -func (r *IsInstance) bootVolumeCostComponent() []*schema.CostComponent { - costComponents := []*schema.CostComponent{} - for _, volume := range r.BootVolume { - var q *decimal.Decimal - if r.MonthlyInstanceHours != nil { - q = decimalPtr(decimal.NewFromFloat(*r.MonthlyInstanceHours)) - q = decimalPtr(q.Mul(decimal.NewFromInt(volume.Size))) - } - - component := &schema.CostComponent{ - Name: fmt.Sprintf("Boot volume (%s, %d GB)", volume.Name, volume.Size), - Unit: "GB Hours", - UnitMultiplier: decimal.NewFromInt(1), - MonthlyQuantity: q, - ProductFilter: &schema.ProductFilter{ - VendorName: strPtr("ibm"), - ProductFamily: strPtr("service"), - Service: strPtr("is.volume"), - Region: strPtr(r.Region), - AttributeFilters: []*schema.AttributeFilter{ - {Key: "planName", ValueRegex: regexPtr(("gen2-volume-general-purpose"))}, - }, - }, - } - costComponents = append(costComponents, component) - } - return costComponents -} - -func (r *IsInstance) cpuCostComponent(arch ArchType, cpu int64, multiplier decimal.Decimal) *schema.CostComponent { - var quantity *decimal.Decimal - - if r.MonthlyInstanceHours != nil { - quantity = decimalPtr(decimal.NewFromFloat(*r.MonthlyInstanceHours)) - quantity = decimalPtr(quantity.Mul(decimal.NewFromInt(cpu))) - } - - var unit string = "VCPU_HOURS" - - component := &schema.CostComponent{ - Name: fmt.Sprintf("CPU hours (%d CPUs, %s)", cpu, r.Zone), - Unit: "CPU hours", - UnitMultiplier: decimal.NewFromInt(1), - MonthlyQuantity: quantity, - ProductFilter: &schema.ProductFilter{ - VendorName: strPtr("ibm"), - Region: strPtr(r.Region), - Service: strPtr("is.instance"), - AttributeFilters: []*schema.AttributeFilter{ - {Key: "planName", Value: strPtr(arch.toPlanName())}, - }, - }, - PriceFilter: &schema.PriceFilter{ - Unit: strPtr(unit), - }, - } - component.SetCustomPriceMultiplier(decimalPtr(multiplier)) - return component -} - -func (r *IsInstance) onDedicatedHostCostComponent(cores int64, memory int64) *schema.CostComponent { - var quantity *decimal.Decimal - - if r.MonthlyInstanceHours != nil { - quantity = decimalPtr(decimal.NewFromFloat(*r.MonthlyInstanceHours)) - } - costCompoment := &schema.CostComponent{ - Name: fmt.Sprintf("Host Hours (%d vCPUs, %d GB, %s)", cores, memory, r.Zone), - Unit: "hours", - UnitMultiplier: decimal.NewFromInt(1), - MonthlyQuantity: quantity, - ProductFilter: &schema.ProductFilter{ - VendorName: strPtr("ibm"), - Region: strPtr(r.Region), - Service: strPtr("is.instance"), - }, - } - costCompoment.SetCustomPrice(decimalPtr(decimal.NewFromInt(0))) - return costCompoment -} - -// BuildResource builds a schema.Resource from a valid IsInstance struct. +// BuildResource builds a schema.Resource from a valid IsShare struct. // This method is called after the resource is initialised by an IaC provider. // See providers folder for more information. func (r *IsInstance) BuildResource() *schema.Resource { - var costComponents []*schema.CostComponent - - gcProfile, err := getProfileFromCatalog(r.Profile) - multiplier := getProfileMultiplier(r.Profile) - - if err == nil { - // If the VSI instance runs on a dedicated host, the customer is charged for the dedicated host usages - if r.IsDedicated { - costComponents = append(costComponents, r.onDedicatedHostCostComponent(gcProfile.Metadata.Other.Profile.DefaultConfig.CPU, gcProfile.Metadata.Other.Profile.DefaultConfig.RAM)) - } else { - arch := parseArch(gcProfile.Metadata.Other.Profile.DefaultConfig.VcpuArchitecture) - costComponents = append(costComponents, r.cpuCostComponent(arch, gcProfile.Metadata.Other.Profile.DefaultConfig.CPU, multiplier.Cpu)) - costComponents = append(costComponents, r.memoryCostComponent(arch, gcProfile.Metadata.Other.Profile.DefaultConfig.RAM, multiplier.Memory)) - costComponents = append(costComponents, r.bootVolumeCostComponent()...) - if gcProfile.Metadata.Other.Profile.DefaultConfig.GPUModel != "" { - costComponents = append(costComponents, r.gpuCostComponent(arch, gcProfile.Metadata.Other.Profile.DefaultConfig.GPUModel, gcProfile.Metadata.Other.Profile.DefaultConfig.GPUCount)) - } - if gcProfile.Metadata.Other.Profile.DefaultConfig.Disks != nil { - for _, disk := range gcProfile.Metadata.Other.Profile.DefaultConfig.Disks { - costComponents = append(costComponents, r.storageCostComponent(arch, disk.Size, disk.Quantity)) - } - } - } - } else { - log.Warn(err) + costComponents := []*schema.CostComponent{ + r.instanceHoursCostComponent(), + r.bootVolumeCostComponent(), } return &schema.Resource{