Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

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

11 changes: 11 additions & 0 deletions api/handler/space_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,17 @@ func (h *SpaceResourceHandler) Index(ctx *gin.Context) {
Page: page,
},
}
if ctx.Query("resource_type") != "" {
req.ResourceType = types.ResourceType(ctx.Query("resource_type"))
if !types.ResourceTypeValid(req.ResourceType) {
slog.ErrorContext(ctx.Request.Context(), "Invalid resource type", "resource_type", req.ResourceType)
httpbase.BadRequest(ctx, "Invalid resource type")
return
}
}
if ctx.Query("hardware_type") != "" {
req.HardwareType = ctx.Query("hardware_type")
}
spaceResources, total, err := h.spaceResource.Index(ctx.Request.Context(), req)
if err != nil {
slog.ErrorContext(ctx.Request.Context(), "Failed to get space resources", slog.String("cluster_id", clusterId), slog.String("deploy_type", deployTypeStr), slog.Any("error", err))
Expand Down
50 changes: 33 additions & 17 deletions api/handler/space_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/gin-gonic/gin"
mockcomponent "opencsg.com/csghub-server/_mocks/opencsg.com/csghub-server/component"
"opencsg.com/csghub-server/api/httpbase"
"opencsg.com/csghub-server/builder/testutil"
"opencsg.com/csghub-server/common/types"
)
Expand Down Expand Up @@ -36,26 +37,41 @@ func (t *SpaceResourceTester) WithHandleFunc(fn func(h *SpaceResourceHandler) gi
}

func TestSpaceResourceHandler_Index(t *testing.T) {
tester := NewSpaceResourceTester(t).WithHandleFunc(func(h *SpaceResourceHandler) gin.HandlerFunc {
return h.Index
})
t.Run("200", func(t *testing.T) {
tester := NewSpaceResourceTester(t).WithHandleFunc(func(h *SpaceResourceHandler) gin.HandlerFunc {
return h.Index
})

req := &types.SpaceResourceIndexReq{
ClusterID: "c1",
DeployType: types.InferenceType,
CurrentUser: "u",
PageOpts: types.PageOpts{
PageSize: 50,
Page: 1,
},
}
req := &types.SpaceResourceIndexReq{
ClusterID: "c1",
DeployType: types.InferenceType,
CurrentUser: "u",
ResourceType: types.ResourceTypeCPU,
HardwareType: "intel",
PageOpts: types.PageOpts{
PageSize: 50,
Page: 1,
},
}

tester.mocks.spaceResource.EXPECT().Index(tester.Ctx(), req).Return(
[]types.SpaceResource{{Name: "sp"}}, 0, nil,
)
tester.WithQuery("cluster_id", "c1").WithQuery("deploy_type", "").
WithQuery("resource_type", "cpu").WithQuery("hardware_type", "intel").WithUser().Execute()

tester.mocks.spaceResource.EXPECT().Index(tester.Ctx(), req).Return(
[]types.SpaceResource{{Name: "sp"}}, 0, nil,
)
tester.WithQuery("cluster_id", "c1").WithQuery("deploy_type", "").WithUser().Execute()
tester.ResponseEq(t, 200, tester.OKText, []types.SpaceResource{{Name: "sp"}})
})
t.Run("invalid resource type", func(t *testing.T) {
tester := NewSpaceResourceTester(t).WithHandleFunc(func(h *SpaceResourceHandler) gin.HandlerFunc {
return h.Index
})

tester.WithQuery("cluster_id", "c1").WithQuery("deploy_type", "").
WithQuery("resource_type", "invalid").WithQuery("hardware_type", "intel").WithUser().Execute()

tester.ResponseEq(t, 200, tester.OKText, []types.SpaceResource{{Name: "sp"}})
tester.ResponseEqSimple(t, 400, httpbase.R{Msg: "Invalid resource type"})
})
}

