AjayTripathy 5 anni fa
parent
commit
7c2420a0dc
4 ha cambiato i file con 151 aggiunte e 19 eliminazioni
  1. 2 2
      configs/pricing_schema.csv
  2. 4 0
      configs/pricing_schema_region.csv
  3. 30 11
      pkg/cloud/csvprovider.go
  4. 115 6
      test/cloud_test.go

+ 2 - 2
configs/pricing_schema.csv

@@ -1,2 +1,2 @@
-EndTimestamp,InstanceID,AssetClass,InstanceIDField,InstanceType,MarketPriceHourly,Version
-2019-04-17 23:34:22 UTC,gke-standard-cluster-1-pool-1-91dc432d-cg69,node,metadata.name,,0.1337,
+EndTimestamp,InstanceID,Region,AssetClass,InstanceIDField,InstanceType,MarketPriceHourly,Version
+2019-04-17 23:34:22 UTC,gke-standard-cluster-1-pool-1-91dc432d-cg69,,node,metadata.name,,0.1337,

+ 4 - 0
configs/pricing_schema_region.csv

@@ -0,0 +1,4 @@
+EndTimestamp,InstanceID,Region,AssetClass,InstanceIDField,InstanceType,MarketPriceHourly,Version
+2019-04-17 23:34:22 UTC,gke-standard-cluster-1-pool-1-91dc432d-cg69,regionone,node,spec.providerID,,0.1337,
+2019-04-17 23:34:22 UTC,gke-standard-cluster-1-pool-1-91dc432d-cg69,regiontwo,node,spec.providerID,,0.1338,
+2019-04-17 23:34:22 UTC,gke-standard-cluster-1-pool-1-91dc432d-cg69,,node,spec.providerID,,0.1339,

+ 30 - 11
pkg/cloud/csvprovider.go

@@ -28,11 +28,13 @@ type CSVProvider struct {
 	NodeMapField            string
 	PricingPV               map[string]*price
 	PVMapField              string
+	UsesRegion              bool
 	DownloadPricingDataLock sync.RWMutex
 }
 type price struct {
 	EndTimestamp      string `csv:"EndTimestamp"`
 	InstanceID        string `csv:"InstanceID"`
+	Region            string `csv:"Region"`
 	AssetClass        string `csv:"AssetClass"`
 	InstanceIDField   string `csv:"InstanceIDField"`
 	InstanceType      string `csv:"InstanceType"`
@@ -115,15 +117,20 @@ func (c *CSVProvider) DownloadPricingData() error {
 			continue
 		}
 		klog.V(4).Infof("Found price info %+v", p)
+		key := p.InstanceID
+		if p.Region != "" {
+			key = fmt.Sprintf("%s,%s", p.Region, p.InstanceID)
+			c.UsesRegion = true
+		}
 		if p.AssetClass == "pv" {
-			pvpricing[p.InstanceID] = &p
+			pvpricing[key] = &p
 			c.PVMapField = p.InstanceIDField
 		} else if p.AssetClass == "node" {
-			pricing[p.InstanceID] = &p
+			pricing[key] = &p
 			c.NodeMapField = p.InstanceIDField
 		} else {
 			klog.Infof("Unrecognized asset class %s, defaulting to node", p.AssetClass)
-			pricing[p.InstanceID] = &p
+			pricing[key] = &p
 			c.NodeMapField = p.InstanceIDField
 		}
 	}
@@ -160,32 +167,44 @@ func (c *CSVProvider) NodePricing(key Key) (*Node, error) {
 			Cost: p.MarketPriceHourly,
 		}, nil
 	}
+	s := strings.Split(key.ID(), ",") // Try without a region to be sure
+	if len(s) == 2 {
+		if p, ok := c.Pricing[s[1]]; ok {
+			return &Node{
+				Cost: p.MarketPriceHourly,
+			}, nil
+		}
+	}
 	return nil, fmt.Errorf("Unable to find Node matching %s", key.ID())
 }
 
