Skip to content

Commit

Permalink
Add support for Watson Discovery resource (#164)
Browse files Browse the repository at this point in the history
* Add new resource instance variables for Watson Discovery

* Add new file & parent Cost Component func

* Add Documents cost component

* Add queries and models cost components

* Add collections cost component

* Add usage for Watson Discovery units

* Add Terraform resources for plus and enterprise plans

* Add test usage values for Watson Discovery

* Adjust calculations to match global catalog

* Update usage units to be minimum required

* Update test golden file with Watson Discovery results
  • Loading branch information
luisarojas authored Apr 22, 2024
1 parent 2596c0e commit 752e0b8
Show file tree
Hide file tree
Showing 6 changed files with 331 additions and 4 deletions.
17 changes: 17 additions & 0 deletions infracost-usage-example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ resource_type_default_usage:
wa_instance: 1
wa_monthly_active_users: 1001
wa_voice_users: 101
wd_instance: 1
wd_documents: 11000
wd_queries: 11000
wd_custom_models: 4
wd_collections: 301
ibm_tg_gateway:
connection: 3
data_transfer_global: 1000
Expand Down Expand Up @@ -1303,6 +1308,18 @@ resource_usage:
wa_monthly_active_users: 1100 # The number of monthly active users
wa_monthly_voice_users: 100 # The number of monthly active voice users

ibm_resource_instance.watson_discovery_plus:
wd_instance: 1 # Number of instances used per month
wd_documents: 11000 # Number of monthly documents created; 10,000 included in the Plus plan; $50 for every additional 1,000 documents.
wd_queries: 11000 # Number of queries documents created; 10,000 included in the Plus plan; $20 for every additional 1,000 queries.

ibm_resource_instance.watson_discovery_enterprise:
wd_instance: 1 # Number of instances used per month
wd_documents: 101000 # Number of monthly documents created; 100,000 included in the Enterprise plan; $5 for every additional 1,000 documents.
wd_queries: 101000 # Number of queries documents created; 100,000 included in the Enterprise plan; $5 for every additional 1,000 queries.
wd_custom_models: 4 # Number of monthly custom models created; 3 included in the Enterprise plan; $500 for every additional custom model.
wd_collections: 301 # Number of monthly collections created; 300 included in the Enterprise plan. $500 for every additional 100 collections.

ibm_tg_gateway.tg_gateway:
connection: 25 # Monthly number of connections to the gateway
data_transfer_local: 2500 # Monthly local traffic through the gateway in GB
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,18 @@
ibm_resource_instance.wa_instance_trial
└─ Trial 1 $0.00

ibm_resource_instance.watson_discovery_enterprise
├─ Instance 1 Instance $5,000.00
├─ Additional Monthly Documents 1,000 Documents $5.00
├─ Additional Monthly Queries 1,000 Queries $5.00
├─ Additional Monthly Custom Models 1 Custom Models $500.00
└─ Additional Monthly Collections 1 Hundred Collections $500.00

ibm_resource_instance.watson_discovery_plus
├─ Instance 1 Instance $500.00
├─ Additional Monthly Documents 1,000 Documents $50.00
└─ Additional Monthly Queries 1,000 Queries $20.00

ibm_resource_instance.wml_instance_essentials
├─ Capacity Unit-Hours 20 CUH $10.40
├─ Class 1 Resource Units 50 RU $0.03
Expand All @@ -105,7 +117,7 @@
├─ Class 2 Resource Units 50 RU $0.09
└─ Class 3 Resource Units 50 RU $0.25

OVERALL TOTAL $8,880.14
OVERALL TOTAL $15,460.14
──────────────────────────────────
23 cloud resources were detected:
23 were estimated, all of which include usage-based costs, see https://infracost.io/usage-file
25 cloud resources were detected:
25 were estimated, all of which include usage-based costs, see https://infracost.io/usage-file
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,19 @@ resource "ibm_resource_instance" "wa_instance_enterprise" {
location = "us-south"
resource_group_id = "default"
}

resource "ibm_resource_instance" "watson_discovery_plus" {
name = "wd_plus"
service = "discovery"
plan = "plus"
location = "us-south"
resource_group_id = "default"
}

resource "ibm_resource_instance" "watson_discovery_enterprise" {
name = "wd_enterprise"
service = "discovery"
plan = "enterprise"
location = "us-south"
resource_group_id = "default"
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,16 @@ resource_usage:
ibm_resource_instance.wa_instance_enterprise:
wa_instance: 1
wa_monthly_active_users: 51000
wa_voice_users: 1000
wa_voice_users: 1000

ibm_resource_instance.watson_discovery_plus:
wd_instance: 1 # Number of instances used per month
wd_documents: 11000 # Number of monthly documents created; 10,000 included in the Plus plan; $50 for every additional 1,000 documents.
wd_queries: 11000 # Number of queries documents created; 10,000 included in the Plus plan; $20 for every additional 1,000 queries.

ibm_resource_instance.watson_discovery_enterprise:
wd_instance: 1 # Number of instances used per month
wd_documents: 101000 # Number of monthly documents created; 100,000 included in the Enterprise plan; $5 for every additional 1,000 documents.
wd_queries: 101000 # Number of queries documents created; 100,000 included in the Enterprise plan; $5 for every additional 1,000 queries.
wd_custom_models: 4 # Number of monthly custom models created; 3 included in the Enterprise plan; $500 for every additional custom model.
wd_collections: 301 # Number of monthly collections created; 300 included in the Enterprise plan. $500 for every additional 100 collections.
12 changes: 12 additions & 0 deletions internal/resources/ibm/resource_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ type ResourceInstance struct {
WA_Instance *float64 `infracost_usage:"wa_instance"`
WA_mau *float64 `infracost_usage:"wa_monthly_active_users"`
WA_vu *float64 `infracost_usage:"wa_voice_users"`
// Watson Discovery
WD_Instance *float64 `infracost_usage:"wd_instance"`
WD_Documents *float64 `infracost_usage:"wd_documents"`
WD_Queries *float64 `infracost_usage:"wd_queries"`
WD_CustomModels *float64 `infracost_usage:"wd_custom_models"`
WD_Collections *float64 `infracost_usage:"wd_collections"`
}

type ResourceCostComponentsFunc func(*ResourceInstance) []*schema.CostComponent
Expand Down Expand Up @@ -105,6 +111,11 @@ var ResourceInstanceUsageSchema = []*schema.UsageItem{
{Key: "wa_instance", DefaultValue: 0, ValueType: schema.Float64},
{Key: "wa_monthly_active_users", DefaultValue: 0, ValueType: schema.Float64},
{Key: "wa_voice_users", DefaultValue: 0, ValueType: schema.Float64},
{Key: "wd_instance", DefaultValue: 0, ValueType: schema.Float64},
{Key: "wd_documents", DefaultValue: 0, ValueType: schema.Float64},
{Key: "wd_queries", DefaultValue: 0, ValueType: schema.Float64},
{Key: "wd_custom_models", DefaultValue: 0, ValueType: schema.Float64},
{Key: "wd_collections", DefaultValue: 0, ValueType: schema.Float64},
}

var ResourceInstanceCostMap map[string]ResourceCostComponentsFunc = map[string]ResourceCostComponentsFunc{
Expand All @@ -119,6 +130,7 @@ var ResourceInstanceCostMap map[string]ResourceCostComponentsFunc = map[string]R
"continuous-delivery": GetContinuousDeliveryCostComponenets,
"pm-20": GetWMLCostComponents,
"conversation": GetWACostComponents,
"discovery": GetWDCostComponents,
}

func KMSKeyVersionsFreeCostComponent(r *ResourceInstance) *schema.CostComponent {
Expand Down
258 changes: 258 additions & 0 deletions internal/resources/ibm/resource_instance_discovery.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
package ibm

import (
"fmt"
"math"

"github.com/infracost/infracost/internal/schema"
"github.com/shopspring/decimal"
)

const ENTERPRISE_PLAN_PROGRAMMATIC_NAME string = "enterprise"
const PLUS_PLAN_PROGRAMMATIC_NAME string = "plus"

/*
* Plus = 'plus'
* Enterprise = 'enterprise'
* Premium = 'premium' (not applicable, need to call for pricing)
*/
func GetWDCostComponents(r *ResourceInstance) []*schema.CostComponent {
if r.Plan == PLUS_PLAN_PROGRAMMATIC_NAME {
return []*schema.CostComponent{
WDInstanceCostComponent(r),
WDMonthlyDocumentsCostComponent(r),
WDMonthlyQueriesCostComponent(r),
}
} else if r.Plan == ENTERPRISE_PLAN_PROGRAMMATIC_NAME {
return []*schema.CostComponent{
WDInstanceCostComponent(r),
WDMonthlyDocumentsCostComponent(r),
WDMonthlyQueriesCostComponent(r),
WDMonthlyCustomModelsCostComponent(r),
WDMonthlyCollectionsCostComponent(r),
}
} else {
costComponent := schema.CostComponent{
Name: fmt.Sprintf("Plan %s with customized pricing", r.Plan),
UnitMultiplier: decimal.NewFromInt(1), // Final quantity for this cost component will be divided by this amount
MonthlyQuantity: decimalPtr(decimal.NewFromInt(1)),
}
costComponent.SetCustomPrice(decimalPtr(decimal.NewFromInt(0)))
return []*schema.CostComponent{
&costComponent,
}
}
}

/*
* Instance
* - Plus: $USD/instance/month
* - Enterprise: $USD/instance/month
*/
func WDInstanceCostComponent(r *ResourceInstance) *schema.CostComponent {

var instances_unit_name string
var quantity *decimal.Decimal

if r.Plan == PLUS_PLAN_PROGRAMMATIC_NAME {
instances_unit_name = "PLUS_SERVICE_INSTANCES_PER_MONTH"
} else {
instances_unit_name = "ENTERPRISE_SERVICE_INSTANCES_PER_MONTH"
}

if r.WD_Instance != nil {
quantity = decimalPtr(decimal.NewFromFloat(*r.WD_Instance))
} else {
quantity = decimalPtr(decimal.NewFromInt(1))
}

return &schema.CostComponent{
Name: "Instance",
Unit: "Instance",
UnitMultiplier: decimal.NewFromInt(1), // Final quantity for this cost component will be divided by this amount
MonthlyQuantity: quantity,
ProductFilter: &schema.ProductFilter{
VendorName: strPtr("ibm"),
Region: strPtr(r.Location),
Service: &r.Service,
AttributeFilters: []*schema.AttributeFilter{
{Key: "planName", Value: &r.Plan},
},
},
PriceFilter: &schema.PriceFilter{
Unit: strPtr(instances_unit_name),
},
}
}

/*
* Documents:
* - Plus: $USD/documents/month. Includes 10,000 documents per month; $USD for every additional 1,000 documents.
* - Enterprise: $USD/documents/month. Includes 100,000 documents per month; $USD for every additional 1,000 documents.
*/
func WDMonthlyDocumentsCostComponent(r *ResourceInstance) *schema.CostComponent {

var documents_included int // Base number of documents that are included with an instance and do not have a cost
var documents_unit_name string // Unit to display
var quantity *decimal.Decimal // Quantity of current cost component (e.g. number of additional 1000 "blocks" over the base number of documents included)

if r.Plan == PLUS_PLAN_PROGRAMMATIC_NAME {
documents_unit_name = "PLUS_DOCUMENTS_TOTAL"
documents_included = 10000
} else {
documents_unit_name = "ENTERPRISE_DOCUMENTS_TOTAL"
documents_included = 100000
}

if r.WD_Documents != nil {

additional_documents := *r.WD_Documents - float64(documents_included)

if additional_documents > 0 {
quantity = decimalPtr(decimal.NewFromFloat(additional_documents))
}
} else {
quantity = decimalPtr(decimal.NewFromInt(0))
}

return &schema.CostComponent{
Name: "Additional Monthly Documents",
Unit: "Documents",
UnitMultiplier: decimal.NewFromFloat(1), // Final quantity for this cost component will be divided by this amount
MonthlyQuantity: quantity,
ProductFilter: &schema.ProductFilter{
VendorName: strPtr("ibm"),
Region: strPtr(r.Location),
Service: &r.Service,
AttributeFilters: []*schema.AttributeFilter{
{Key: "planName", Value: &r.Plan},
},
},
PriceFilter: &schema.PriceFilter{
Unit: strPtr(documents_unit_name),
},
}
}

/*
* Queries:
* - Plus: $USD/queries/month. Includes 10,000 queries per month; $USD for every additional 1,000 queries.
* - Enterprise: $USD/queries/month. Includes 100,000 queries per month; $USD for every additional 1,000 queries.
*/
func WDMonthlyQueriesCostComponent(r *ResourceInstance) *schema.CostComponent {

var quantity *decimal.Decimal
var queries_included int // Base number of queries that are included with an instance and do not have a cost

if r.Plan == PLUS_PLAN_PROGRAMMATIC_NAME {
queries_included = 10000
} else {
queries_included = 100000
}

if r.WD_Queries != nil {

additional_queries := *r.WD_Queries - float64(queries_included)

if additional_queries > 0 {
quantity = decimalPtr(decimal.NewFromFloat(additional_queries))
}
} else {
quantity = decimalPtr(decimal.NewFromInt(0))
}

return &schema.CostComponent{
Name: "Additional Monthly Queries",
Unit: "Queries",
UnitMultiplier: decimal.NewFromInt(1), // Final quantity for this cost component will be divided by this amount
MonthlyQuantity: quantity,
ProductFilter: &schema.ProductFilter{
VendorName: strPtr("ibm"),
Region: strPtr(r.Location),
Service: &r.Service,
AttributeFilters: []*schema.AttributeFilter{
{Key: "planName", Value: &r.Plan},
},
},
PriceFilter: &schema.PriceFilter{
Unit: strPtr("GRADUATED_PRICE_QUERIES_PER_MONTH"),
},
}
}

/*
* Custom Models:
* - Enterprise: $USD/additional custom models/month. Includes 3 custom models per month.
*/
func WDMonthlyCustomModelsCostComponent(r *ResourceInstance) *schema.CostComponent {

var quantity *decimal.Decimal
var custom_models_included int = 3 // Base number of custom models that are included with an instance and do not have a cost

if r.WD_CustomModels != nil {
// Determine number of custom models that go over the base number of custom models included
quantity = decimalPtr(decimal.NewFromFloat(*r.WD_CustomModels - float64(custom_models_included)))
} else {
quantity = decimalPtr(decimal.NewFromInt(0))
}

return &schema.CostComponent{
Name: "Additional Monthly Custom Models",
Unit: "Custom Models",
UnitMultiplier: decimal.NewFromInt(1), // Final quantity for this cost component will be divided by this amount
MonthlyQuantity: quantity,
ProductFilter: &schema.ProductFilter{
VendorName: strPtr("ibm"),
Region: strPtr(r.Location),
Service: &r.Service,
AttributeFilters: []*schema.AttributeFilter{
{Key: "planName", Value: &r.Plan},
},
},
PriceFilter: &schema.PriceFilter{
Unit: strPtr("CUSTOM_MODELS_PER_MONTH"),
},
}
}

/*
* Collections
* - Enterprise: $USD/additional collections/month. Includes 300 collections per month; $USD for every additional 100 collections.
*/
func WDMonthlyCollectionsCostComponent(r *ResourceInstance) *schema.CostComponent {

var quantity *decimal.Decimal
var collections_additional_range int = 100 // Additional cost for every 100 over the included amount of collections
var collections_included int = 300 // Base number of collections that are included with an instance and do not have a cost

if r.WD_Collections != nil {

additional_collections := *r.WD_Collections - float64(collections_included)

if additional_collections > 0 {
// Determine number of 100 "blocks" of collections go over the base number of collections included
quantity = decimalPtr(decimal.NewFromFloat(math.Ceil(additional_collections / float64(collections_additional_range))))
}

} else {
quantity = decimalPtr(decimal.NewFromInt(0))
}

return &schema.CostComponent{
Name: "Additional Monthly Collections",
Unit: "Hundred Collections",
UnitMultiplier: decimal.NewFromFloat(1), // Final quantity for this cost component will be divided by this amount
MonthlyQuantity: quantity,
ProductFilter: &schema.ProductFilter{
VendorName: strPtr("ibm"),
Region: strPtr(r.Location),
Service: &r.Service,
AttributeFilters: []*schema.AttributeFilter{
{Key: "planName", Value: &r.Plan},
},
},
PriceFilter: &schema.PriceFilter{
Unit: strPtr("ENTERPRISE_COLLECTIONS_TOTAL"),
},
}
}

0 comments on commit 752e0b8

Please sign in to comment.