|
@@ -2,6 +2,12 @@ package provider
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
"testing"
|
|
"testing"
|
|
|
|
|
+
|
|
|
|
|
+ "github.com/opencost/opencost/core/pkg/clustercache"
|
|
|
|
|
+ "github.com/opencost/opencost/core/pkg/storage"
|
|
|
|
|
+ "github.com/opencost/opencost/pkg/config"
|
|
|
|
|
+ v1 "k8s.io/api/core/v1"
|
|
|
|
|
+ "k8s.io/apimachinery/pkg/api/resource"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
func TestParseLocalDiskID(t *testing.T) {
|
|
func TestParseLocalDiskID(t *testing.T) {
|
|
@@ -42,3 +48,160 @@ func TestParseLocalDiskID(t *testing.T) {
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+func TestProviderConfigUpdateFromMapPreservesHourlyPrices(t *testing.T) {
|
|
|
|
|
+ confMan := config.NewConfigFileManager(storage.NewMemoryStorage())
|
|
|
|
|
+ providerConfig := NewProviderConfig(confMan, "default.json")
|
|
|
|
|
+
|
|
|
|
|
+ updated, err := providerConfig.UpdateFromMap(map[string]string{
|
|
|
|
|
+ "CPU": "0.031611",
|
|
|
|
|
+ "spotCPU": "0.006655",
|
|
|
|
|
+ "RAM": "0.004237",
|
|
|
|
|
+ "spotRAM": "0.000892",
|
|
|
|
|
+ "GPU": "0.95",
|
|
|
|
|
+ "spotGPU": "0.308",
|
|
|
|
|
+ "storage": "0.00005479452",
|
|
|
|
|
+ })
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatalf("UpdateFromMap returned error: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if updated.CPU != "0.031611" {
|
|
|
|
|
+ t.Errorf("CPU = %q, want hourly value %q", updated.CPU, "0.031611")
|
|
|
|
|
+ }
|
|
|
|
|
+ if updated.SpotCPU != "0.006655" {
|
|
|
|
|
+ t.Errorf("SpotCPU = %q, want hourly value %q", updated.SpotCPU, "0.006655")
|
|
|
|
|
+ }
|
|
|
|
|
+ if updated.RAM != "0.004237" {
|
|
|
|
|
+ t.Errorf("RAM = %q, want hourly value %q", updated.RAM, "0.004237")
|
|
|
|
|
+ }
|
|
|
|
|
+ if updated.SpotRAM != "0.000892" {
|
|
|
|
|
+ t.Errorf("SpotRAM = %q, want hourly value %q", updated.SpotRAM, "0.000892")
|
|
|
|
|
+ }
|
|
|
|
|
+ if updated.GPU != "0.95" {
|
|
|
|
|
+ t.Errorf("GPU = %q, want hourly value %q", updated.GPU, "0.95")
|
|
|
|
|
+ }
|
|
|
|
|
+ if updated.SpotGPU != "0.308" {
|
|
|
|
|
+ t.Errorf("SpotGPU = %q, want hourly value %q", updated.SpotGPU, "0.308")
|
|
|
|
|
+ }
|
|
|
|
|
+ if updated.Storage != "0.00005479452" {
|
|
|
|
|
+ t.Errorf("Storage = %q, want hourly value %q", updated.Storage, "0.00005479452")
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func TestCustomProviderGetKeyDetectsGPUCapacity(t *testing.T) {
|
|
|
|
|
+ cases := []struct {
|
|
|
|
|
+ name string
|
|
|
|
|
+ provider *CustomProvider
|
|
|
|
|
+ labels map[string]string
|
|
|
|
|
+ capacity v1.ResourceList
|
|
|
|
|
+ wantGPUType string
|
|
|
|
|
+ wantGPUCount int
|
|
|
|
|
+ }{
|
|
|
|
|
+ {
|
|
|
|
|
+ name: "nvidia GPU capacity",
|
|
|
|
|
+ capacity: v1.ResourceList{
|
|
|
|
|
+ "nvidia.com/gpu": resource.MustParse("2"),
|
|
|
|
|
+ },
|
|
|
|
|
+ wantGPUType: "nvidia.com/gpu",
|
|
|
|
|
+ wantGPUCount: 2,
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: "virtual GPU capacity",
|
|
|
|
|
+ capacity: v1.ResourceList{
|
|
|
|
|
+ "k8s.amazonaws.com/vgpu": resource.MustParse("3"),
|
|
|
|
|
+ },
|
|
|
|
|
+ wantGPUType: "k8s.amazonaws.com/vgpu",
|
|
|
|
|
+ wantGPUCount: 3,
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: "configured GPU label takes precedence over capacity type",
|
|
|
|
|
+ provider: &CustomProvider{
|
|
|
|
|
+ GPULabel: "gpu.example/type",
|
|
|
|
|
+ },
|
|
|
|
|
+ labels: map[string]string{
|
|
|
|
|
+ "gpu.example/type": "a100",
|
|
|
|
|
+ },
|
|
|
|
|
+ capacity: v1.ResourceList{
|
|
|
|
|
+ "nvidia.com/gpu": resource.MustParse("4"),
|
|
|
|
|
+ },
|
|
|
|
|
+ wantGPUType: "a100",
|
|
|
|
|
+ wantGPUCount: 4,
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: "no GPU capacity",
|
|
|
|
|
+ capacity: v1.ResourceList{},
|
|
|
|
|
+ wantGPUType: "",
|
|
|
|
|
+ wantGPUCount: 0,
|
|
|
|
|
+ },
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for _, tt := range cases {
|
|
|
|
|
+ t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
+ customProvider := tt.provider
|
|
|
|
|
+ if customProvider == nil {
|
|
|
|
|
+ customProvider = &CustomProvider{}
|
|
|
|
|
+ }
|
|
|
|
|
+ labels := tt.labels
|
|
|
|
|
+ if labels == nil {
|
|
|
|
|
+ labels = map[string]string{}
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ key := customProvider.GetKey(labels, &clustercache.Node{
|
|
|
|
|
+ Labels: labels,
|
|
|
|
|
+ Status: v1.NodeStatus{
|
|
|
|
|
+ Capacity: tt.capacity,
|
|
|
|
|
+ },
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ if got := key.GPUType(); got != tt.wantGPUType {
|
|
|
|
|
+ t.Errorf("GPUType() = %q, want %q", got, tt.wantGPUType)
|
|
|
|
|
+ }
|
|
|
|
|
+ if got := key.GPUCount(); got != tt.wantGPUCount {
|
|
|
|
|
+ t.Errorf("GPUCount() = %d, want %d", got, tt.wantGPUCount)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func TestCustomProviderNodePricingUsesDetectedGPUCount(t *testing.T) {
|
|
|
|
|
+ customProvider := &CustomProvider{
|
|
|
|
|
+ Pricing: map[string]*NodePrice{
|
|
|
|
|
+ "default": {
|
|
|
|
|
+ CPU: "0.031611",
|
|
|
|
|
+ RAM: "0.004237",
|
|
|
|
|
+ },
|
|
|
|
|
+ "default,gpu": {
|
|
|
|
|
+ CPU: "0.031611",
|
|
|
|
|
+ RAM: "0.004237",
|
|
|
|
|
+ GPU: "0.95",
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ key := customProvider.GetKey(map[string]string{}, &clustercache.Node{
|
|
|
|
|
+ Status: v1.NodeStatus{
|
|
|
|
|
+ Capacity: v1.ResourceList{
|
|
|
|
|
+ "nvidia.com/gpu": resource.MustParse("2"),
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ node, _, err := customProvider.NodePricing(key)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatalf("NodePricing returned error: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if node.VCPUCost != "0.031611" {
|
|
|
|
|
+ t.Errorf("VCPUCost = %q, want %q", node.VCPUCost, "0.031611")
|
|
|
|
|
+ }
|
|
|
|
|
+ if node.RAMCost != "0.004237" {
|
|
|
|
|
+ t.Errorf("RAMCost = %q, want %q", node.RAMCost, "0.004237")
|
|
|
|
|
+ }
|
|
|
|
|
+ if node.GPUCost != "0.95" {
|
|
|
|
|
+ t.Errorf("GPUCost = %q, want %q", node.GPUCost, "0.95")
|
|
|
|
|
+ }
|
|
|
|
|
+ if node.GPU != "2" {
|
|
|
|
|
+ t.Errorf("GPU = %q, want %q", node.GPU, "2")
|
|
|
|
|
+ }
|
|
|
|
|
+}
|