-func NodeValueFromMapField(m string, n *v1.Node) string {
+func NodeValueFromMapField(m string, n *v1.Node, useRegion bool) string {
 	mf := strings.Split(m, ".")
+	toReturn := ""
+	if useRegion {
+		toReturn = n.Labels[v1.LabelZoneRegion] + ","
+	}
 	if len(mf) == 2 && mf[0] == "spec" && mf[1] == "providerID" {
 		provIdRx := regexp.MustCompile("aws:///([^/]+)/([^/]+)") // It's of the form aws:///us-east-2a/i-0fea4fd46592d050b and we want i-0fea4fd46592d050b, if it exists
 		for matchNum, group := range provIdRx.FindStringSubmatch(n.Spec.ProviderID) {
 			if matchNum == 2 {
-				return group
+				return toReturn + group
 			}
 		}
 		if strings.HasPrefix(n.Spec.ProviderID, "azure://") {
 			vmOrScaleSet := strings.TrimPrefix(n.Spec.ProviderID, "azure://")
-			return vmOrScaleSet
+			return toReturn + vmOrScaleSet
 		}
-		return n.Spec.ProviderID
+		return toReturn + n.Spec.ProviderID
 	} else if len(mf) > 1 && mf[0] == "metadata" {
 		if mf[1] == "name" {
-			return n.Name
+			return toReturn + n.Name
 		} else if mf[1] == "labels" {
 			lkey := strings.Join(mf[2:len(mf)], "")
-			return n.Labels[lkey]
+			return toReturn + n.Labels[lkey]
 		} else if mf[1] == "annotations" {
 			akey := strings.Join(mf[2:len(mf)], "")
-			return n.Annotations[akey]
+			return toReturn + n.Annotations[akey]
 		} else {
 			klog.Infof("[ERROR] Unsupported InstanceIDField %s in CSV For Node", m)
 			return ""
@@ -218,7 +237,7 @@ func PVValueFromMapField(m string, n *v1.PersistentVolume) string {
 }
 
 func (c *CSVProvider) GetKey(l map[string]string, n *v1.Node) Key {
-	id := NodeValueFromMapField(c.NodeMapField, n)
+	id := NodeValueFromMapField(c.NodeMapField, n, c.UsesRegion)
 	return &csvKey{
 		ProviderID: id,
 		Labels:     l,

+ 115 - 6
test/cloud_test.go

@@ -11,11 +11,28 @@ const(
 	nameMap = "metadata.name"
 	labelMapFoo = "metadata.labels.foo"
 )
+
+func TestRegionValueFromMapField(t * testing.T) {
+	wantRegion := "useast"
+	wantpid := "/subscriptions/0bd50fdf-c923-4e1e-850c-196dd3dcc5d3/resourceGroups/MC_test_test_eastus/providers/Microsoft.Compute/virtualMachines/aks-agentpool-20139558-0"
+	providerIDWant := wantRegion + "," + wantpid
+
+	n := &v1.Node{}
+	n.Spec.ProviderID = "azure:///subscriptions/0bd50fdf-c923-4e1e-850c-196dd3dcc5d3/resourceGroups/MC_test_test_eastus/providers/Microsoft.Compute/virtualMachines/aks-agentpool-20139558-0"
+	n.Labels = make(map[string]string)
+	n.Labels[v1.LabelZoneRegion] = wantRegion
+	got := cloud.NodeValueFromMapField(providerIDMap, n, true)
+	if got != providerIDWant {
+		t.Errorf("Assert on '%s' want '%s' got '%s'", providerIDMap, providerIDWant, got)
+	}
+
+
+}
 func TestTransformedValueFromMapField(t *testing.T) {
 	providerIDWant := "i-05445591e0d182d42"
 	n := &v1.Node{}
 	n.Spec.ProviderID = "aws:///us-east-1a/i-05445591e0d182d42"
-	got := cloud.NodeValueFromMapField(providerIDMap, n)
+	got := cloud.NodeValueFromMapField(providerIDMap, n, false)
 	if got != providerIDWant {
 		t.Errorf("Assert on '%s' want '%s' got '%s'", providerIDMap, providerIDWant, got)
 	}
@@ -23,7 +40,7 @@ func TestTransformedValueFromMapField(t *testing.T) {
 	providerIDWant2 := "/subscriptions/0bd50fdf-c923-4e1e-850c-196dd3dcc5d3/resourceGroups/MC_test_test_eastus/providers/Microsoft.Compute/virtualMachines/aks-agentpool-20139558-0"
 	n2 := &v1.Node{}
 	n2.Spec.ProviderID = "azure:///subscriptions/0bd50fdf-c923-4e1e-850c-196dd3dcc5d3/resourceGroups/MC_test_test_eastus/providers/Microsoft.Compute/virtualMachines/aks-agentpool-20139558-0"
-	got2 := cloud.NodeValueFromMapField(providerIDMap, n2)
+	got2 := cloud.NodeValueFromMapField(providerIDMap, n2, false)
 	if got2 != providerIDWant2 {
 		t.Errorf("Assert on '%s' want '%s' got '%s'", providerIDMap, providerIDWant2, got2)
 	}
@@ -31,7 +48,7 @@ func TestTransformedValueFromMapField(t *testing.T) {
 	providerIDWant3 := "/subscriptions/0bd50fdf-c923-4e1e-850c-196dd3dcc5d3/resourceGroups/mc_testspot_testspot_eastus/providers/Microsoft.Compute/virtualMachineScaleSets/aks-nodepool1-19213364-vmss/virtualMachines/0"
 	n3 := &v1.Node{}
 	n3.Spec.ProviderID = "azure:///subscriptions/0bd50fdf-c923-4e1e-850c-196dd3dcc5d3/resourceGroups/mc_testspot_testspot_eastus/providers/Microsoft.Compute/virtualMachineScaleSets/aks-nodepool1-19213364-vmss/virtualMachines/0"
-	got3 := cloud.NodeValueFromMapField(providerIDMap, n3)
+	got3 := cloud.NodeValueFromMapField(providerIDMap, n3, false)
 	if got3 != providerIDWant3 {
 		t.Errorf("Assert on '%s' want '%s' got '%s'", providerIDMap, providerIDWant3, got3)
 	}
@@ -50,17 +67,17 @@ func TestNodeValueFromMapField(t *testing.T) {
 	n.Labels = make(map[string]string)
 	n.Labels["foo"] = labelFooWant
 
-	got := cloud.NodeValueFromMapField(providerIDMap, n)
+	got := cloud.NodeValueFromMapField(providerIDMap, n, false)
 	if got != providerIDWant {
 		t.Errorf("Assert on '%s' want '%s' got '%s'", providerIDMap, providerIDWant, got)
 	}
 	
-	got = cloud.NodeValueFromMapField(nameMap, n)
+	got = cloud.NodeValueFromMapField(nameMap, n, false)
 	if got != nameWant {
 		t.Errorf("Assert on '%s' want '%s' got '%s'", nameMap, nameWant, got)
 	}
 
-	got = cloud.NodeValueFromMapField(labelMapFoo, n)
+	got = cloud.NodeValueFromMapField(labelMapFoo, n, false)
 	if got != labelFooWant {
 		t.Errorf("Assert on '%s' want '%s' got '%s'", labelMapFoo, labelFooWant, got)
 	}
@@ -121,3 +138,95 @@ func TestNodePriceFromCSV(t * testing.T) {
 		t.Errorf("CSV provider should return nil on missing csv")
 	}
 }
+
+func TestNodePriceFromCSVWithRegion(t * testing.T) {
+	providerIDWant := "gke-standard-cluster-1-pool-1-91dc432d-cg69"
+	nameWant := "foo"
+	labelFooWant := "labelfoo"
+
+	n := &v1.Node{}
+	n.Spec.ProviderID = providerIDWant
+	n.Name = nameWant
+	n.Labels = make(map[string]string)
+	n.Labels["foo"] = labelFooWant
+	n.Labels[v1.LabelZoneRegion] = "regionone"
+	wantPrice := "0.1337"
+
+	n2 := &v1.Node{}
+	n2.Spec.ProviderID = providerIDWant
+	n2.Name = nameWant
+	n2.Labels = make(map[string]string)
+	n2.Labels["foo"] = labelFooWant
+	n2.Labels[v1.LabelZoneRegion] = "regiontwo"
+	wantPrice2 := "0.1338"
+
+	n3 := &v1.Node{}
+	n3.Spec.ProviderID = providerIDWant
+	n3.Name = nameWant
+	n3.Labels = make(map[string]string)
+	n3.Labels["foo"] = labelFooWant
+	n3.Labels[v1.LabelZoneRegion] = "fakeregion"
+	wantPrice3 := "0.1339"
+
+	c := &cloud.CSVProvider{
+		CSVLocation: "../configs/pricing_schema_region.csv",
+		CustomProvider: &cloud.CustomProvider{
+			Config:    cloud.NewProviderConfig("../configs/default.json"),
+		},
+	}
+	c.DownloadPricingData()
+	k := c.GetKey(n.Labels, n)
+	resN, err := c.NodePricing(k)
+	if err != nil {
+		t.Errorf("Error in NodePricing: %s", err.Error())
+	} else {
+		gotPrice := resN.Cost
+		if gotPrice != wantPrice {
+			t.Errorf("Wanted price '%s' got price '%s'", wantPrice, gotPrice)
+		}
+	}
+	k2 := c.GetKey(n2.Labels, n2)
+	resN2, err := c.NodePricing(k2)
+	if err != nil {
+		t.Errorf("Error in NodePricing: %s", err.Error())
+	} else {
+		gotPrice := resN2.Cost
+		if gotPrice != wantPrice2 {
+			t.Errorf("Wanted price '%s' got price '%s'", wantPrice2, gotPrice)
+		}
+	}
+	k3 := c.GetKey(n3.Labels, n3)
+	resN3, err := c.NodePricing(k3)
+	if err != nil {
+		t.Errorf("Error in NodePricing: %s", err.Error())
+	} else {
+		gotPrice := resN3.Cost
+		if gotPrice != wantPrice3 {
+			t.Errorf("Wanted price '%s' got price '%s'", wantPrice3, gotPrice)
+		}
+	}
+
+
+	unknownN := &v1.Node{}
+	unknownN.Spec.ProviderID = "fake providerID"
+	unknownN.Name = "unknownname"
+	unknownN.Labels = make(map[string]string)
+	unknownN.Labels["foo"] = labelFooWant
+	k4 := c.GetKey(unknownN.Labels, unknownN)
+	resN4, _ := c.NodePricing(k4)
+	if resN4 != nil {
+		t.Errorf("CSV provider should return nil on missing node, instead returned %+v", resN4)
+	}
+	
+	c2 := &cloud.CSVProvider{
+		CSVLocation: "../configs/fake.csv",
+		CustomProvider: &cloud.CustomProvider{
+			Config:    cloud.NewProviderConfig("../configs/default.json"),
+		},
+	}
+	k5 := c.GetKey(n.Labels, n)
+	resN5, _ := c2.NodePricing(k5)
+	if resN5 != nil {
+		t.Errorf("CSV provider should return nil on missing csv")
+	}
+}