Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ cd module/api
go run main.go --apiserver-host=https://192.168.33.129:6443
```

### Notice
If your API server is running with self-signed certificate, you can set `--apiserver-skip-tls-verify true` option to ignore the certificate verification.

### Frontend
Expand Down
135 changes: 103 additions & 32 deletions modules/api/pkg/handler/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,39 +26,110 @@ import (
"github.com/kubeedge/dashboard/errors"
)

// --- Route helper 定义 ---
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please write comments in English

// RouteOpt 是对 RouteBuilder 的配置函数(类似 functional options)
type RouteOpt func(rb *restful.RouteBuilder)

// 常用选项:Param / Reads / Writes / Returns / Doc
func WithParam(p *restful.Parameter) RouteOpt {
return func(rb *restful.RouteBuilder) {
rb.Param(p)
}
}
func WithReads(obj interface{}) RouteOpt {
return func(rb *restful.RouteBuilder) {
rb.Reads(obj)
}
}
func WithWrites(obj interface{}) RouteOpt {
return func(rb *restful.RouteBuilder) {
rb.Writes(obj)
}
}
func WithReturns(code int, desc string, obj interface{}) RouteOpt {
return func(rb *restful.RouteBuilder) {
rb.Returns(code, desc, obj)
}
}
func WithDoc(doc string) RouteOpt {
return func(rb *restful.RouteBuilder) {
rb.Doc(doc)
}
}

// addRoute 根据 method 动态创建 RouteBuilder 并应用所有 RouteOpt,最后调用 ws.Route(...)
func addRoute(ws *restful.WebService, method string, path string, handler restful.RouteFunction, opts ...RouteOpt) {
var rb *restful.RouteBuilder
switch method {
case http.MethodGet:
rb = ws.GET(path).To(handler)
case http.MethodPost:
rb = ws.POST(path).To(handler)
case http.MethodPut:
rb = ws.PUT(path).To(handler)
case http.MethodDelete:
rb = ws.DELETE(path).To(handler)
default:
// 保守回退为 GET(或者根据需要 panic/返回 error)
rb = ws.GET(path).To(handler)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The default case in this switch statement silently falls back to creating a GET route for any unsupported HTTP method. This can lead to subtle bugs that are hard to track down if a developer accidentally provides an incorrect or unsupported method string. It's better to fail fast in such cases.

Since this function is part of the application setup, panicking is an appropriate way to signal a programming error immediately.

Suggested change
default:
// 保守回退为 GET(或者根据需要 panic/返回 error)
rb = ws.GET(path).To(handler)
default:
panic("unsupported HTTP method: " + method)

}

for _, o := range opts {
o(rb)
}
ws.Route(rb)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should move these functions to another file to make them reusable


// --- 使用 helper 的路由注册 ---
// 将原来的 addRoleRoutes 用下面实现替换
func (apiHandler *APIHandler) addRoleRoutes(apiV1Ws *restful.WebService) *APIHandler {
apiV1Ws.Route(
apiV1Ws.GET("/role").To(apiHandler.handleGetRoles).
Writes(rbacv1.RoleList{}).
Returns(http.StatusOK, "OK", rbacv1.RoleList{}))
apiV1Ws.Route(
apiV1Ws.GET("/role/{namespace}").To(apiHandler.handleGetRoles).
Param(apiV1Ws.PathParameter("namespace", "Name of the namespace")).
Writes(rbacv1.RoleList{}).
Returns(http.StatusOK, "OK", rbacv1.RoleList{}))
apiV1Ws.Route(
apiV1Ws.GET("/role/{namespace}/{name}").To(apiHandler.handleGetRole).
Param(apiV1Ws.PathParameter("namespace", "Name of the namespace")).
Param(apiV1Ws.PathParameter("name", "Name of the role")).
Writes(rbacv1.Role{}).
Returns(http.StatusOK, "OK", rbacv1.Role{}))
apiV1Ws.Route(
apiV1Ws.POST("/role/{namespace}").To(apiHandler.handleCreateRole).
Param(apiV1Ws.PathParameter("namespace", "Name of the namespace")).
Reads(rbacv1.Role{}).
Writes(rbacv1.Role{}).
Returns(http.StatusCreated, "Created", rbacv1.Role{}))
apiV1Ws.Route(
apiV1Ws.PUT("/role/{namespace}").To(apiHandler.handleUpdateRole).
Param(apiV1Ws.PathParameter("namespace", "Name of the namespace")).
Reads(rbacv1.Role{}).
Writes(rbacv1.Role{}).
Returns(http.StatusOK, "OK", rbacv1.Role{}))
apiV1Ws.Route(
apiV1Ws.DELETE("/role/{namespace}/{name}").To(apiHandler.handleDeleteRole).
Param(apiV1Ws.PathParameter("namespace", "Name of the namespace")).
Param(apiV1Ws.PathParameter("name", "Name of the role")).
Returns(http.StatusNoContent, "No Content", nil))
// 复用的 path 参数(避免多次创建同样的 Parameter)
nsParam := apiV1Ws.PathParameter("namespace", "Name of the namespace")
nameParam := apiV1Ws.PathParameter("name", "Name of the role")

// GET /role -> 列表(跨 namespace)
addRoute(apiV1Ws, http.MethodGet, "/role", apiHandler.handleGetRoles,
WithWrites(rbacv1.RoleList{}),
WithReturns(http.StatusOK, "OK", rbacv1.RoleList{}),
WithDoc("Get all roles"))

// GET /role/{namespace} -> 某 namespace 下的角色列表
addRoute(apiV1Ws, http.MethodGet, "/role/{namespace}", apiHandler.handleGetRoles,
WithParam(nsParam),
WithWrites(rbacv1.RoleList{}),
WithReturns(http.StatusOK, "OK", rbacv1.RoleList{}),
WithDoc("Get roles in a namespace"))

// GET /role/{namespace}/{name} -> 单个角色
addRoute(apiV1Ws, http.MethodGet, "/role/{namespace}/{name}", apiHandler.handleGetRole,
WithParam(nsParam),
WithParam(nameParam),
WithWrites(rbacv1.Role{}),
WithReturns(http.StatusOK, "OK", rbacv1.Role{}),
WithDoc("Get a role by name"))

// POST /role/{namespace} -> 创建
addRoute(apiV1Ws, http.MethodPost, "/role/{namespace}", apiHandler.handleCreateRole,
WithParam(nsParam),
WithReads(rbacv1.Role{}),
WithWrites(rbacv1.Role{}),
WithReturns(http.StatusCreated, "Created", rbacv1.Role{}),
WithDoc("Create a role in a namespace"))

// PUT /role/{namespace} -> 更新(保留原路径行为)
addRoute(apiV1Ws, http.MethodPut, "/role/{namespace}", apiHandler.handleUpdateRole,
WithParam(nsParam),
WithReads(rbacv1.Role{}),
WithWrites(rbacv1.Role{}),
WithReturns(http.StatusOK, "OK", rbacv1.Role{}),
WithDoc("Update a role in a namespace"))

// DELETE /role/{namespace}/{name} -> 删除
addRoute(apiV1Ws, http.MethodDelete, "/role/{namespace}/{name}", apiHandler.handleDeleteRole,
WithParam(nsParam),
WithParam(nameParam),
WithReturns(http.StatusNoContent, "No Content", nil),
WithDoc("Delete a role"))

return apiHandler
}
Expand Down