Просмотр исходного кода

Update Public Pricing for Load Balancers, Fix Defaults, Logs (#4032)

alexsouthard 8 часов назад
Родитель
Сommit
9873561c72

+ 1 - 1
modules/pricing/basic/default.go

@@ -5,7 +5,7 @@ import (
 	"github.com/opencost/opencost/core/pkg/unit"
 )
 
-const DefaultClusterPricePerHour float64 = 0.0
+const DefaultClusterPricePerHour float64 = 0.10
 
 const DefaultNetworkLocalEgressPricePerGiB float64 = 0.0
 const DefaultNetworkCrossZoneEgressPricePerGiB float64 = 0.01

+ 37 - 2
modules/pricing/public/aws/awspricingsource.go

@@ -33,11 +33,14 @@ func (p *AWSPricingSource) GetPricing() (*pricing.PricingSet, error) {
 	ps := &pricing.PricingSet{
 		NodePricing:             []*pricing.NodePricing{},
 		PersistentVolumePricing: []*pricing.PersistentVolumePricing{},
+		ServicePricing:          []*pricing.ServicePricing{},
 	}
 	skuToNodeKey := make(map[string]nodeKey)
 	seenNodeKeys := make(map[nodeKey]struct{})
 	skuToVolumeKey := make(map[string]volumeKey)
 	seenVolumeKeys := make(map[volumeKey]struct{})
+	skuToLBRegion := make(map[string]string)
+	seenLBRegions := make(map[string]struct{})
 
 	// Regions is used by the spotAPI to know what to query
 	regions := make(map[string]struct{})
@@ -108,6 +111,19 @@ func (p *AWSPricingSource) GetPricing() (*pricing.PricingSet, error) {
 			return
 		}
 
+		// Handle Network Load Balancer pricing
+		if strings.Contains(attr.UsageType, "LoadBalancerUsage") && attr.Operation == "LoadBalancing:Network" {
+			if attr.RegionCode == "" {
+				return
+			}
+			if _, seen := seenLBRegions[attr.RegionCode]; seen {
+				return
+			}
+			seenLBRegions[attr.RegionCode] = struct{}{}
+			skuToLBRegion[product.Sku] = attr.RegionCode
+			return
+		}
+
 		// Handle EBS volumes
 		if strings.Contains(attr.UsageType, "EBS:Volume") {
 			// Extract the volume type from the usage type (e.g., "USE1-EBS:VolumeUsage.gp3" -> "EBS:VolumeUsage.gp3")
@@ -148,11 +164,12 @@ func (p *AWSPricingSource) GetPricing() (*pricing.PricingSet, error) {
 				termCount, len(ps.NodePricing), len(ps.PersistentVolumePricing))
 		}
 
-		// Check if this SKU is for a node or volume we're tracking
+		// Check if this SKU is for a node, volume, or load balancer we're tracking
 		nk, isNode := skuToNodeKey[term.Sku]
 		vk, isVolume := skuToVolumeKey[term.Sku]
+		lbRegion, isLB := skuToLBRegion[term.Sku]
 
-		if !isNode && !isVolume {
+		if !isNode && !isVolume && !isLB {
 			return
 		}
 
@@ -220,6 +237,24 @@ func (p *AWSPricingSource) GetPricing() (*pricing.PricingSet, error) {
 
 			ps.PersistentVolumePricing = append(ps.PersistentVolumePricing, volumePricing)
 		}
+
+		// Handle load balancer pricing
+		if isLB {
+			servicePricing := &pricing.ServicePricing{
+				Properties: pricing.ServicePricingProperties{
+					Provider: cloud.ProviderAWS,
+					Region:   lbRegion,
+				},
+				Prices: pricing.Prices{
+					pricing.ResourceService: pricing.Price{
+						Unit:  unit.Hour,
+						Price: price,
+					},
+				},
+			}
+
+			ps.ServicePricing = append(ps.ServicePricing, servicePricing)
+		}
 	}
 
 	err := QueryEC2PriceList(region, handleProduct, handleTerm)

+ 11 - 2
modules/pricing/public/cmd/main.go

@@ -52,8 +52,8 @@ func run(cmd *cobra.Command, args []string) error {
 		return fmt.Errorf("failed to generate pricing: %w", err)
 	}
 
-	log.Infof("Generated %d node pricing entries and %d volume pricing entries",
-		len(pricingSet.NodePricing), len(pricingSet.PersistentVolumePricing))
+	log.Infof("Generated %d node pricing entries, %d volume pricing entries, %d service pricing entries",
+		len(pricingSet.NodePricing), len(pricingSet.PersistentVolumePricing), len(pricingSet.ServicePricing))
 
 	if compare {
 		return comparePricing(curr, pricingSet)
@@ -71,6 +71,9 @@ func writePricingJSONL(dir string, ps *pricing.PricingSet) error {
 	if err := writeJSONL(dir+"/persistentvolumes.jsonl", ps.PersistentVolumePricing); err != nil {
 		return err
 	}
+	if err := writeJSONL(dir+"/services.jsonl", ps.ServicePricing); err != nil {
+		return err
+	}
 	return nil
 }
 
@@ -141,6 +144,12 @@ func readPricingJSONL(dir string) (*pricing.PricingSet, error) {
 	}
 	ps.PersistentVolumePricing = pvs
 
+	svcs, err := readJSONL[*pricing.ServicePricing](dir + "/services.jsonl")
+	if err != nil {
+		return nil, err
+	}
+	ps.ServicePricing = svcs
+
 	return ps, nil
 }
 

+ 1 - 0
modules/pricing/public/cny/services.jsonl

@@ -0,0 +1 @@
+{"properties":{"provider":"AWS","region":"cn-north-1"},"prices":{"service":{"unit":"hr","price":0.156}}}

+ 9 - 5
modules/pricing/public/generator.go

@@ -82,6 +82,7 @@ func GeneratePricing(currency unit.Currency) (*pricing.PricingSet, error) {
 	combinedSet := &pricing.PricingSet{
 		NodePricing:             []*pricing.NodePricing{},
 		PersistentVolumePricing: []*pricing.PersistentVolumePricing{},
+		ServicePricing:          []*pricing.ServicePricing{},
 	}
 
 	// Fetch AWS pricing
@@ -91,7 +92,8 @@ func GeneratePricing(currency unit.Currency) (*pricing.PricingSet, error) {
 	}
 	combinedSet.NodePricing = append(combinedSet.NodePricing, awsSet.NodePricing...)
 	combinedSet.PersistentVolumePricing = append(combinedSet.PersistentVolumePricing, awsSet.PersistentVolumePricing...)
-	log.Infof("Added %d AWS node pricing entries", len(awsSet.NodePricing))
+	combinedSet.ServicePricing = append(combinedSet.ServicePricing, awsSet.ServicePricing...)
+	log.Infof("Added %d AWS node pricing entries, %d service pricing entries", len(awsSet.NodePricing), len(awsSet.ServicePricing))
 
 	// Fetch Azure pricing
 	azureSet, err := GenerateAzurePricing(currency)
@@ -100,7 +102,8 @@ func GeneratePricing(currency unit.Currency) (*pricing.PricingSet, error) {
 	}
 	combinedSet.NodePricing = append(combinedSet.NodePricing, azureSet.NodePricing...)
 	combinedSet.PersistentVolumePricing = append(combinedSet.PersistentVolumePricing, azureSet.PersistentVolumePricing...)
-	log.Infof("Added %d Azure node pricing entries", len(azureSet.NodePricing))
+	combinedSet.ServicePricing = append(combinedSet.ServicePricing, azureSet.ServicePricing...)
+	log.Infof("Added %d Azure node pricing entries, %d service pricing entries", len(azureSet.NodePricing), len(azureSet.ServicePricing))
 
 	// GCP does NOT support CNY
 	if currency != "CNY" {
@@ -110,14 +113,15 @@ func GeneratePricing(currency unit.Currency) (*pricing.PricingSet, error) {
 		}
 		combinedSet.NodePricing = append(combinedSet.NodePricing, gcpSet.NodePricing...)
 		combinedSet.PersistentVolumePricing = append(combinedSet.PersistentVolumePricing, gcpSet.PersistentVolumePricing...)
-		log.Infof("Added %d GCP node pricing entries", len(gcpSet.NodePricing))
+		combinedSet.ServicePricing = append(combinedSet.ServicePricing, gcpSet.ServicePricing...)
+		log.Infof("Added %d GCP node pricing entries, %d service pricing entries", len(gcpSet.NodePricing), len(gcpSet.ServicePricing))
 	}
 
 	// Sort the combined set to ensure deterministic output
 	combinedSet.Sort()
 
-	log.Infof("Generated combined pricing set with %d total node entries and %d volume entries",
-		len(combinedSet.NodePricing), len(combinedSet.PersistentVolumePricing))
+	log.Infof("Generated combined pricing set with %d total node entries, %d volume entries, %d service entries",
+		len(combinedSet.NodePricing), len(combinedSet.PersistentVolumePricing), len(combinedSet.ServicePricing))
 
 	return combinedSet, nil
 }

+ 28 - 3
modules/pricing/public/usd/module.go

@@ -40,6 +40,14 @@ func (pm *PricingModule) newPVReader() (reader.Reader[*pricing.PersistentVolumeP
 	return reader.NewJSONLinesReader[*pricing.PersistentVolumePricing](f), nil
 }
 
+func (pm *PricingModule) newServiceReader() (reader.Reader[*pricing.ServicePricing], error) {
+	f, err := embeddedFS.Open("services.jsonl")
+	if err != nil {
+		return nil, fmt.Errorf("opening embedded services.jsonl: %w", err)
+	}
+	return reader.NewJSONLinesReader[*pricing.ServicePricing](f), nil
+}
+
 func (pm *PricingModule) NewNodePricingReader(ctx context.Context) (reader.Reader[*pricing.NodePricing], error) {
 	return pm.newNodeReader()
 }
@@ -49,15 +57,15 @@ func (pm *PricingModule) NewPersistentVolumePricingReader(ctx context.Context) (
 }
 
 func (pm *PricingModule) NewClusterPricingReader(ctx context.Context) (reader.Reader[*pricing.ClusterPricing], error) {
-	return nil, fmt.Errorf("cluster pricing not yet implemented")
+	return nil, fmt.Errorf("cluster pricing not provided by public pricing module")
 }
 
 func (pm *PricingModule) NewNetworkPricingReader(ctx context.Context) (reader.Reader[*pricing.NetworkPricing], error) {
-	return nil, fmt.Errorf("network pricing not yet implemented")
+	return nil, fmt.Errorf("network pricing not provided by public pricing module")
 }
 
 func (pm *PricingModule) NewServicePricingReader(ctx context.Context) (reader.Reader[*pricing.ServicePricing], error) {
-	return nil, fmt.Errorf("service pricing not yet implemented")
+	return pm.newServiceReader()
 }
 
 func (pm *PricingModule) GetPricingSet(ctx context.Context) (*pricing.PricingSet, error) {
@@ -97,6 +105,23 @@ func (pm *PricingModule) GetPricingSet(ctx context.Context) (*pricing.PricingSet
 		}
 	}
 
+	svcReader, err := pm.newServiceReader()
+	if err != nil {
+		return nil, err
+	}
+	defer svcReader.Close()
+	svcDst := make([]*pricing.ServicePricing, 64)
+	for {
+		n, err := svcReader.Read(ctx, svcDst)
+		ps.ServicePricing = append(ps.ServicePricing, svcDst[:n]...)
+		if err == io.EOF {
+			break
+		}
+		if err != nil {
+			return nil, err
+		}
+	}
+
 	return ps, nil
 }
 

Разница между файлами не показана из-за своего большого размера
+ 406 - 406
modules/pricing/public/usd/nodes.jsonl


+ 23 - 0
modules/pricing/public/usd/services.jsonl

@@ -0,0 +1,23 @@
+{"properties":{"provider":"AWS","region":"af-south-1"},"prices":{"service":{"unit":"hr","price":0.029988}}}
+{"properties":{"provider":"AWS","region":"ap-east-1"},"prices":{"service":{"unit":"hr","price":0.0277}}}
+{"properties":{"provider":"AWS","region":"ap-northeast-1"},"prices":{"service":{"unit":"hr","price":0.0243}}}
+{"properties":{"provider":"AWS","region":"ap-northeast-2"},"prices":{"service":{"unit":"hr","price":0.0225}}}
+{"properties":{"provider":"AWS","region":"ap-northeast-3"},"prices":{"service":{"unit":"hr","price":0.0243}}}
+{"properties":{"provider":"AWS","region":"ap-south-1"},"prices":{"service":{"unit":"hr","price":0.0239}}}
+{"properties":{"provider":"AWS","region":"ap-southeast-1"},"prices":{"service":{"unit":"hr","price":0.0252}}}
+{"properties":{"provider":"AWS","region":"ap-southeast-2"},"prices":{"service":{"unit":"hr","price":0.0252}}}
+{"properties":{"provider":"AWS","region":"ca-central-1"},"prices":{"service":{"unit":"hr","price":0.02475}}}
+{"properties":{"provider":"AWS","region":"eu-central-1"},"prices":{"service":{"unit":"hr","price":0.027}}}
+{"properties":{"provider":"AWS","region":"eu-north-1"},"prices":{"service":{"unit":"hr","price":0.02394}}}
+{"properties":{"provider":"AWS","region":"eu-south-1"},"prices":{"service":{"unit":"hr","price":0.02646}}}
+{"properties":{"provider":"AWS","region":"eu-west-1"},"prices":{"service":{"unit":"hr","price":0.0252}}}
+{"properties":{"provider":"AWS","region":"eu-west-2"},"prices":{"service":{"unit":"hr","price":0.02646}}}
+{"properties":{"provider":"AWS","region":"eu-west-3"},"prices":{"service":{"unit":"hr","price":0.02646}}}
+{"properties":{"provider":"AWS","region":"me-south-1"},"prices":{"service":{"unit":"hr","price":0.02772}}}
+{"properties":{"provider":"AWS","region":"sa-east-1"},"prices":{"service":{"unit":"hr","price":0.034}}}
+{"properties":{"provider":"AWS","region":"us-east-1"},"prices":{"service":{"unit":"hr","price":0.0225}}}
+{"properties":{"provider":"AWS","region":"us-east-2"},"prices":{"service":{"unit":"hr","price":0.0225}}}
+{"properties":{"provider":"AWS","region":"us-gov-east-1"},"prices":{"service":{"unit":"hr","price":0.032}}}
+{"properties":{"provider":"AWS","region":"us-gov-west-1"},"prices":{"service":{"unit":"hr","price":0.032}}}
+{"properties":{"provider":"AWS","region":"us-west-1"},"prices":{"service":{"unit":"hr","price":0.0252}}}
+{"properties":{"provider":"AWS","region":"us-west-2"},"prices":{"service":{"unit":"hr","price":0.0225}}}

Некоторые файлы не были показаны из-за большого количества измененных файлов