func TestSpaceResourceHandler_Create(t *testing.T) {
Expand Down
19 changes: 14 additions & 5 deletions builder/store/database/space_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type spaceResourceStoreImpl struct {
}

type SpaceResourceStore interface {
Index(ctx context.Context, clusterId string, per, page int) ([]SpaceResource, int, error)
Index(ctx context.Context, filter types.SpaceResourceFilter, per, page int) ([]SpaceResource, int, error)
Create(ctx context.Context, input SpaceResource) (*SpaceResource, error)
Update(ctx context.Context, input SpaceResource) (*SpaceResource, error)
Delete(ctx context.Context, input SpaceResource) error
Expand All @@ -40,9 +40,18 @@ type SpaceResource struct {
times
}

func (s *spaceResourceStoreImpl) Index(ctx context.Context, clusterId string, per, page int) ([]SpaceResource, int, error) {
func (s *spaceResourceStoreImpl) Index(ctx context.Context, filter types.SpaceResourceFilter, per, page int) ([]SpaceResource, int, error) {
var result []SpaceResource
query := s.db.Operator.Core.NewSelect().Model(&result).Where("cluster_id = ?", clusterId)
query := s.db.Operator.Core.NewSelect().Model(&result)
if filter.ClusterID != "" {
query = query.Where("cluster_id = ?", filter.ClusterID)
}
if filter.HardwareType != "" {
query = query.Where("EXISTS (SELECT 1 FROM jsonb_each(resources::jsonb) WHERE value->>'type' = ?)", filter.HardwareType)
}
if filter.ResourceType != "" {
query = query.Where("EXISTS (SELECT 1 FROM jsonb_each(resources::jsonb) WHERE key = ?)", filter.ResourceType)
}
query = query.Order("name asc").
Limit(per).
Offset((page - 1) * per)
Expand Down Expand Up @@ -106,14 +115,14 @@ func (s *spaceResourceStoreImpl) FindAll(ctx context.Context) ([]SpaceResource,
func (s *spaceResourceStoreImpl) FindAllResourceTypes(ctx context.Context, clusterId string) ([]string, error) {
typeSet := make(map[string]bool)
var hardWareTypes []string

filter := types.SpaceResourceFilter{ClusterID: clusterId}
// Use pagination to query resources
page := 1
per := 100 // Set a reasonable page size

for {
// Get resources for current page
resources, _, err := s.Index(ctx, clusterId, per, page)
resources, _, err := s.Index(ctx, filter, per, page)
if err != nil {
return nil, err
}
Expand Down
30 changes: 29 additions & 1 deletion builder/store/database/space_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/require"
"opencsg.com/csghub-server/builder/store/database"
"opencsg.com/csghub-server/common/tests"
"opencsg.com/csghub-server/common/types"
)

func TestSpaceResourceStore_CRUD(t *testing.T) {
Expand Down Expand Up @@ -39,7 +40,7 @@ func TestSpaceResourceStore_CRUD(t *testing.T) {
require.Equal(t, 1, len(srs))
require.Equal(t, "c1", srs[0].ClusterID)

srs, _, err = store.Index(ctx, "c1", 50, 1)
srs, _, err = store.Index(ctx, types.SpaceResourceFilter{ClusterID: "c1"}, 50, 1)
require.Nil(t, err)
require.Equal(t, 1, len(srs))
require.Equal(t, "c1", srs[0].ClusterID)
Expand Down Expand Up @@ -147,3 +148,30 @@ func TestSpaceResourceStore_FindAllResourceTypes_InvalidJSON(t *testing.T) {
require.Nil(t, err)
require.Empty(t, types)
}

func TestSpaceResourceStore_Filter(t *testing.T) {
db := tests.InitTestDB()
defer db.Close()
ctx := context.TODO()

store := database.NewSpaceResourceStoreWithDB(db)

_, err := store.Create(ctx, database.SpaceResource{
Name: "r1",
ClusterID: "c1",
Resources: `{"cpu": { "type": "Intel","num": "200m"}}`,
})
require.Nil(t, err)
sr := &database.SpaceResource{}
err = db.Core.NewSelect().Model(sr).Where("name=?", "r1").Scan(ctx, sr)
require.Nil(t, err)
require.Equal(t, "c1", sr.ClusterID)

srs, total, err := store.Index(ctx, types.SpaceResourceFilter{
ResourceType: types.ResourceTypeCPU,
}, 50, 1)
require.Nil(t, err)
require.Equal(t, 1, len(srs))
require.Equal(t, 1, total)
require.Equal(t, "c1", srs[0].ClusterID)
}
25 changes: 22 additions & 3 deletions common/types/space_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ const (
PayModeYear PayMode = "year"
)

func ResourceTypeValid(resourceType ResourceType) bool {
return resourceType == ResourceTypeCPU ||
resourceType == ResourceTypeGPU ||
resourceType == ResourceTypeNPU ||
resourceType == ResourceTypeGCU ||
resourceType == ResourceTypeGPGPU ||
resourceType == ResourceTypeMLU ||
resourceType == ResourceTypeDCU
}

type SpaceResource struct {
ID int64 `json:"id"`
Name string `json:"name"`
Expand All @@ -43,8 +53,17 @@ type UpdateSpaceResourceReq struct {
}

type SpaceResourceIndexReq struct {
ClusterID string `json:"cluster_id"`
DeployType int `json:"deploy_type"`
CurrentUser string `json:"current_user"`
ClusterID string `json:"cluster_id"`
DeployType int `json:"deploy_type"`
CurrentUser string `json:"current_user"`
ResourceType ResourceType `json:"resource_type"`
HardwareType string `json:"hardware_type"`
IsAvailable bool `json:"is_available"`
PageOpts
}

type SpaceResourceFilter struct {
ClusterID string `json:"cluster_id"`
ResourceType ResourceType `json:"resource_type"`
HardwareType string `json:"hardware_type"`
}
11 changes: 10 additions & 1 deletion component/space_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,16 @@ func (c *spaceResourceComponentImpl) Index(ctx context.Context, req *types.Space
var total int
for _, clusterID := range clusterIDs {
var singleClusterResult []types.SpaceResource
databaseSpaceResources, currentTotal, err := c.spaceResourceStore.Index(ctx, clusterID, req.PageSize, req.Page)
dbReq := types.SpaceResourceFilter{
ClusterID: clusterID,
}
if req.HardwareType != "" {
dbReq.HardwareType = req.HardwareType
}
if req.ResourceType != "" {
dbReq.ResourceType = req.ResourceType
}
databaseSpaceResources, currentTotal, err := c.spaceResourceStore.Index(ctx, dbReq, req.PageSize, req.Page)
if err != nil {
slog.Error("failed to index space resource", slog.String("clusterID", clusterID), slog.Any("error", err))
continue
Expand Down
4 changes: 2 additions & 2 deletions component/space_resource_ce_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func TestSpaceResourceComponent_Index(t *testing.T) {
ctx := context.TODO()
sc := initializeTestSpaceResourceComponent(ctx, t)

sc.mocks.stores.SpaceResourceMock().EXPECT().Index(ctx, "c1", 50, 1).Return(
sc.mocks.stores.SpaceResourceMock().EXPECT().Index(ctx, types.SpaceResourceFilter{ClusterID: "c1"}, 50, 1).Return(
[]database.SpaceResource{
{ID: 1, Name: "sr", Resources: `{"memory": "1000", "gpu": {"num": "5"}}`},
{ID: 2, Name: "sr2", Resources: `{"memory": "1000"}`},
Expand Down Expand Up @@ -54,7 +54,7 @@ func TestSpaceResourceComponent_Index_With_Status_Filter(t *testing.T) {
sc.mocks.deployer.EXPECT().ListCluster(ctx).Return(clusters, nil)
sc.mocks.deployer.EXPECT().CheckHeartbeatTimeout(ctx, "cluster1").Once().Return(true, nil)
sc.mocks.deployer.EXPECT().CheckHeartbeatTimeout(ctx, "cluster2").Once().Return(false, nil)
sc.mocks.stores.SpaceResourceMock().EXPECT().Index(ctx, "cluster2", 50, 1).Return([]database.SpaceResource{}, 0, nil)
sc.mocks.stores.SpaceResourceMock().EXPECT().Index(ctx, types.SpaceResourceFilter{ClusterID: "cluster2"}, 50, 1).Return([]database.SpaceResource{}, 0, nil)
sc.mocks.deployer.EXPECT().GetClusterById(ctx, "cluster2").Return(&types.ClusterRes{}, nil)
req := &types.SpaceResourceIndexReq{
CurrentUser: "user1",
Expand Down
Loading