Skip to content

Commit

Permalink
Add cost-service route /a/cost/detail/{cost_group}
Browse files Browse the repository at this point in the history
  • Loading branch information
marcus-sva committed Jan 14, 2025
1 parent 097d607 commit 3152e3b
Show file tree
Hide file tree
Showing 7 changed files with 465 additions and 81 deletions.
329 changes: 248 additions & 81 deletions v3/protos/cost/cost.pb.go

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions v3/protos/cost/cost.proto
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ service CostSvc {
rpc GetCostPresent (general.GetRequest) returns (Cost);
// Response reflects how many costs have been and are currently generated in a cost group (reflects running and terminated resources)
rpc GetCost (general.GetRequest) returns (Cost);
// Response contains cost details for a cost group
rpc GetCostDetail (general.GetRequest) returns (CostDetail);
rpc DeleteCost (general.ResourceId) returns (google.protobuf.Empty);
// Response reflects how many costs have been and are currently generated for all cost groups (reflects running and terminated resources)
rpc ListCost (general.ListOptions) returns (ListCostsResponse);
Expand All @@ -32,6 +34,20 @@ message CostSource {
uint64 count = 3;
}

message CostDetail {
string cost_group = 1;
repeated CostDetailSource source = 2;
}

message CostDetailSource {
string kind = 1; // like VirtualMachine
double base_price = 2;
string time_unit = 3;
string id = 4; // resource id
int64 creation_unix_timestamp = 5; // unix timestamp in seconds
optional int64 deletion_unix_timestamp = 6; // unix timestamp in seconds
}

message CreateOrUpdateCostRequest {
string cost_group = 1;
string kind = 2; // like VirtualMachine
Expand Down
39 changes: 39 additions & 0 deletions v3/protos/cost/cost_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 71 additions & 0 deletions v3/services/costsvc/internal/costservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,38 @@ func NewPreparedCost(cost *costpb.Cost) PreparedCost {
}
}

type PreparedCostDetail struct {
CostGroup string `json:"cost_group"`
Sources []PreparedCostDetailSource `json:"source"`
}

type PreparedCostDetailSource struct {
Kind string `json:"kind"`
BasePrice float64 `json:"base_price"`
TimeUnit string `json:"time_unit"`
ID string `json:"id"`
CreationUnixTimestamp int64 `json:"creation_unix_timestamp"`
DeletionUnixTimestamp int64 `json:"deletion_unix_timestamp,omitempty"`
}

func NewPreparedCostDetail(costDetail *costpb.CostDetail) PreparedCostDetail {
sources := make([]PreparedCostDetailSource, len(costDetail.GetSource()))
for i, source := range costDetail.GetSource() {
sources[i] = PreparedCostDetailSource{
Kind: source.GetKind(),
BasePrice: source.GetBasePrice(),
TimeUnit: source.TimeUnit,
ID: source.GetId(),
CreationUnixTimestamp: source.GetCreationUnixTimestamp(),
DeletionUnixTimestamp: source.GetDeletionUnixTimestamp(),
}
}
return PreparedCostDetail{
CostGroup: costDetail.GetCostGroup(),
Sources: sources,
}
}

func (cs CostServer) GetCostFunc(w http.ResponseWriter, r *http.Request) {
_, err := rbac.AuthenticateRequest(r, cs.authnClient)
if err != nil {
Expand Down Expand Up @@ -159,6 +191,45 @@ func (cs CostServer) GetCostPresentFunc(w http.ResponseWriter, r *http.Request)
glog.V(2).Infof("retrieved cost present %s", cost.GetCostGroup())
}

func (cs CostServer) GetCostDetailFunc(w http.ResponseWriter, r *http.Request) {
_, err := rbac.AuthenticateRequest(r, cs.authnClient)
if err != nil {
util.ReturnHTTPMessage(w, r, 403, "forbidden", "no access to get cost detail")
return
}

vars := mux.Vars(r)

cg := vars["cost_group"]

if len(cg) == 0 {
util.ReturnHTTPMessage(w, r, 400, "bad request", "no cost group passed in")
return
}

costDetail, err := cs.internalCostServer.GetCostDetail(r.Context(), &generalpb.GetRequest{Id: cg, LoadFromCache: true})
if err != nil {
glog.Errorf("error retrieving cost group %s from cache: %s", cg, hferrors.GetErrorMessage(err))
if hferrors.IsGrpcNotFound(err) {
errMsg := fmt.Sprintf("cost group %s not found", cg)
util.ReturnHTTPMessage(w, r, http.StatusNotFound, "not found", errMsg)
return
}
errMsg := fmt.Sprintf("error retrieving cost croup %s", cg)
util.ReturnHTTPMessage(w, r, http.StatusInternalServerError, "error", errMsg)
return
}

preparedCostDetail := NewPreparedCostDetail(costDetail)
encodedCost, err := json.Marshal(preparedCostDetail)
if err != nil {
glog.Error(err)
}
util.ReturnHTTPContent(w, r, 200, "success", encodedCost)

glog.V(2).Infof("retrieved cost detail %s", costDetail.GetCostGroup())
}

func (cs CostServer) GetAllCostListFunc(w http.ResponseWriter, r *http.Request) {
_, err := rbac.AuthenticateRequest(r, cs.authnClient)
if err != nil {
Expand Down
23 changes: 23 additions & 0 deletions v3/services/costsvc/internal/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,29 @@ func (gcs *GrpcCostServer) GetCost(ctx context.Context, req *generalpb.GetReques
return NewCostBuilder(cost).WithAllCosts().Build(gcs.nowFunc()), nil
}

func (gcs *GrpcCostServer) GetCostDetail(ctx context.Context, req *generalpb.GetRequest) (*costpb.CostDetail, error) {
cost, err := util.GenericHfGetter(ctx, req, gcs.costClient, gcs.costLister.Costs(util.GetReleaseNamespace()), "cost", gcs.costSynced())
if err != nil {
return &costpb.CostDetail{}, err
}

source := make([]*costpb.CostDetailSource, len(cost.Spec.Resources))
for i, resource := range cost.Spec.Resources {
source[i] = &costpb.CostDetailSource{
Kind: resource.Kind,
BasePrice: resource.BasePrice,
TimeUnit: resource.TimeUnit,
Id: resource.Id,
CreationUnixTimestamp: resource.CreationUnixTimestamp,
DeletionUnixTimestamp: util.RefOrNil(resource.DeletionUnixTimestamp),
}
}
return &costpb.CostDetail{
CostGroup: cost.Spec.CostGroup,
Source: source,
}, nil
}

func (gcs *GrpcCostServer) DeleteCost(ctx context.Context, req *generalpb.ResourceId) (*emptypb.Empty, error) {
return util.DeleteHfResource(ctx, req, gcs.costClient, "cost")
}
Expand Down
67 changes: 67 additions & 0 deletions v3/services/costsvc/internal/grpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,73 @@ func TestGrpcCostServer_GetCost(t *testing.T) {
}
}

func TestGrpcCostServer_GetCostDetail(t *testing.T) {
fakeClient := &faketyped.FakeHobbyfarmV1{Fake: &k8stesting.Fake{}}
fakeCosts := &faketyped.FakeCosts{Fake: fakeClient}
fakeCostLister := &fakelisters.FakeCostLister{}
fakeCostLister.On("Costs", mock.Anything).Return(nil)
server := GrpcCostServer{
costClient: fakeCosts,
costLister: fakeCostLister,
costSynced: func() bool { return true },
}
givenCost := &hfv1.Cost{
ObjectMeta: metav1.ObjectMeta{
Name: "my-cost-group",
},
Spec: hfv1.CostSpec{
CostGroup: "my-cost-group",
Resources: []hfv1.CostResource{
{
Id: "pod-a",
Kind: "Pod",
BasePrice: 0.1,
TimeUnit: util.TimeUnitSeconds,
CreationUnixTimestamp: 10,
DeletionUnixTimestamp: 0,
},
{
Id: "vm-a",
Kind: "VirtualMachine",
BasePrice: 10.111,
TimeUnit: util.TimeUnitMinutes,
CreationUnixTimestamp: 20,
DeletionUnixTimestamp: 200,
},
},
},
}
expectedCostDetail := costpb.CostDetail{
CostGroup: "my-cost-group",
Source: []*costpb.CostDetailSource{
{
Kind: "Pod",
BasePrice: 0.1,
TimeUnit: util.TimeUnitSeconds,
Id: "pod-a",
CreationUnixTimestamp: 10,
DeletionUnixTimestamp: nil,
},
{
Kind: "VirtualMachine",
BasePrice: 10.111,
TimeUnit: util.TimeUnitMinutes,
Id: "vm-a",
CreationUnixTimestamp: 20,
DeletionUnixTimestamp: util.Ref(int64(200)),
},
},
}
fakeClient.Fake.PrependReactor("get", "costs", func(action k8stesting.Action) (bool, runtime.Object, error) {
return true, givenCost, nil
})

costs, err := server.GetCostDetail(context.TODO(), &generalpb.GetRequest{Id: "my-cost-group"})
assert.NoError(t, err)
assert.Equal(t, costs.GetCostGroup(), expectedCostDetail.CostGroup, "cost group matches")
assert.ElementsMatch(t, expectedCostDetail.GetSource(), costs.GetSource(), "source matches")
}

func TestGrpcCostServer_ListCost(t *testing.T) {
now := time.Unix(10, 0)

Expand Down
1 change: 1 addition & 0 deletions v3/services/costsvc/internal/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func (cs CostServer) SetupRoutes(r *mux.Router) {
r.HandleFunc("/a/cost/all/{cost_group}", cs.GetCostFunc).Methods("GET")
r.HandleFunc("/a/cost/history/{cost_group}", cs.GetCostHistoryFunc).Methods("GET")
r.HandleFunc("/a/cost/present/{cost_group}", cs.GetCostPresentFunc).Methods("GET")
r.HandleFunc("/a/cost/detail/{cost_group}", cs.GetCostDetailFunc).Methods("GET")
r.HandleFunc("/a/cost/list", cs.GetAllCostListFunc).Methods("GET")
glog.V(2).Infof("set up routes")
}

0 comments on commit 3152e3b

Please sign in to comment.