|
|
@@ -14,7 +14,8 @@ import (
|
|
|
"sync"
|
|
|
"time"
|
|
|
|
|
|
- "github.com/opencost/opencost/pkg/cloud/types"
|
|
|
+ "github.com/opencost/opencost/pkg/cloud/models"
|
|
|
+ "github.com/opencost/opencost/pkg/cloud/utils"
|
|
|
"github.com/opencost/opencost/pkg/kubecost"
|
|
|
|
|
|
"github.com/opencost/opencost/pkg/clustercache"
|
|
|
@@ -154,7 +155,7 @@ func (gcp *GCP) GetLocalStorageQuery(window, offset time.Duration, rate bool, us
|
|
|
return fmt.Sprintf(fmtQuery, baseMetric, fmtWindow, fmtOffset, env.GetPromClusterLabel(), localStorageCost)
|
|
|
}
|
|
|
|
|
|
-func (gcp *GCP) GetConfig() (*types.CustomPricing, error) {
|
|
|
+func (gcp *GCP) GetConfig() (*models.CustomPricing, error) {
|
|
|
c, err := gcp.Config.GetCustomPricingData()
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
@@ -169,7 +170,7 @@ func (gcp *GCP) GetConfig() (*types.CustomPricing, error) {
|
|
|
c.CurrencyCode = "USD"
|
|
|
}
|
|
|
if c.ShareTenancyCosts == "" {
|
|
|
- c.ShareTenancyCosts = types.DefaultShareTenancyCost
|
|
|
+ c.ShareTenancyCosts = models.DefaultShareTenancyCost
|
|
|
}
|
|
|
return c, nil
|
|
|
}
|
|
|
@@ -211,7 +212,7 @@ func (*GCP) loadGCPAuthSecret() {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- exists, err := fileutil.FileExists(types.AuthSecretPath)
|
|
|
+ exists, err := fileutil.FileExists(models.AuthSecretPath)
|
|
|
if !exists || err != nil {
|
|
|
errMessage := "Secret does not exist"
|
|
|
if err != nil {
|
|
|
@@ -222,7 +223,7 @@ func (*GCP) loadGCPAuthSecret() {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- result, err := os.ReadFile(types.AuthSecretPath)
|
|
|
+ result, err := os.ReadFile(models.AuthSecretPath)
|
|
|
if err != nil {
|
|
|
log.Warnf("Failed to load auth secret, or was not mounted: %s", err.Error())
|
|
|
return
|
|
|
@@ -234,12 +235,12 @@ func (*GCP) loadGCPAuthSecret() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func (gcp *GCP) UpdateConfigFromConfigMap(a map[string]string) (*types.CustomPricing, error) {
|
|
|
+func (gcp *GCP) UpdateConfigFromConfigMap(a map[string]string) (*models.CustomPricing, error) {
|
|
|
return gcp.Config.UpdateFromMap(a)
|
|
|
}
|
|
|
|
|
|
-func (gcp *GCP) UpdateConfig(r io.Reader, updateType string) (*types.CustomPricing, error) {
|
|
|
- return gcp.Config.Update(func(c *types.CustomPricing) error {
|
|
|
+func (gcp *GCP) UpdateConfig(r io.Reader, updateType string) (*models.CustomPricing, error) {
|
|
|
+ return gcp.Config.Update(func(c *models.CustomPricing) error {
|
|
|
if updateType == BigqueryUpdateType {
|
|
|
a := BigQueryConfig{}
|
|
|
err := json.NewDecoder(r).Decode(&a)
|
|
|
@@ -286,10 +287,10 @@ func (gcp *GCP) UpdateConfig(r io.Reader, updateType string) (*types.CustomPrici
|
|
|
return err
|
|
|
}
|
|
|
for k, v := range a {
|
|
|
- kUpper := types.ToTitle.String(k) // Just so we consistently supply / receive the same values, uppercase the first letter.
|
|
|
+ kUpper := utils.ToTitle.String(k) // Just so we consistently supply / receive the same values, uppercase the first letter.
|
|
|
vstr, ok := v.(string)
|
|
|
if ok {
|
|
|
- err := types.SetCustomPricingField(c, kUpper, vstr)
|
|
|
+ err := models.SetCustomPricingField(c, kUpper, vstr)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
@@ -300,7 +301,7 @@ func (gcp *GCP) UpdateConfig(r io.Reader, updateType string) (*types.CustomPrici
|
|
|
}
|
|
|
|
|
|
if env.IsRemoteEnabled() {
|
|
|
- err := types.UpdateClusterMeta(env.GetClusterID(), c.ClusterName)
|
|
|
+ err := utils.UpdateClusterMeta(env.GetClusterID(), c.ClusterName)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
@@ -445,7 +446,7 @@ func (gcp *GCP) isDiskOrphaned(disk *compute.Disk) (bool, error) {
|
|
|
return true, nil
|
|
|
}
|
|
|
|
|
|
-func (gcp *GCP) GetOrphanedResources() ([]types.OrphanedResource, error) {
|
|
|
+func (gcp *GCP) GetOrphanedResources() ([]models.OrphanedResource, error) {
|
|
|
disks, err := gcp.getAllDisks()
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
@@ -456,7 +457,7 @@ func (gcp *GCP) GetOrphanedResources() ([]types.OrphanedResource, error) {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
- var orphanedResources []types.OrphanedResource
|
|
|
+ var orphanedResources []models.OrphanedResource
|
|
|
|
|
|
for _, diskList := range disks.Items {
|
|
|
if len(diskList.Disks) == 0 {
|
|
|
@@ -489,7 +490,7 @@ func (gcp *GCP) GetOrphanedResources() ([]types.OrphanedResource, error) {
|
|
|
zone = ""
|
|
|
}
|
|
|
|
|
|
- or := types.OrphanedResource{
|
|
|
+ or := models.OrphanedResource{
|
|
|
Kind: "disk",
|
|
|
Region: zone,
|
|
|
Description: desc,
|
|
|
@@ -519,7 +520,7 @@ func (gcp *GCP) GetOrphanedResources() ([]types.OrphanedResource, error) {
|
|
|
region = ""
|
|
|
}
|
|
|
|
|
|
- or := types.OrphanedResource{
|
|
|
+ or := models.OrphanedResource{
|
|
|
Kind: "address",
|
|
|
Region: region,
|
|
|
Description: map[string]string{
|
|
|
@@ -573,8 +574,8 @@ type GCPPricing struct {
|
|
|
ServiceRegions []string `json:"serviceRegions"`
|
|
|
PricingInfo []*PricingInfo `json:"pricingInfo"`
|
|
|
ServiceProviderName string `json:"serviceProviderName"`
|
|
|
- Node *types.Node `json:"node"`
|
|
|
- PV *types.PV `json:"pv"`
|
|
|
+ Node *models.Node `json:"node"`
|
|
|
+ PV *models.PV `json:"pv"`
|
|
|
}
|
|
|
|
|
|
// PricingInfo contains metadata about a cost.
|
|
|
@@ -616,7 +617,7 @@ type GCPResourceInfo struct {
|
|
|
UsageType string `json:"usageType"`
|
|
|
}
|
|
|
|
|
|
-func (gcp *GCP) parsePage(r io.Reader, inputKeys map[string]types.Key, pvKeys map[string]types.PVKey) (map[string]*GCPPricing, string, error) {
|
|
|
+func (gcp *GCP) parsePage(r io.Reader, inputKeys map[string]models.Key, pvKeys map[string]models.PVKey) (map[string]*GCPPricing, string, error) {
|
|
|
gcpPricingList := make(map[string]*GCPPricing)
|
|
|
var nextPageToken string
|
|
|
dec := json.NewDecoder(r)
|
|
|
@@ -657,7 +658,7 @@ func (gcp *GCP) parsePage(r io.Reader, inputKeys map[string]types.Key, pvKeys ma
|
|
|
region := sr
|
|
|
candidateKey := region + "," + "ssd"
|
|
|
if _, ok := pvKeys[candidateKey]; ok {
|
|
|
- product.PV = &types.PV{
|
|
|
+ product.PV = &models.PV{
|
|
|
Cost: strconv.FormatFloat(hourlyPrice, 'f', -1, 64),
|
|
|
}
|
|
|
gcpPricingList[candidateKey] = product
|
|
|
@@ -679,7 +680,7 @@ func (gcp *GCP) parsePage(r io.Reader, inputKeys map[string]types.Key, pvKeys ma
|
|
|
region := sr
|
|
|
candidateKey := region + "," + "ssd" + "," + "regional"
|
|
|
if _, ok := pvKeys[candidateKey]; ok {
|
|
|
- product.PV = &types.PV{
|
|
|
+ product.PV = &models.PV{
|
|
|
Cost: strconv.FormatFloat(hourlyPrice, 'f', -1, 64),
|
|
|
}
|
|
|
gcpPricingList[candidateKey] = product
|
|
|
@@ -700,7 +701,7 @@ func (gcp *GCP) parsePage(r io.Reader, inputKeys map[string]types.Key, pvKeys ma
|
|
|
region := sr
|
|
|
candidateKey := region + "," + "pdstandard"
|
|
|
if _, ok := pvKeys[candidateKey]; ok {
|
|
|
- product.PV = &types.PV{
|
|
|
+ product.PV = &models.PV{
|
|
|
Cost: strconv.FormatFloat(hourlyPrice, 'f', -1, 64),
|
|
|
}
|
|
|
gcpPricingList[candidateKey] = product
|
|
|
@@ -721,7 +722,7 @@ func (gcp *GCP) parsePage(r io.Reader, inputKeys map[string]types.Key, pvKeys ma
|
|
|
region := sr
|
|
|
candidateKey := region + "," + "pdstandard" + "," + "regional"
|
|
|
if _, ok := pvKeys[candidateKey]; ok {
|
|
|
- product.PV = &types.PV{
|
|
|
+ product.PV = &models.PV{
|
|
|
Cost: strconv.FormatFloat(hourlyPrice, 'f', -1, 64),
|
|
|
}
|
|
|
gcpPricingList[candidateKey] = product
|
|
|
@@ -839,7 +840,7 @@ func (gcp *GCP) parsePage(r io.Reader, inputKeys map[string]types.Key, pvKeys ma
|
|
|
pl.Node.GPUCost = strconv.FormatFloat(hourlyPrice, 'f', -1, 64)
|
|
|
pl.Node.GPU = "1"
|
|
|
} else {
|
|
|
- product.Node = &types.Node{
|
|
|
+ product.Node = &models.Node{
|
|
|
GPUName: gpuType,
|
|
|
GPUCost: strconv.FormatFloat(hourlyPrice, 'f', -1, 64),
|
|
|
GPU: "1",
|
|
|
@@ -881,7 +882,7 @@ func (gcp *GCP) parsePage(r io.Reader, inputKeys map[string]types.Key, pvKeys ma
|
|
|
gcpPricingList[candidateKey].Node.RAMCost = strconv.FormatFloat(hourlyPrice, 'f', -1, 64)
|
|
|
} else {
|
|
|
product = &GCPPricing{}
|
|
|
- product.Node = &types.Node{
|
|
|
+ product.Node = &models.Node{
|
|
|
RAMCost: strconv.FormatFloat(hourlyPrice, 'f', -1, 64),
|
|
|
}
|
|
|
partialCPU, pcok := partialCPUMap[instanceType]
|
|
|
@@ -897,7 +898,7 @@ func (gcp *GCP) parsePage(r io.Reader, inputKeys map[string]types.Key, pvKeys ma
|
|
|
} else {
|
|
|
log.Infof("Adding RAM %f for %s", hourlyPrice, candidateKeyGPU)
|
|
|
product = &GCPPricing{}
|
|
|
- product.Node = &types.Node{
|
|
|
+ product.Node = &models.Node{
|
|
|
RAMCost: strconv.FormatFloat(hourlyPrice, 'f', -1, 64),
|
|
|
}
|
|
|
partialCPU, pcok := partialCPUMap[instanceType]
|
|
|
@@ -913,7 +914,7 @@ func (gcp *GCP) parsePage(r io.Reader, inputKeys map[string]types.Key, pvKeys ma
|
|
|
gcpPricingList[candidateKey].Node.VCPUCost = strconv.FormatFloat(hourlyPrice, 'f', -1, 64)
|
|
|
} else {
|
|
|
product = &GCPPricing{}
|
|
|
- product.Node = &types.Node{
|
|
|
+ product.Node = &models.Node{
|
|
|
VCPUCost: strconv.FormatFloat(hourlyPrice, 'f', -1, 64),
|
|
|
}
|
|
|
partialCPU, pcok := partialCPUMap[instanceType]
|
|
|
@@ -927,7 +928,7 @@ func (gcp *GCP) parsePage(r io.Reader, inputKeys map[string]types.Key, pvKeys ma
|
|
|
gcpPricingList[candidateKeyGPU].Node.VCPUCost = strconv.FormatFloat(hourlyPrice, 'f', -1, 64)
|
|
|
} else {
|
|
|
product = &GCPPricing{}
|
|
|
- product.Node = &types.Node{
|
|
|
+ product.Node = &models.Node{
|
|
|
VCPUCost: strconv.FormatFloat(hourlyPrice, 'f', -1, 64),
|
|
|
}
|
|
|
partialCPU, pcok := partialCPUMap[instanceType]
|
|
|
@@ -960,7 +961,7 @@ func (gcp *GCP) parsePage(r io.Reader, inputKeys map[string]types.Key, pvKeys ma
|
|
|
return gcpPricingList, nextPageToken, nil
|
|
|
}
|
|
|
|
|
|
-func (gcp *GCP) parsePages(inputKeys map[string]types.Key, pvKeys map[string]types.PVKey) (map[string]*GCPPricing, error) {
|
|
|
+func (gcp *GCP) parsePages(inputKeys map[string]models.Key, pvKeys map[string]models.PVKey) (map[string]*GCPPricing, error) {
|
|
|
var pages []map[string]*GCPPricing
|
|
|
c, err := gcp.GetConfig()
|
|
|
if err != nil {
|
|
|
@@ -1045,7 +1046,7 @@ func (gcp *GCP) DownloadPricingData() error {
|
|
|
gcp.BillingDataDataset = c.BillingDataDataset
|
|
|
|
|
|
nodeList := gcp.Clientset.GetAllNodes()
|
|
|
- inputkeys := make(map[string]types.Key)
|
|
|
+ inputkeys := make(map[string]models.Key)
|
|
|
|
|
|
defaultRegion := "" // Sometimes, PVs may be missing the region label. In that case assume that they are in the same region as the nodes
|
|
|
for _, n := range nodeList {
|
|
|
@@ -1074,7 +1075,7 @@ func (gcp *GCP) DownloadPricingData() error {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- pvkeys := make(map[string]types.PVKey)
|
|
|
+ pvkeys := make(map[string]models.PVKey)
|
|
|
for _, pv := range pvList {
|
|
|
params, ok := storageClassMap[pv.Spec.StorageClassName]
|
|
|
if !ok {
|
|
|
@@ -1108,19 +1109,19 @@ func (gcp *GCP) DownloadPricingData() error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func (gcp *GCP) PVPricing(pvk types.PVKey) (*types.PV, error) {
|
|
|
+func (gcp *GCP) PVPricing(pvk models.PVKey) (*models.PV, error) {
|
|
|
gcp.DownloadPricingDataLock.RLock()
|
|
|
defer gcp.DownloadPricingDataLock.RUnlock()
|
|
|
pricing, ok := gcp.Pricing[pvk.Features()]
|
|
|
if !ok {
|
|
|
log.Infof("Persistent Volume pricing not found for %s: %s", pvk.GetStorageClass(), pvk.Features())
|
|
|
- return &types.PV{}, nil
|
|
|
+ return &models.PV{}, nil
|
|
|
}
|
|
|
return pricing.PV, nil
|
|
|
}
|
|
|
|
|
|
// Stubbed NetworkPricing for GCP. Pull directly from gcp.json for now
|
|
|
-func (gcp *GCP) NetworkPricing() (*types.Network, error) {
|
|
|
+func (gcp *GCP) NetworkPricing() (*models.Network, error) {
|
|
|
cpricing, err := gcp.Config.GetCustomPricingData()
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
@@ -1138,14 +1139,14 @@ func (gcp *GCP) NetworkPricing() (*types.Network, error) {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
- return &types.Network{
|
|
|
+ return &models.Network{
|
|
|
ZoneNetworkEgressCost: znec,
|
|
|
RegionNetworkEgressCost: rnec,
|
|
|
InternetNetworkEgressCost: inec,
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
|
-func (gcp *GCP) LoadBalancerPricing() (*types.LoadBalancer, error) {
|
|
|
+func (gcp *GCP) LoadBalancerPricing() (*models.LoadBalancer, error) {
|
|
|
fffrc := 0.025
|
|
|
afrc := 0.010
|
|
|
lbidc := 0.008
|
|
|
@@ -1159,7 +1160,7 @@ func (gcp *GCP) LoadBalancerPricing() (*types.LoadBalancer, error) {
|
|
|
} else {
|
|
|
totalCost = fffrc*5 + afrc*(numForwardingRules-5) + lbidc*dataIngressGB
|
|
|
}
|
|
|
- return &types.LoadBalancer{
|
|
|
+ return &models.LoadBalancer{
|
|
|
Cost: totalCost,
|
|
|
}, nil
|
|
|
}
|
|
|
@@ -1219,7 +1220,7 @@ var gcpReservedInstancePlans map[string]*GCPReservedInstancePlan = map[string]*G
|
|
|
},
|
|
|
}
|
|
|
|
|
|
-func (gcp *GCP) ApplyReservedInstancePricing(nodes map[string]*types.Node) {
|
|
|
+func (gcp *GCP) ApplyReservedInstancePricing(nodes map[string]*models.Node) {
|
|
|
numReserved := len(gcp.ReservedInstances)
|
|
|
|
|
|
// Early return if no reserved instance data loaded
|
|
|
@@ -1277,7 +1278,7 @@ func (gcp *GCP) ApplyReservedInstancePricing(nodes map[string]*types.Node) {
|
|
|
continue
|
|
|
}
|
|
|
|
|
|
- node.Reserved = &types.ReservedInstanceData{
|
|
|
+ node.Reserved = &models.ReservedInstanceData{
|
|
|
ReservedCPU: 0,
|
|
|
ReservedRAM: 0,
|
|
|
}
|
|
|
@@ -1403,7 +1404,7 @@ func (key *pvKey) GetStorageClass() string {
|
|
|
return key.StorageClass
|
|
|
}
|
|
|
|
|
|
-func (gcp *GCP) GetPVKey(pv *v1.PersistentVolume, parameters map[string]string, defaultRegion string) types.PVKey {
|
|
|
+func (gcp *GCP) GetPVKey(pv *v1.PersistentVolume, parameters map[string]string, defaultRegion string) models.PVKey {
|
|
|
providerID := ""
|
|
|
if pv.Spec.GCEPersistentDisk != nil {
|
|
|
providerID = pv.Spec.GCEPersistentDisk.PDName
|
|
|
@@ -1442,7 +1443,7 @@ type gcpKey struct {
|
|
|
Labels map[string]string
|
|
|
}
|
|
|
|
|
|
-func (gcp *GCP) GetKey(labels map[string]string, n *v1.Node) types.Key {
|
|
|
+func (gcp *GCP) GetKey(labels map[string]string, n *v1.Node) models.Key {
|
|
|
return &gcpKey{
|
|
|
Labels: labels,
|
|
|
}
|
|
|
@@ -1523,13 +1524,13 @@ func (gcp *GCP) AllNodePricing() (interface{}, error) {
|
|
|
return gcp.Pricing, nil
|
|
|
}
|
|
|
|
|
|
-func (gcp *GCP) getPricing(key types.Key) (*GCPPricing, bool) {
|
|
|
+func (gcp *GCP) getPricing(key models.Key) (*GCPPricing, bool) {
|
|
|
gcp.DownloadPricingDataLock.RLock()
|
|
|
defer gcp.DownloadPricingDataLock.RUnlock()
|
|
|
n, ok := gcp.Pricing[key.Features()]
|
|
|
return n, ok
|
|
|
}
|
|
|
-func (gcp *GCP) isValidPricingKey(key types.Key) bool {
|
|
|
+func (gcp *GCP) isValidPricingKey(key models.Key) bool {
|
|
|
gcp.DownloadPricingDataLock.RLock()
|
|
|
defer gcp.DownloadPricingDataLock.RUnlock()
|
|
|
_, ok := gcp.ValidPricingKeys[key.Features()]
|
|
|
@@ -1537,7 +1538,7 @@ func (gcp *GCP) isValidPricingKey(key types.Key) bool {
|
|
|
}
|
|
|
|
|
|
// NodePricing returns GCP pricing data for a single node
|
|
|
-func (gcp *GCP) NodePricing(key types.Key) (*types.Node, error) {
|
|
|
+func (gcp *GCP) NodePricing(key models.Key) (*models.Node, error) {
|
|
|
if n, ok := gcp.getPricing(key); ok {
|
|
|
log.Debugf("Returning pricing for node %s: %+v from SKU %s", key, n.Node, n.Name)
|
|
|
n.Node.BaseCPUPrice = gcp.BaseCPUPrice
|
|
|
@@ -1558,14 +1559,14 @@ func (gcp *GCP) NodePricing(key types.Key) (*types.Node, error) {
|
|
|
return nil, fmt.Errorf("Warning: no pricing data found for %s", key)
|
|
|
}
|
|
|
|
|
|
-func (gcp *GCP) ServiceAccountStatus() *types.ServiceAccountStatus {
|
|
|
- return &types.ServiceAccountStatus{
|
|
|
- Checks: []*types.ServiceAccountCheck{},
|
|
|
+func (gcp *GCP) ServiceAccountStatus() *models.ServiceAccountStatus {
|
|
|
+ return &models.ServiceAccountStatus{
|
|
|
+ Checks: []*models.ServiceAccountCheck{},
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func (gcp *GCP) PricingSourceStatus() map[string]*types.PricingSource {
|
|
|
- return make(map[string]*types.PricingSource)
|
|
|
+func (gcp *GCP) PricingSourceStatus() map[string]*models.PricingSource {
|
|
|
+ return make(map[string]*models.PricingSource)
|
|
|
}
|
|
|
|
|
|
func (gcp *GCP) CombinedDiscountForNode(instanceType string, isPreemptible bool, defaultDiscount, negotiatedDiscount float64) float64 {
|