diff --git a/v3/pkg/apis/hobbyfarm.io/v1/types.go b/v3/pkg/apis/hobbyfarm.io/v1/types.go index 6360cd1d..eacb50ab 100644 --- a/v3/pkg/apis/hobbyfarm.io/v1/types.go +++ b/v3/pkg/apis/hobbyfarm.io/v1/types.go @@ -595,10 +595,10 @@ type CostSpec struct { } type CostResource struct { - Id string `json:"id"` // id of the resource - Kind string `json:"kind"` // name like VirtualMachine - BasePrice uint64 `json:"base_price"` - TimeUnit string `json:"time_unit"` // one of [seconds, minutes, hours] - CreationUnixTimestamp int64 `json:"creation_unix_timestamp"` // unix timestamp in seconds - DeletionUnixTimestamp int64 `json:"deletion_unix_timestamp,omitempty"` // unix timestamp in seconds + Id string `json:"id"` // id of the resource + Kind string `json:"kind"` // name like VirtualMachine + BasePrice float64 `json:"base_price"` + TimeUnit string `json:"time_unit"` // one of [seconds, minutes, hours] + CreationUnixTimestamp int64 `json:"creation_unix_timestamp"` // unix timestamp in seconds + DeletionUnixTimestamp int64 `json:"deletion_unix_timestamp,omitempty"` // unix timestamp in seconds } diff --git a/v3/protos/cost/cost.pb.go b/v3/protos/cost/cost.pb.go index 2681da11..c700bf0c 100644 --- a/v3/protos/cost/cost.pb.go +++ b/v3/protos/cost/cost.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.1 +// protoc-gen-go v1.36.2 // protoc v3.21.12 // source: cost/cost.proto @@ -25,7 +25,7 @@ const ( type Cost struct { state protoimpl.MessageState `protogen:"open.v1"` CostGroup string `protobuf:"bytes,1,opt,name=cost_group,json=costGroup,proto3" json:"cost_group,omitempty"` // name of the cost group - Total uint64 `protobuf:"varint,2,opt,name=total,proto3" json:"total,omitempty"` // total cost for all sources + Total float64 `protobuf:"fixed64,2,opt,name=total,proto3" json:"total,omitempty"` // total cost for all sources Source []*CostSource `protobuf:"bytes,3,rep,name=source,proto3" json:"source,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -68,7 +68,7 @@ func (x *Cost) GetCostGroup() string { return "" } -func (x *Cost) GetTotal() uint64 { +func (x *Cost) GetTotal() float64 { if x != nil { return x.Total } @@ -84,8 +84,8 @@ func (x *Cost) GetSource() []*CostSource { type CostSource struct { state protoimpl.MessageState `protogen:"open.v1"` - Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` // resource kind like VirtualMachine - Cost uint64 `protobuf:"varint,2,opt,name=cost,proto3" json:"cost,omitempty"` // total cost for this kind + Kind string `protobuf:"bytes,1,opt,name=kind,proto3" json:"kind,omitempty"` // resource kind like VirtualMachine + Cost float64 `protobuf:"fixed64,2,opt,name=cost,proto3" json:"cost,omitempty"` // total cost for this kind Count uint64 `protobuf:"varint,3,opt,name=count,proto3" json:"count,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -128,7 +128,7 @@ func (x *CostSource) GetKind() string { return "" } -func (x *CostSource) GetCost() uint64 { +func (x *CostSource) GetCost() float64 { if x != nil { return x.Cost } @@ -145,12 +145,12 @@ func (x *CostSource) GetCount() uint64 { type CreateOrUpdateCostRequest struct { state protoimpl.MessageState `protogen:"open.v1"` CostGroup string `protobuf:"bytes,1,opt,name=cost_group,json=costGroup,proto3" json:"cost_group,omitempty"` - Kind string `protobuf:"bytes,3,opt,name=kind,proto3" json:"kind,omitempty"` // like VirtualMachine - BasePrice uint64 `protobuf:"varint,4,opt,name=base_price,json=basePrice,proto3" json:"base_price,omitempty"` - TimeUnit string `protobuf:"bytes,5,opt,name=time_unit,json=timeUnit,proto3" json:"time_unit,omitempty"` - Id string `protobuf:"bytes,6,opt,name=id,proto3" json:"id,omitempty"` // resource id - CreationUnixTimestamp int64 `protobuf:"varint,7,opt,name=creation_unix_timestamp,json=creationUnixTimestamp,proto3" json:"creation_unix_timestamp,omitempty"` // unix timestamp in seconds - DeletionUnixTimestamp *int64 `protobuf:"varint,8,opt,name=deletion_unix_timestamp,json=deletionUnixTimestamp,proto3,oneof" json:"deletion_unix_timestamp,omitempty"` // unix timestamp in seconds + Kind string `protobuf:"bytes,2,opt,name=kind,proto3" json:"kind,omitempty"` // like VirtualMachine + BasePrice float64 `protobuf:"fixed64,3,opt,name=base_price,json=basePrice,proto3" json:"base_price,omitempty"` + TimeUnit string `protobuf:"bytes,4,opt,name=time_unit,json=timeUnit,proto3" json:"time_unit,omitempty"` + Id string `protobuf:"bytes,5,opt,name=id,proto3" json:"id,omitempty"` // resource id + CreationUnixTimestamp int64 `protobuf:"varint,6,opt,name=creation_unix_timestamp,json=creationUnixTimestamp,proto3" json:"creation_unix_timestamp,omitempty"` // unix timestamp in seconds + DeletionUnixTimestamp *int64 `protobuf:"varint,7,opt,name=deletion_unix_timestamp,json=deletionUnixTimestamp,proto3,oneof" json:"deletion_unix_timestamp,omitempty"` // unix timestamp in seconds unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -199,7 +199,7 @@ func (x *CreateOrUpdateCostRequest) GetKind() string { return "" } -func (x *CreateOrUpdateCostRequest) GetBasePrice() uint64 { +func (x *CreateOrUpdateCostRequest) GetBasePrice() float64 { if x != nil { return x.BasePrice } @@ -289,29 +289,29 @@ var file_cost_cost_proto_rawDesc = []byte{ 0x6f, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x28, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, + 0x01, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x28, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x63, 0x6f, 0x73, 0x74, 0x2e, 0x43, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x4a, 0x0a, 0x0a, 0x43, 0x6f, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x04, 0x63, 0x6f, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, + 0x28, 0x01, 0x52, 0x04, 0x63, 0x6f, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xab, 0x02, 0x0a, 0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x73, 0x74, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x73, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x6b, - 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, - 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x09, 0x62, 0x61, 0x73, 0x65, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1b, - 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, + 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x09, 0x62, 0x61, 0x73, 0x65, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1b, + 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x75, 0x6e, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x36, 0x0a, 0x17, 0x63, + 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x63, 0x72, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x78, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3b, 0x0a, 0x17, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x08, + 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x15, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x6e, 0x69, 0x78, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x88, 0x01, 0x01, 0x42, 0x1a, 0x0a, 0x18, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x6e, diff --git a/v3/protos/cost/cost.proto b/v3/protos/cost/cost.proto index ab9bca1e..ffa108c6 100644 --- a/v3/protos/cost/cost.proto +++ b/v3/protos/cost/cost.proto @@ -22,24 +22,24 @@ service CostSvc { message Cost { string cost_group = 1; // name of the cost group - uint64 total = 2; // total cost for all sources + double total = 2; // total cost for all sources repeated CostSource source = 3; } message CostSource { string kind = 1; // resource kind like VirtualMachine - uint64 cost = 2; // total cost for this kind + double cost = 2; // total cost for this kind uint64 count = 3; } message CreateOrUpdateCostRequest { string cost_group = 1; - string kind = 3; // like VirtualMachine - uint64 base_price = 4; - string time_unit = 5; - string id = 6; // resource id - int64 creation_unix_timestamp = 7; // unix timestamp in seconds - optional int64 deletion_unix_timestamp = 8; // unix timestamp in seconds + string kind = 2; // like VirtualMachine + double base_price = 3; + string time_unit = 4; + string id = 5; // resource id + int64 creation_unix_timestamp = 6; // unix timestamp in seconds + optional int64 deletion_unix_timestamp = 7; // unix timestamp in seconds } message ListCostsResponse { diff --git a/v3/services/costsvc/internal/controller.go b/v3/services/costsvc/internal/controller.go index e038e3c4..582addc9 100644 --- a/v3/services/costsvc/internal/controller.go +++ b/v3/services/costsvc/internal/controller.go @@ -20,10 +20,9 @@ type costGroup struct { Id string Kind string CostGroup string - BasePrice uint64 + BasePrice float64 TimeUnit util.TimeUnit CreationTimestamp int64 - DeletionTimestamp *int64 } func newCostGroup(obj interface{}) (*costGroup, error) { @@ -42,9 +41,9 @@ func newCostGroup(obj interface{}) (*costGroup, error) { if !found { return nil, fmt.Errorf("%s label not found", labels.CostBasePrice) } - basePrice, err := strconv.ParseUint(basePriceLabel, 10, 64) + basePrice, err := strconv.ParseFloat(basePriceLabel, 64) if err != nil { - return nil, fmt.Errorf("%s label value is not an uint", labels.CostBasePrice) + return nil, fmt.Errorf("%s label value is not a float64", labels.CostBasePrice) } timeUnitLabel, found := objLabels[labels.CostTimeUnit] if !found { @@ -52,12 +51,7 @@ func newCostGroup(obj interface{}) (*costGroup, error) { } timeUnit, err := util.ParseTimeUnit(timeUnitLabel) if err != nil { - return nil, err - } - - var deletionTimestamp int64 - if unstructuredObj.GetDeletionTimestamp() != nil { - deletionTimestamp = unstructuredObj.GetDeletionTimestamp().Unix() + return nil, fmt.Errorf("%s label value is not a valid time unit", labels.CostTimeUnit) } return &costGroup{ @@ -67,7 +61,6 @@ func newCostGroup(obj interface{}) (*costGroup, error) { BasePrice: basePrice, TimeUnit: timeUnit, CreationTimestamp: unstructuredObj.GetCreationTimestamp().Unix(), - DeletionTimestamp: &deletionTimestamp, }, nil } diff --git a/v3/services/costsvc/internal/controller_test.go b/v3/services/costsvc/internal/controller_test.go new file mode 100644 index 00000000..e2aba785 --- /dev/null +++ b/v3/services/costsvc/internal/controller_test.go @@ -0,0 +1,157 @@ +package costservice + +import ( + "github.com/hobbyfarm/gargantua/v3/pkg/labels" + "github.com/hobbyfarm/gargantua/v3/pkg/util" + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "testing" + "time" +) + +func Test_newCostGroup(t *testing.T) { + creationUnixTimestamp := int64(100) + creation := time.Unix(creationUnixTimestamp, 0) + + tests := []struct { + name string + input *unstructured.Unstructured + want *costGroup + wantErr assert.ErrorAssertionFunc + }{ + { + name: "ok", + input: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "kind": "VirtualMachine", + "metadata": map[string]interface{}{ + "name": "vm-test", + "creationTimestamp": creation.Format(time.RFC3339), + "labels": map[string]interface{}{ + labels.CostGroup: "my-cost-group", + labels.CostBasePrice: "10.01", + labels.CostTimeUnit: util.TimeUnitSeconds, + }, + }, + }, + }, + want: &costGroup{ + Id: "vm-test", + Kind: "VirtualMachine", + CostGroup: "my-cost-group", + BasePrice: 10.01, + TimeUnit: util.TimeUnitSeconds, + CreationTimestamp: creationUnixTimestamp, + }, + }, + { + name: "no cost group", + input: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "kind": "VirtualMachine", + "metadata": map[string]interface{}{ + "name": "vm-test", + "creationTimestamp": creation.Format(time.RFC3339), + "labels": map[string]interface{}{ + labels.CostBasePrice: "10.01", + labels.CostTimeUnit: util.TimeUnitSeconds, + }, + }, + }, + }, + wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { + return assert.ErrorContains(t, err, labels.CostGroup) + }, + }, + { + name: "no base price", + input: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "kind": "VirtualMachine", + "metadata": map[string]interface{}{ + "name": "vm-test", + "creationTimestamp": creation.Format(time.RFC3339), + "labels": map[string]interface{}{ + labels.CostGroup: "my-cost-group", + labels.CostTimeUnit: util.TimeUnitSeconds, + }, + }, + }, + }, + wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { + return assert.ErrorContains(t, err, labels.CostBasePrice) + }, + }, + { + name: "invalid base price", + input: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "kind": "VirtualMachine", + "metadata": map[string]interface{}{ + "name": "vm-test", + "creationTimestamp": creation.Format(time.RFC3339), + "labels": map[string]interface{}{ + labels.CostGroup: "my-cost-group", + labels.CostBasePrice: "invalid", + labels.CostTimeUnit: util.TimeUnitSeconds, + }, + }, + }, + }, + wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { + return assert.ErrorContains(t, err, labels.CostBasePrice) + }, + }, + + { + name: "no time unit", + input: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "kind": "VirtualMachine", + "metadata": map[string]interface{}{ + "name": "vm-test", + "creationTimestamp": creation.Format(time.RFC3339), + "labels": map[string]interface{}{ + labels.CostGroup: "my-cost-group", + labels.CostBasePrice: "10.01", + }, + }, + }, + }, + wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { + return assert.ErrorContains(t, err, labels.CostTimeUnit) + }, + }, + { + name: "invalid base price", + input: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "kind": "VirtualMachine", + "metadata": map[string]interface{}{ + "name": "vm-test", + "creationTimestamp": creation.Format(time.RFC3339), + "labels": map[string]interface{}{ + labels.CostGroup: "my-cost-group", + labels.CostBasePrice: "10.01", + labels.CostTimeUnit: "invalid", + }, + }, + }, + }, + wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { + return assert.ErrorContains(t, err, labels.CostTimeUnit) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := newCostGroup(tt.input) + if tt.wantErr != nil { + tt.wantErr(t, err) + } else { + assert.NoErrorf(t, err, "newCostGroup() error = %v", err) + assert.Equal(t, tt.want, got) + } + }) + } +} diff --git a/v3/services/costsvc/internal/costservice.go b/v3/services/costsvc/internal/costservice.go index 91343f1c..0b7598a2 100644 --- a/v3/services/costsvc/internal/costservice.go +++ b/v3/services/costsvc/internal/costservice.go @@ -16,14 +16,14 @@ import ( type PreparedCost struct { CostGroup string `json:"cost_group"` - Total uint64 `json:"total"` + Total float64 `json:"total"` Sources []PreparedCostSource `json:"source"` } type PreparedCostSource struct { - Kind string `json:"kind"` - Cost uint64 `json:"cost"` - Count uint64 `json:"count"` + Kind string `json:"kind"` + Cost float64 `json:"cost"` + Count uint64 `json:"count"` } func NewPreparedCost(cost *costpb.Cost) PreparedCost { diff --git a/v3/services/costsvc/internal/grpc_test.go b/v3/services/costsvc/internal/grpc_test.go index 7eefff83..1e68f05d 100644 --- a/v3/services/costsvc/internal/grpc_test.go +++ b/v3/services/costsvc/internal/grpc_test.go @@ -95,17 +95,17 @@ func TestGrpcCostServer_GetCostHistory(t *testing.T) { costs, err := server.GetCostHistory(context.TODO(), &generalpb.GetRequest{Id: "my-cost-group"}) assert.NoError(t, err) assert.Equal(t, costs.GetCostGroup(), expectedCost.Name, "cost group matches") - assert.Equal(t, costs.GetTotal(), uint64(10+10+10+50), "cost group total") + assert.Equal(t, costs.GetTotal(), float64(10+10+10+50), "cost group total") assert.Len(t, costs.GetSource(), 2, "size of cost source") for _, source := range costs.Source { switch source.GetKind() { case "Pod": assert.Equal(t, source.GetCount(), uint64(2), "pod count") - assert.Equal(t, source.GetCost(), uint64(20), "pod costs") + assert.Equal(t, source.GetCost(), float64(20), "pod costs") case "VirtualMachine": assert.Equal(t, source.GetCount(), uint64(2), "virtual machine count") - assert.Equal(t, source.GetCost(), uint64(60), "virtual machine costs") + assert.Equal(t, source.GetCost(), float64(60), "virtual machine costs") default: t.Errorf("unkown source kind = %s; want Pod or VirtualMachine", source.Kind) } @@ -190,17 +190,17 @@ func TestGrpcCostServer_GetCostPresent(t *testing.T) { costs, err := server.GetCostPresent(context.TODO(), &generalpb.GetRequest{Id: "my-cost-group"}) assert.NoError(t, err) assert.Equal(t, costs.GetCostGroup(), expectedCost.Name, "cost group matches") - assert.Equal(t, costs.GetTotal(), uint64(10+10+10+50), "cost group total") + assert.Equal(t, costs.GetTotal(), float64(10+10+10+50), "cost group total") assert.Len(t, costs.GetSource(), 2, "size of cost source") for _, source := range costs.Source { switch source.GetKind() { case "Pod": assert.Equal(t, source.GetCount(), uint64(2), "pod count") - assert.Equal(t, source.GetCost(), uint64(20), "pod costs") + assert.Equal(t, source.GetCost(), float64(20), "pod costs") case "VirtualMachine": assert.Equal(t, source.GetCount(), uint64(2), "virtual machine count") - assert.Equal(t, source.GetCost(), uint64(60), "virtual machine costs") + assert.Equal(t, source.GetCost(), float64(60), "virtual machine costs") default: t.Errorf("unkown source kind = %s; want Pod or VirtualMachine", source.Kind) } @@ -285,17 +285,17 @@ func TestGrpcCostServer_GetCost(t *testing.T) { costs, err := server.GetCost(context.TODO(), &generalpb.GetRequest{Id: "my-cost-group"}) assert.NoError(t, err) assert.Equal(t, costs.GetCostGroup(), expectedCost.Name, "cost group matches") - assert.Equal(t, costs.GetTotal(), uint64(10+10+10+10+50+10), "cost group total") + assert.Equal(t, costs.GetTotal(), float64(10+10+10+10+50+10), "cost group total") assert.Len(t, costs.GetSource(), 2, "size of cost source") for _, source := range costs.Source { switch source.GetKind() { case "Pod": assert.Equal(t, source.GetCount(), uint64(3), "pod count") - assert.Equal(t, source.GetCost(), uint64(30), "pod costs") + assert.Equal(t, source.GetCost(), float64(30), "pod costs") case "VirtualMachine": assert.Equal(t, source.GetCount(), uint64(3), "virtual machine count") - assert.Equal(t, source.GetCost(), uint64(70), "virtual machine costs") + assert.Equal(t, source.GetCost(), float64(70), "virtual machine costs") default: t.Errorf("unkown source kind = %s; want Pod or VirtualMachine", source.Kind) } @@ -428,10 +428,10 @@ func TestGrpcCostServer_ListCost(t *testing.T) { switch source.GetKind() { case "Pod": assert.Equal(t, source.GetCount(), uint64(3), "pod count") - assert.Equal(t, source.GetCost(), uint64(30), "pod costs") + assert.Equal(t, source.GetCost(), float64(30), "pod costs") case "VirtualMachine": assert.Equal(t, source.GetCount(), uint64(3), "virtual machine count") - assert.Equal(t, source.GetCost(), uint64(70), "virtual machine costs") + assert.Equal(t, source.GetCost(), float64(70), "virtual machine costs") default: t.Errorf("unkown source kind = %s; want Pod or VirtualMachine", source.Kind) } @@ -441,7 +441,7 @@ func TestGrpcCostServer_ListCost(t *testing.T) { switch source.GetKind() { case "VirtualMachine": assert.Equal(t, source.GetCount(), uint64(3), "virtual machine count") - assert.Equal(t, source.GetCost(), uint64(70), "virtual machine costs") + assert.Equal(t, source.GetCost(), float64(70), "virtual machine costs") default: t.Errorf("unkown source kind = %s; want Pod or VirtualMachine", source.Kind) } diff --git a/v3/services/costsvc/internal/util.go b/v3/services/costsvc/internal/util.go index 70068a43..24d50520 100644 --- a/v3/services/costsvc/internal/util.go +++ b/v3/services/costsvc/internal/util.go @@ -8,16 +8,16 @@ import ( "time" ) -func CostResourceCalcCost(cr v1.CostResource, duration time.Duration) uint64 { - var durationInTimeUnit uint64 +func CostResourceCalcCost(cr v1.CostResource, duration time.Duration) float64 { + var durationInTimeUnit float64 switch cr.TimeUnit { case util.TimeUnitSeconds: - durationInTimeUnit = uint64(math.Ceil(duration.Seconds())) + durationInTimeUnit = math.Ceil(duration.Seconds()) case util.TimeUnitMinutes: - durationInTimeUnit = uint64(math.Ceil(duration.Minutes())) + durationInTimeUnit = math.Ceil(duration.Minutes()) case util.TimeUnitHours: - durationInTimeUnit = uint64(math.Ceil(duration.Hours())) + durationInTimeUnit = math.Ceil(duration.Hours()) default: durationInTimeUnit = 0 } @@ -78,10 +78,10 @@ func (cb *CostBuilder) WithHistoricCosts() *CostBuilder { func (cb *CostBuilder) Build(now time.Time) *costpb.Cost { var costSources []*costpb.CostSource - var totalCost uint64 + var totalCost float64 for kind, resources := range GroupCostResourceByKind(cb.cost.Spec.Resources) { - var costForKind uint64 + var costForKind float64 var count uint64 for _, resource := range resources { diff --git a/v3/services/costsvc/internal/util_test.go b/v3/services/costsvc/internal/util_test.go index 582a99bd..9e15e7c2 100644 --- a/v3/services/costsvc/internal/util_test.go +++ b/v3/services/costsvc/internal/util_test.go @@ -14,7 +14,7 @@ func TestCostResourceCalcCost(t *testing.T) { name string input v1.CostResource duration time.Duration - want uint64 + want float64 }{ { name: "seconds", diff --git a/v3/services/vmsetsvc/internal/controller.go b/v3/services/vmsetsvc/internal/controller.go index 56f3b7ac..12ae6364 100644 --- a/v3/services/vmsetsvc/internal/controller.go +++ b/v3/services/vmsetsvc/internal/controller.go @@ -169,7 +169,7 @@ func (v *VMSetController) reconcileVirtualMachineSet(vmset *vmsetpb.VMSet) error if vmt.GetCostBasePrice() != "" && vmt.GetCostTimeUnit() != "" { vmLabels[hflabels.CostGroup] = seName vmLabels[hflabels.CostBasePrice] = vmt.GetCostBasePrice() - vmLabels[hflabels.CostGroup] = vmt.GetCostTimeUnit() + vmLabels[hflabels.CostTimeUnit] = vmt.GetCostTimeUnit() } if restrictedBind { vmLabels["restrictedbindvalue"] = vmset.GetRestrictedBindValue() diff --git a/v3/services/vmtemplatesvc/internal/vmtemplateservice.go b/v3/services/vmtemplatesvc/internal/vmtemplateservice.go index aa1b42fc..3721c7b9 100644 --- a/v3/services/vmtemplatesvc/internal/vmtemplateservice.go +++ b/v3/services/vmtemplatesvc/internal/vmtemplateservice.go @@ -207,9 +207,9 @@ func normalizeCost(costBasePrice, costTimeUnit string) (basePrice, timeUnit *str return } - _, err = strconv.ParseUint(costBasePrice, 10, 64) + _, err = strconv.ParseFloat(costBasePrice, 64) if err != nil { - err = errors.New("cost_base_price needs to be a positive number") + err = errors.New("cost_base_price needs to be a float64") return }