Explorar o código

updated gcp to read custom load balancer costs like network costs are read

Calvin Wang %!s(int64=5) %!d(string=hai) anos
pai
achega
8ab5c824c7
Modificáronse 4 ficheiros con 92 adicións e 76 borrados
  1. 31 0
      pkg/cloud/gcpprovider.go
  2. 57 53
      pkg/cloud/provider.go
  3. 2 21
      pkg/costmodel/costmodel.go
  4. 2 2
      pkg/costmodel/router.go

+ 31 - 0
pkg/cloud/gcpprovider.go

@@ -1048,6 +1048,37 @@ func (gcp *GCP) NetworkPricing() (*Network, error) {
 	}, nil
 }
 
+func (gcp *GCP) LoadBalancerPricing() (*LoadBalancer, error) {
+	cpricing, err := gcp.Config.GetCustomPricingData()
+	if err != nil {
+		return nil, err
+	}
+	fffrc, err := strconv.ParseFloat(cpricing.FirstFiveForwardingRulesCost, 64)
+	if err != nil {
+		return nil, err
+	}
+	afrc, err := strconv.ParseFloat(cpricing.AdditionalForwardingRuleCost, 64)
+	if err != nil {
+		return nil, err
+	}
+	lbidc, err := strconv.ParseFloat(cpricing.LBIngressDataCost, 64)
+	if err != nil {
+		return nil, err
+	}
+	var totalCost float64
+	numForwardingRules := 1.0 // hard-code at 1 for now
+	dataIngressGB := 0.0      // hard-code at 0 for now
+
+	if numForwardingRules < 5 {
+		totalCost = fffrc*numForwardingRules + lbidc*dataIngressGB
+	} else {
+		totalCost = fffrc*5 + afrc*(numForwardingRules-5) + lbidc*dataIngressGB
+	}
+	return &LoadBalancer{
+		Cost: totalCost,
+	}, nil
+}
+
 const (
 	GCPReservedInstanceResourceTypeRAM string = "MEMORY"
 	GCPReservedInstanceResourceTypeCPU string = "VCPU"

+ 57 - 53
pkg/cloud/provider.go

@@ -73,15 +73,15 @@ func (n *Node) IsSpot() bool {
 // LoadBalancer is the interface by which the provider and cost model communicate LoadBalancer prices.
 // The provider will best-effort try to fill out this struct.
 type LoadBalancer struct {
-	IngressIPAddresses           []string `json:"IngressIPAddresses"`
-	Cost                         float64  `json:"hourlyCost"` // TODO: find out if cloud providers return these values as strings or floats
-	FirstFiveForwardingRulesCost float64  `json:"firstFiveForwardingRulesCost"`
-	AdditionalForwardingRuleCost float64  `json:"additionalForwardingRuleCost"`
-	IngressDataCostPerGB         float64  `json:"ingressDataCostPerGB"`
-	// TODO: work in progress. Currently designed for GCP, unsure if other cloud providers price differently (e.g., if Azure prices a flat rate per rule)
-	// TODO: potentially need to make an additional struct to marshal GCP ALB data
+	IngressIPAddresses []string `json:"IngressIPAddresses"`
+	Cost               float64  `json:"hourlyCost"`
 }
 
+// TODO: used for dynamic cloud provider price fetching.
+// determine what identifies a load balancer in the json returned from the cloud provider pricing API call
+// type LBKey interface {
+// }
+
 // Network is the interface by which the provider and cost model communicate network egress prices.
 // The provider will best-effort try to fill out this struct.
 type Network struct {
@@ -123,51 +123,54 @@ type OutOfClusterAllocation struct {
 }
 
 type CustomPricing struct {
-	Provider              string            `json:"provider"`
-	Description           string            `json:"description"`
-	CPU                   string            `json:"CPU"`
-	SpotCPU               string            `json:"spotCPU"`
-	RAM                   string            `json:"RAM"`
-	SpotRAM               string            `json:"spotRAM"`
-	GPU                   string            `json:"GPU"`
-	SpotGPU               string            `json:"spotGPU"`
-	Storage               string            `json:"storage"`
-	ZoneNetworkEgress     string            `json:"zoneNetworkEgress"`
-	RegionNetworkEgress   string            `json:"regionNetworkEgress"`
-	InternetNetworkEgress string            `json:"internetNetworkEgress"`
-	SpotLabel             string            `json:"spotLabel,omitempty"`
-	SpotLabelValue        string            `json:"spotLabelValue,omitempty"`
-	GpuLabel              string            `json:"gpuLabel,omitempty"`
-	GpuLabelValue         string            `json:"gpuLabelValue,omitempty"`
-	ServiceKeyName        string            `json:"awsServiceKeyName,omitempty"`
-	ServiceKeySecret      string            `json:"awsServiceKeySecret,omitempty"`
-	SpotDataRegion        string            `json:"awsSpotDataRegion,omitempty"`
-	SpotDataBucket        string            `json:"awsSpotDataBucket,omitempty"`
-	SpotDataPrefix        string            `json:"awsSpotDataPrefix,omitempty"`
-	ProjectID             string            `json:"projectID,omitempty"`
-	AthenaProjectID       string            `json:"athenaProjectID,omitempty"`
-	AthenaBucketName      string            `json:"athenaBucketName"`
-	AthenaRegion          string            `json:"athenaRegion"`
-	AthenaDatabase        string            `json:"athenaDatabase"`
-	AthenaTable           string            `json:"athenaTable"`
-	MasterPayerARN        string            `json:"masterPayerARN"`
-	BillingDataDataset    string            `json:"billingDataDataset,omitempty"`
-	CustomPricesEnabled   string            `json:"customPricesEnabled"`
-	DefaultIdle           string            `json:"defaultIdle"`
-	AzureSubscriptionID   string            `json:"azureSubscriptionID"`
-	AzureClientID         string            `json:"azureClientID"`
-	AzureClientSecret     string            `json:"azureClientSecret"`
-	AzureTenantID         string            `json:"azureTenantID"`
-	AzureBillingRegion    string            `json:"azureBillingRegion"`
-	CurrencyCode          string            `json:"currencyCode"`
-	Discount              string            `json:"discount"`
-	NegotiatedDiscount    string            `json:"negotiatedDiscount"`
-	SharedCosts           map[string]string `json:"sharedCost"`
-	ClusterName           string            `json:"clusterName"`
-	SharedNamespaces      string            `json:"sharedNamespaces"`
-	SharedLabelNames      string            `json:"sharedLabelNames"`
-	SharedLabelValues     string            `json:"sharedLabelValues"`
-	ReadOnly              string            `json:"readOnly"`
+	Provider                     string            `json:"provider"`
+	Description                  string            `json:"description"`
+	CPU                          string            `json:"CPU"`
+	SpotCPU                      string            `json:"spotCPU"`
+	RAM                          string            `json:"RAM"`
+	SpotRAM                      string            `json:"spotRAM"`
+	GPU                          string            `json:"GPU"`
+	SpotGPU                      string            `json:"spotGPU"`
+	Storage                      string            `json:"storage"`
+	ZoneNetworkEgress            string            `json:"zoneNetworkEgress"`
+	RegionNetworkEgress          string            `json:"regionNetworkEgress"`
+	InternetNetworkEgress        string            `json:"internetNetworkEgress"`
+	FirstFiveForwardingRulesCost string            `json:"firstFiveForwardingRulesCost"`
+	AdditionalForwardingRuleCost string            `json:"additionalForwardingRuleCost"`
+	LBIngressDataCost            string            `json:"LBIngressDataCost"`
+	SpotLabel                    string            `json:"spotLabel,omitempty"`
+	SpotLabelValue               string            `json:"spotLabelValue,omitempty"`
+	GpuLabel                     string            `json:"gpuLabel,omitempty"`
+	GpuLabelValue                string            `json:"gpuLabelValue,omitempty"`
+	ServiceKeyName               string            `json:"awsServiceKeyName,omitempty"`
+	ServiceKeySecret             string            `json:"awsServiceKeySecret,omitempty"`
+	SpotDataRegion               string            `json:"awsSpotDataRegion,omitempty"`
+	SpotDataBucket               string            `json:"awsSpotDataBucket,omitempty"`
+	SpotDataPrefix               string            `json:"awsSpotDataPrefix,omitempty"`
+	ProjectID                    string            `json:"projectID,omitempty"`
+	AthenaProjectID              string            `json:"athenaProjectID,omitempty"`
+	AthenaBucketName             string            `json:"athenaBucketName"`
+	AthenaRegion                 string            `json:"athenaRegion"`
+	AthenaDatabase               string            `json:"athenaDatabase"`
+	AthenaTable                  string            `json:"athenaTable"`
+	MasterPayerARN               string            `json:"masterPayerARN"`
+	BillingDataDataset           string            `json:"billingDataDataset,omitempty"`
+	CustomPricesEnabled          string            `json:"customPricesEnabled"`
+	DefaultIdle                  string            `json:"defaultIdle"`
+	AzureSubscriptionID          string            `json:"azureSubscriptionID"`
+	AzureClientID                string            `json:"azureClientID"`
+	AzureClientSecret            string            `json:"azureClientSecret"`
+	AzureTenantID                string            `json:"azureTenantID"`
+	AzureBillingRegion           string            `json:"azureBillingRegion"`
+	CurrencyCode                 string            `json:"currencyCode"`
+	Discount                     string            `json:"discount"`
+	NegotiatedDiscount           string            `json:"negotiatedDiscount"`
+	SharedCosts                  map[string]string `json:"sharedCost"`
+	ClusterName                  string            `json:"clusterName"`
+	SharedNamespaces             string            `json:"sharedNamespaces"`
+	SharedLabelNames             string            `json:"sharedLabelNames"`
+	SharedLabelValues            string            `json:"sharedLabelValues"`
+	ReadOnly                     string            `json:"readOnly"`
 }
 
 type ServiceAccountStatus struct {
@@ -187,7 +190,8 @@ type Provider interface {
 	GetDisks() ([]byte, error)
 	NodePricing(Key) (*Node, error)
 	PVPricing(PVKey) (*PV, error)
-	NetworkPricing() (*Network, error)
+	NetworkPricing() (*Network, error)           // TODO: add key interface arg for dynamic price fetching
+	LoadBalancerPricing() (*LoadBalancer, error) // TODO: add key interface arg for dynamic price fetching
 	AllNodePricing() (interface{}, error)
 	DownloadPricingData() error
 	GetKey(map[string]string, *v1.Node) Key

+ 2 - 21
pkg/costmodel/costmodel.go

@@ -355,13 +355,6 @@ func (cm *CostModel) ComputeCostData(cli prometheusClient.Client, clientset kube
 		return nil, err
 	}
 
-	// TODO: is this necessary?
-	// loadBalancers, err := cm.GetLBCost(cp)
-	// if err != nil {
-	// 	log.Warningf("GetLBCost: no load balancer cost model available: " + err.Error())
-	// 	return nil, err
-	// }
-
 	// Unmounted PVs represent the PVs that are not mounted or tied to a volume on a container
 	unmountedPVs := make(map[string][]*PersistentVolumeClaimData)
 	pvClaimMapping, err := GetPVInfo(resPVRequests, clusterID)
@@ -1201,31 +1194,19 @@ func (cm *CostModel) GetLBCost(cp costAnalyzerCloud.Provider) (map[string]*costA
 	servicesList := cm.Cache.GetAllServices()
 	loadBalancerMap := make(map[string]*costAnalyzerCloud.LoadBalancer)
 
-	// 1. need to check whether the service is a loadbalancer /
-	// 2. need to generate a unique key for this loadbalancer --> use name, which is unique across a namespace
-	// 3. need to check if key exists in servicesList and then populate loadBalancers with it
-
 	for _, service := range servicesList {
 		namespace := service.GetObjectMeta().GetNamespace()
 		name := service.GetObjectMeta().GetName()
-		// TODO: add clusterID to key? possible if called in ComputeCostData(), but what about metrics.go?
-		key := namespace + "," + name
+		key := namespace + "," + name // + "," + clusterID?
 
-		// Does this identify ELB vs. ILB? Need to test the /api/allServices call with an ALB. Current work is on ELBs.
 		if service.Spec.Type == "LoadBalancer" {
-			// TODO: dynamically fetch based on cloud provider and region. Currently using hard-coded GCP us-central1 values.
-			loadBalancer := &costAnalyzerCloud.LoadBalancer{
-				FirstFiveForwardingRulesCost: 0.025,
-				AdditionalForwardingRuleCost: 0.010,
-				IngressDataCostPerGB:         0.008,
-			}
+			loadBalancer := cp.LoadBalancerPricing()
 			newLoadBalancer := *loadBalancer
 			if len(service.Status.LoadBalancer.Ingress) > 0 { // should actually check if LoadBalancer.Ingress exists
 				for _, loadBalancerIngress := range service.Status.LoadBalancer.Ingress {
 					newLoadBalancer.IngressIPAddresses = append(newLoadBalancer.IngressIPAddresses, loadBalancerIngress.IP)
 				}
 			}
-			newLoadBalancer.Cost = 88.88
 			loadBalancerMap[key] = &newLoadBalancer
 		}
 	}

+ 2 - 2
pkg/costmodel/router.go

@@ -900,8 +900,8 @@ func Initialize(additionalConfigWatchers ...ConfigWatchers) {
 		Help: "kubecost_cluster_management_cost Hourly cost paid as a cluster management fee.",
 	}, []string{"provisioner_name"})
 	LBCostRecorder := prometheus.NewGaugeVec(prometheus.GaugeOpts{ // don't know if necessary to differentiate ELB vs. ALB cost
-		Name: "load_balancer_cost",
-		Help: "load_balancer_cost Hourly cost of load balancer",
+		Name: "kubecost_load_balancer_cost",
+		Help: "kubecost_load_balancer_cost Hourly cost of load balancer",
 	}, []string{"namespace", "service_name"}) // will likely need some adjustments. could have multiple IPs per LB, so ignore for now
 
 	prometheus.MustRegister(cpuGv)